16b1baefeSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24a2833c6SLee Jones /*
317cdd29dSKeshava Munegowda * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI
417cdd29dSKeshava Munegowda *
54f4ed454SAlexander A. Klimov * Copyright (C) 2011-2013 Texas Instruments Incorporated - https://www.ti.com
617cdd29dSKeshava Munegowda * Author: Keshava Munegowda <keshava_mgowda@ti.com>
703a8f438SRoger Quadros * Author: Roger Quadros <rogerq@ti.com>
817cdd29dSKeshava Munegowda */
917cdd29dSKeshava Munegowda #include <linux/kernel.h>
10417e206bSMing Lei #include <linux/module.h>
1117cdd29dSKeshava Munegowda #include <linux/types.h>
1217cdd29dSKeshava Munegowda #include <linux/slab.h>
1317cdd29dSKeshava Munegowda #include <linux/delay.h>
1417cdd29dSKeshava Munegowda #include <linux/clk.h>
1517cdd29dSKeshava Munegowda #include <linux/dma-mapping.h>
16c05995c3SRuss Dill #include <linux/gpio.h>
17e8c4a7acSFelipe Balbi #include <linux/platform_device.h>
18e8c4a7acSFelipe Balbi #include <linux/platform_data/usb-omap.h>
191e7fe1a9SKeshava Munegowda #include <linux/pm_runtime.h>
2003a8f438SRoger Quadros #include <linux/of.h>
2103a8f438SRoger Quadros #include <linux/of_platform.h>
22d011c450SSachin Kamat #include <linux/err.h>
2317cdd29dSKeshava Munegowda
24e8c4a7acSFelipe Balbi #include "omap-usb.h"
25e8c4a7acSFelipe Balbi
26a6d3a662SKeshava Munegowda #define USBHS_DRIVER_NAME "usbhs_omap"
2717cdd29dSKeshava Munegowda #define OMAP_EHCI_DEVICE "ehci-omap"
2817cdd29dSKeshava Munegowda #define OMAP_OHCI_DEVICE "ohci-omap3"
2917cdd29dSKeshava Munegowda
3017cdd29dSKeshava Munegowda /* OMAP USBHOST Register addresses */
3117cdd29dSKeshava Munegowda
3217cdd29dSKeshava Munegowda /* UHH Register Set */
3317cdd29dSKeshava Munegowda #define OMAP_UHH_REVISION (0x00)
3417cdd29dSKeshava Munegowda #define OMAP_UHH_SYSCONFIG (0x10)
3517cdd29dSKeshava Munegowda #define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12)
3617cdd29dSKeshava Munegowda #define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8)
3717cdd29dSKeshava Munegowda #define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3)
3817cdd29dSKeshava Munegowda #define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2)
3917cdd29dSKeshava Munegowda #define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1)
4017cdd29dSKeshava Munegowda #define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0)
4117cdd29dSKeshava Munegowda
4217cdd29dSKeshava Munegowda #define OMAP_UHH_SYSSTATUS (0x14)
4317cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG (0x40)
4417cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0)
4517cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0)
4617cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11)
4717cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12)
4817cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2)
4917cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3)
5017cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4)
5117cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5)
5217cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8)
5317cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9)
5417cdd29dSKeshava Munegowda #define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10)
5517cdd29dSKeshava Munegowda #define OMAP4_UHH_HOSTCONFIG_APP_START_CLK (1 << 31)
5617cdd29dSKeshava Munegowda
5717cdd29dSKeshava Munegowda /* OMAP4-specific defines */
5817cdd29dSKeshava Munegowda #define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2)
5917cdd29dSKeshava Munegowda #define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2)
6017cdd29dSKeshava Munegowda #define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4)
6117cdd29dSKeshava Munegowda #define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4)
6217cdd29dSKeshava Munegowda #define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0)
6317cdd29dSKeshava Munegowda
6417cdd29dSKeshava Munegowda #define OMAP4_P1_MODE_CLEAR (3 << 16)
6517cdd29dSKeshava Munegowda #define OMAP4_P1_MODE_TLL (1 << 16)
6617cdd29dSKeshava Munegowda #define OMAP4_P1_MODE_HSIC (3 << 16)
6717cdd29dSKeshava Munegowda #define OMAP4_P2_MODE_CLEAR (3 << 18)
6817cdd29dSKeshava Munegowda #define OMAP4_P2_MODE_TLL (1 << 18)
6917cdd29dSKeshava Munegowda #define OMAP4_P2_MODE_HSIC (3 << 18)
7017cdd29dSKeshava Munegowda
7117cdd29dSKeshava Munegowda #define OMAP_UHH_DEBUG_CSR (0x44)
7217cdd29dSKeshava Munegowda
7317cdd29dSKeshava Munegowda /* Values of UHH_REVISION - Note: these are not given in the TRM */
7417cdd29dSKeshava Munegowda #define OMAP_USBHS_REV1 0x00000010 /* OMAP3 */
7517cdd29dSKeshava Munegowda #define OMAP_USBHS_REV2 0x50700100 /* OMAP4 */
7617cdd29dSKeshava Munegowda
7717cdd29dSKeshava Munegowda #define is_omap_usbhs_rev1(x) (x->usbhs_rev == OMAP_USBHS_REV1)
7817cdd29dSKeshava Munegowda #define is_omap_usbhs_rev2(x) (x->usbhs_rev == OMAP_USBHS_REV2)
7917cdd29dSKeshava Munegowda
8017cdd29dSKeshava Munegowda #define is_ehci_phy_mode(x) (x == OMAP_EHCI_PORT_MODE_PHY)
8117cdd29dSKeshava Munegowda #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL)
8217cdd29dSKeshava Munegowda #define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC)
8317cdd29dSKeshava Munegowda
8417cdd29dSKeshava Munegowda
8517cdd29dSKeshava Munegowda struct usbhs_hcd_omap {
86d7eaf866SRoger Quadros int nports;
8706ba7dc7SRoger Quadros struct clk **utmi_clk;
88340c64eaSRoger Quadros struct clk **hsic60m_clk;
89340c64eaSRoger Quadros struct clk **hsic480m_clk;
90d7eaf866SRoger Quadros
9117cdd29dSKeshava Munegowda struct clk *xclk60mhsp1_ck;
9217cdd29dSKeshava Munegowda struct clk *xclk60mhsp2_ck;
9306ba7dc7SRoger Quadros struct clk *utmi_p1_gfclk;
9406ba7dc7SRoger Quadros struct clk *utmi_p2_gfclk;
9517cdd29dSKeshava Munegowda struct clk *init_60m_fclk;
961e7fe1a9SKeshava Munegowda struct clk *ehci_logic_fck;
9717cdd29dSKeshava Munegowda
9817cdd29dSKeshava Munegowda void __iomem *uhh_base;
9917cdd29dSKeshava Munegowda
1009d9c6ae7SRoger Quadros struct usbhs_omap_platform_data *pdata;
10117cdd29dSKeshava Munegowda
10217cdd29dSKeshava Munegowda u32 usbhs_rev;
10317cdd29dSKeshava Munegowda };
10417cdd29dSKeshava Munegowda /*-------------------------------------------------------------------------*/
10517cdd29dSKeshava Munegowda
1067844b989SSachin Kamat static const char usbhs_driver_name[] = USBHS_DRIVER_NAME;
107cbb8c220SGovindraj.R static u64 usbhs_dmamask = DMA_BIT_MASK(32);
10817cdd29dSKeshava Munegowda
10917cdd29dSKeshava Munegowda /*-------------------------------------------------------------------------*/
11017cdd29dSKeshava Munegowda
usbhs_write(void __iomem * base,u32 reg,u32 val)11117cdd29dSKeshava Munegowda static inline void usbhs_write(void __iomem *base, u32 reg, u32 val)
11217cdd29dSKeshava Munegowda {
1139981a314SVictor Kamensky writel_relaxed(val, base + reg);
11417cdd29dSKeshava Munegowda }
11517cdd29dSKeshava Munegowda
usbhs_read(void __iomem * base,u32 reg)11617cdd29dSKeshava Munegowda static inline u32 usbhs_read(void __iomem *base, u32 reg)
11717cdd29dSKeshava Munegowda {
1189981a314SVictor Kamensky return readl_relaxed(base + reg);
11917cdd29dSKeshava Munegowda }
12017cdd29dSKeshava Munegowda
12117cdd29dSKeshava Munegowda /*-------------------------------------------------------------------------*/
12217cdd29dSKeshava Munegowda
1235d36df75SLee Jones /*
12403a8f438SRoger Quadros * Map 'enum usbhs_omap_port_mode' found in <linux/platform_data/usb-omap.h>
12503a8f438SRoger Quadros * to the device tree binding portN-mode found in
12603a8f438SRoger Quadros * 'Documentation/devicetree/bindings/mfd/omap-usb-host.txt'
12703a8f438SRoger Quadros */
12803a8f438SRoger Quadros static const char * const port_modes[] = {
12903a8f438SRoger Quadros [OMAP_USBHS_PORT_MODE_UNUSED] = "",
13003a8f438SRoger Quadros [OMAP_EHCI_PORT_MODE_PHY] = "ehci-phy",
13103a8f438SRoger Quadros [OMAP_EHCI_PORT_MODE_TLL] = "ehci-tll",
13203a8f438SRoger Quadros [OMAP_EHCI_PORT_MODE_HSIC] = "ehci-hsic",
13303a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0] = "ohci-phy-6pin-datse0",
13403a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM] = "ohci-phy-6pin-dpdm",
13503a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0] = "ohci-phy-3pin-datse0",
13603a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM] = "ohci-phy-4pin-dpdm",
13703a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0] = "ohci-tll-6pin-datse0",
13803a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM] = "ohci-tll-6pin-dpdm",
13903a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0] = "ohci-tll-3pin-datse0",
14003a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM] = "ohci-tll-4pin-dpdm",
14103a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0] = "ohci-tll-2pin-datse0",
14203a8f438SRoger Quadros [OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM] = "ohci-tll-2pin-dpdm",
14303a8f438SRoger Quadros };
14403a8f438SRoger Quadros
omap_usbhs_alloc_child(const char * name,struct resource * res,int num_resources,void * pdata,size_t pdata_size,struct device * dev)14517cdd29dSKeshava Munegowda static struct platform_device *omap_usbhs_alloc_child(const char *name,
14617cdd29dSKeshava Munegowda struct resource *res, int num_resources, void *pdata,
14717cdd29dSKeshava Munegowda size_t pdata_size, struct device *dev)
14817cdd29dSKeshava Munegowda {
14917cdd29dSKeshava Munegowda struct platform_device *child;
15017cdd29dSKeshava Munegowda int ret;
15117cdd29dSKeshava Munegowda
15217cdd29dSKeshava Munegowda child = platform_device_alloc(name, 0);
15317cdd29dSKeshava Munegowda
15417cdd29dSKeshava Munegowda if (!child) {
15517cdd29dSKeshava Munegowda dev_err(dev, "platform_device_alloc %s failed\n", name);
15617cdd29dSKeshava Munegowda goto err_end;
15717cdd29dSKeshava Munegowda }
15817cdd29dSKeshava Munegowda
15917cdd29dSKeshava Munegowda ret = platform_device_add_resources(child, res, num_resources);
16017cdd29dSKeshava Munegowda if (ret) {
16117cdd29dSKeshava Munegowda dev_err(dev, "platform_device_add_resources failed\n");
16217cdd29dSKeshava Munegowda goto err_alloc;
16317cdd29dSKeshava Munegowda }
16417cdd29dSKeshava Munegowda
16517cdd29dSKeshava Munegowda ret = platform_device_add_data(child, pdata, pdata_size);
16617cdd29dSKeshava Munegowda if (ret) {
16717cdd29dSKeshava Munegowda dev_err(dev, "platform_device_add_data failed\n");
16817cdd29dSKeshava Munegowda goto err_alloc;
16917cdd29dSKeshava Munegowda }
17017cdd29dSKeshava Munegowda
17117cdd29dSKeshava Munegowda child->dev.dma_mask = &usbhs_dmamask;
172cbb8c220SGovindraj.R dma_set_coherent_mask(&child->dev, DMA_BIT_MASK(32));
17317cdd29dSKeshava Munegowda child->dev.parent = dev;
17417cdd29dSKeshava Munegowda
17517cdd29dSKeshava Munegowda ret = platform_device_add(child);
17617cdd29dSKeshava Munegowda if (ret) {
17717cdd29dSKeshava Munegowda dev_err(dev, "platform_device_add failed\n");
17817cdd29dSKeshava Munegowda goto err_alloc;
17917cdd29dSKeshava Munegowda }
18017cdd29dSKeshava Munegowda
18117cdd29dSKeshava Munegowda return child;
18217cdd29dSKeshava Munegowda
18317cdd29dSKeshava Munegowda err_alloc:
18417cdd29dSKeshava Munegowda platform_device_put(child);
18517cdd29dSKeshava Munegowda
18617cdd29dSKeshava Munegowda err_end:
18717cdd29dSKeshava Munegowda return NULL;
18817cdd29dSKeshava Munegowda }
18917cdd29dSKeshava Munegowda
omap_usbhs_alloc_children(struct platform_device * pdev)19017cdd29dSKeshava Munegowda static int omap_usbhs_alloc_children(struct platform_device *pdev)
19117cdd29dSKeshava Munegowda {
19217cdd29dSKeshava Munegowda struct device *dev = &pdev->dev;
193334a41ceSJingoo Han struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev);
19417cdd29dSKeshava Munegowda struct platform_device *ehci;
19517cdd29dSKeshava Munegowda struct platform_device *ohci;
19617cdd29dSKeshava Munegowda struct resource *res;
19717cdd29dSKeshava Munegowda struct resource resources[2];
19817cdd29dSKeshava Munegowda int ret;
19917cdd29dSKeshava Munegowda
20017cdd29dSKeshava Munegowda res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci");
20117cdd29dSKeshava Munegowda if (!res) {
20217cdd29dSKeshava Munegowda dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n");
20317cdd29dSKeshava Munegowda ret = -ENODEV;
20417cdd29dSKeshava Munegowda goto err_end;
20517cdd29dSKeshava Munegowda }
20617cdd29dSKeshava Munegowda resources[0] = *res;
20717cdd29dSKeshava Munegowda
20817cdd29dSKeshava Munegowda res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ehci-irq");
20917cdd29dSKeshava Munegowda if (!res) {
21017cdd29dSKeshava Munegowda dev_err(dev, " EHCI get resource IORESOURCE_IRQ failed\n");
21117cdd29dSKeshava Munegowda ret = -ENODEV;
21217cdd29dSKeshava Munegowda goto err_end;
21317cdd29dSKeshava Munegowda }
21417cdd29dSKeshava Munegowda resources[1] = *res;
21517cdd29dSKeshava Munegowda
2169d9c6ae7SRoger Quadros ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata,
2179d9c6ae7SRoger Quadros sizeof(*pdata), dev);
21817cdd29dSKeshava Munegowda
21917cdd29dSKeshava Munegowda if (!ehci) {
22017cdd29dSKeshava Munegowda dev_err(dev, "omap_usbhs_alloc_child failed\n");
221d910774fSAxel Lin ret = -ENOMEM;
22217cdd29dSKeshava Munegowda goto err_end;
22317cdd29dSKeshava Munegowda }
22417cdd29dSKeshava Munegowda
22517cdd29dSKeshava Munegowda res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci");
22617cdd29dSKeshava Munegowda if (!res) {
22717cdd29dSKeshava Munegowda dev_err(dev, "OHCI get resource IORESOURCE_MEM failed\n");
22817cdd29dSKeshava Munegowda ret = -ENODEV;
22917cdd29dSKeshava Munegowda goto err_ehci;
23017cdd29dSKeshava Munegowda }
23117cdd29dSKeshava Munegowda resources[0] = *res;
23217cdd29dSKeshava Munegowda
23317cdd29dSKeshava Munegowda res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ohci-irq");
23417cdd29dSKeshava Munegowda if (!res) {
23517cdd29dSKeshava Munegowda dev_err(dev, "OHCI get resource IORESOURCE_IRQ failed\n");
23617cdd29dSKeshava Munegowda ret = -ENODEV;
23717cdd29dSKeshava Munegowda goto err_ehci;
23817cdd29dSKeshava Munegowda }
23917cdd29dSKeshava Munegowda resources[1] = *res;
24017cdd29dSKeshava Munegowda
2419d9c6ae7SRoger Quadros ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata,
2429d9c6ae7SRoger Quadros sizeof(*pdata), dev);
24317cdd29dSKeshava Munegowda if (!ohci) {
24417cdd29dSKeshava Munegowda dev_err(dev, "omap_usbhs_alloc_child failed\n");
245d910774fSAxel Lin ret = -ENOMEM;
24617cdd29dSKeshava Munegowda goto err_ehci;
24717cdd29dSKeshava Munegowda }
24817cdd29dSKeshava Munegowda
24917cdd29dSKeshava Munegowda return 0;
25017cdd29dSKeshava Munegowda
25117cdd29dSKeshava Munegowda err_ehci:
252d910774fSAxel Lin platform_device_unregister(ehci);
25317cdd29dSKeshava Munegowda
25417cdd29dSKeshava Munegowda err_end:
25517cdd29dSKeshava Munegowda return ret;
25617cdd29dSKeshava Munegowda }
25717cdd29dSKeshava Munegowda
is_ohci_port(enum usbhs_omap_port_mode pmode)25817cdd29dSKeshava Munegowda static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
25917cdd29dSKeshava Munegowda {
26017cdd29dSKeshava Munegowda switch (pmode) {
26117cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0:
26217cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM:
26317cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0:
26417cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM:
26517cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0:
26617cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM:
26717cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0:
26817cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM:
26917cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0:
27017cdd29dSKeshava Munegowda case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM:
27117cdd29dSKeshava Munegowda return true;
27217cdd29dSKeshava Munegowda
27317cdd29dSKeshava Munegowda default:
27417cdd29dSKeshava Munegowda return false;
27517cdd29dSKeshava Munegowda }
27617cdd29dSKeshava Munegowda }
27717cdd29dSKeshava Munegowda
usbhs_runtime_resume(struct device * dev)2781e7fe1a9SKeshava Munegowda static int usbhs_runtime_resume(struct device *dev)
27917cdd29dSKeshava Munegowda {
28017cdd29dSKeshava Munegowda struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
2819d9c6ae7SRoger Quadros struct usbhs_omap_platform_data *pdata = omap->pdata;
28206ba7dc7SRoger Quadros int i, r;
28317cdd29dSKeshava Munegowda
2841e7fe1a9SKeshava Munegowda dev_dbg(dev, "usbhs_runtime_resume\n");
2851e7fe1a9SKeshava Munegowda
2869f4a3eceSRoger Quadros omap_tll_enable(pdata);
28717cdd29dSKeshava Munegowda
28806ba7dc7SRoger Quadros if (!IS_ERR(omap->ehci_logic_fck))
289b0e59926SRoger Quadros clk_prepare_enable(omap->ehci_logic_fck);
2901e7fe1a9SKeshava Munegowda
29106ba7dc7SRoger Quadros for (i = 0; i < omap->nports; i++) {
292340c64eaSRoger Quadros switch (pdata->port_mode[i]) {
293340c64eaSRoger Quadros case OMAP_EHCI_PORT_MODE_HSIC:
294340c64eaSRoger Quadros if (!IS_ERR(omap->hsic60m_clk[i])) {
295b0e59926SRoger Quadros r = clk_prepare_enable(omap->hsic60m_clk[i]);
296340c64eaSRoger Quadros if (r) {
297340c64eaSRoger Quadros dev_err(dev,
298340c64eaSRoger Quadros "Can't enable port %d hsic60m clk:%d\n",
299340c64eaSRoger Quadros i, r);
300340c64eaSRoger Quadros }
301340c64eaSRoger Quadros }
302760189b3SKeshava Munegowda
303340c64eaSRoger Quadros if (!IS_ERR(omap->hsic480m_clk[i])) {
304b0e59926SRoger Quadros r = clk_prepare_enable(omap->hsic480m_clk[i]);
305340c64eaSRoger Quadros if (r) {
306340c64eaSRoger Quadros dev_err(dev,
307340c64eaSRoger Quadros "Can't enable port %d hsic480m clk:%d\n",
308340c64eaSRoger Quadros i, r);
309340c64eaSRoger Quadros }
310340c64eaSRoger Quadros }
311df561f66SGustavo A. R. Silva fallthrough; /* as HSIC mode needs utmi_clk */
312340c64eaSRoger Quadros
313340c64eaSRoger Quadros case OMAP_EHCI_PORT_MODE_TLL:
314340c64eaSRoger Quadros if (!IS_ERR(omap->utmi_clk[i])) {
315b0e59926SRoger Quadros r = clk_prepare_enable(omap->utmi_clk[i]);
316340c64eaSRoger Quadros if (r) {
317340c64eaSRoger Quadros dev_err(dev,
318340c64eaSRoger Quadros "Can't enable port %d clk : %d\n",
319340c64eaSRoger Quadros i, r);
320340c64eaSRoger Quadros }
321340c64eaSRoger Quadros }
322340c64eaSRoger Quadros break;
323340c64eaSRoger Quadros default:
324340c64eaSRoger Quadros break;
325340c64eaSRoger Quadros }
32606ba7dc7SRoger Quadros }
3271e7fe1a9SKeshava Munegowda
3281e7fe1a9SKeshava Munegowda return 0;
3291e7fe1a9SKeshava Munegowda }
3301e7fe1a9SKeshava Munegowda
usbhs_runtime_suspend(struct device * dev)3311e7fe1a9SKeshava Munegowda static int usbhs_runtime_suspend(struct device *dev)
3321e7fe1a9SKeshava Munegowda {
3331e7fe1a9SKeshava Munegowda struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
3349d9c6ae7SRoger Quadros struct usbhs_omap_platform_data *pdata = omap->pdata;
33506ba7dc7SRoger Quadros int i;
3361e7fe1a9SKeshava Munegowda
3371e7fe1a9SKeshava Munegowda dev_dbg(dev, "usbhs_runtime_suspend\n");
3381e7fe1a9SKeshava Munegowda
33906ba7dc7SRoger Quadros for (i = 0; i < omap->nports; i++) {
340340c64eaSRoger Quadros switch (pdata->port_mode[i]) {
341340c64eaSRoger Quadros case OMAP_EHCI_PORT_MODE_HSIC:
342340c64eaSRoger Quadros if (!IS_ERR(omap->hsic60m_clk[i]))
343b0e59926SRoger Quadros clk_disable_unprepare(omap->hsic60m_clk[i]);
344340c64eaSRoger Quadros
345340c64eaSRoger Quadros if (!IS_ERR(omap->hsic480m_clk[i]))
346b0e59926SRoger Quadros clk_disable_unprepare(omap->hsic480m_clk[i]);
347df561f66SGustavo A. R. Silva fallthrough; /* as utmi_clks were used in HSIC mode */
348340c64eaSRoger Quadros
349340c64eaSRoger Quadros case OMAP_EHCI_PORT_MODE_TLL:
350340c64eaSRoger Quadros if (!IS_ERR(omap->utmi_clk[i]))
351b0e59926SRoger Quadros clk_disable_unprepare(omap->utmi_clk[i]);
352340c64eaSRoger Quadros break;
353340c64eaSRoger Quadros default:
354340c64eaSRoger Quadros break;
355340c64eaSRoger Quadros }
35606ba7dc7SRoger Quadros }
357760189b3SKeshava Munegowda
35806ba7dc7SRoger Quadros if (!IS_ERR(omap->ehci_logic_fck))
359b0e59926SRoger Quadros clk_disable_unprepare(omap->ehci_logic_fck);
3601e7fe1a9SKeshava Munegowda
3619f4a3eceSRoger Quadros omap_tll_disable(pdata);
3621e7fe1a9SKeshava Munegowda
3631e7fe1a9SKeshava Munegowda return 0;
3641e7fe1a9SKeshava Munegowda }
3651e7fe1a9SKeshava Munegowda
omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap * omap,unsigned reg)366c4df00aeSRoger Quadros static unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap,
367c4df00aeSRoger Quadros unsigned reg)
368c4df00aeSRoger Quadros {
369c4df00aeSRoger Quadros struct usbhs_omap_platform_data *pdata = omap->pdata;
370c4df00aeSRoger Quadros int i;
371c4df00aeSRoger Quadros
372c4df00aeSRoger Quadros for (i = 0; i < omap->nports; i++) {
373c4df00aeSRoger Quadros switch (pdata->port_mode[i]) {
374c4df00aeSRoger Quadros case OMAP_USBHS_PORT_MODE_UNUSED:
375c4df00aeSRoger Quadros reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i);
376c4df00aeSRoger Quadros break;
377c4df00aeSRoger Quadros case OMAP_EHCI_PORT_MODE_PHY:
378c4df00aeSRoger Quadros if (pdata->single_ulpi_bypass)
379c4df00aeSRoger Quadros break;
380c4df00aeSRoger Quadros
381c4df00aeSRoger Quadros if (i == 0)
382c4df00aeSRoger Quadros reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
383c4df00aeSRoger Quadros else
384c4df00aeSRoger Quadros reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
385c4df00aeSRoger Quadros << (i-1));
386c4df00aeSRoger Quadros break;
387c4df00aeSRoger Quadros default:
388c4df00aeSRoger Quadros if (pdata->single_ulpi_bypass)
389c4df00aeSRoger Quadros break;
390c4df00aeSRoger Quadros
391c4df00aeSRoger Quadros if (i == 0)
392c4df00aeSRoger Quadros reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS;
393c4df00aeSRoger Quadros else
394c4df00aeSRoger Quadros reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS
395c4df00aeSRoger Quadros << (i-1);
396c4df00aeSRoger Quadros break;
397c4df00aeSRoger Quadros }
398c4df00aeSRoger Quadros }
399c4df00aeSRoger Quadros
400c4df00aeSRoger Quadros if (pdata->single_ulpi_bypass) {
401c4df00aeSRoger Quadros /* bypass ULPI only if none of the ports use PHY mode */
402c4df00aeSRoger Quadros reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
403c4df00aeSRoger Quadros
404c4df00aeSRoger Quadros for (i = 0; i < omap->nports; i++) {
405c4df00aeSRoger Quadros if (is_ehci_phy_mode(pdata->port_mode[i])) {
40646de8ff8SMichael Welling reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS;
407c4df00aeSRoger Quadros break;
408c4df00aeSRoger Quadros }
409c4df00aeSRoger Quadros }
410c4df00aeSRoger Quadros }
411c4df00aeSRoger Quadros
412c4df00aeSRoger Quadros return reg;
413c4df00aeSRoger Quadros }
414c4df00aeSRoger Quadros
omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap * omap,unsigned reg)415c4df00aeSRoger Quadros static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap,
416c4df00aeSRoger Quadros unsigned reg)
417c4df00aeSRoger Quadros {
418c4df00aeSRoger Quadros struct usbhs_omap_platform_data *pdata = omap->pdata;
419c4df00aeSRoger Quadros int i;
420c4df00aeSRoger Quadros
421c4df00aeSRoger Quadros for (i = 0; i < omap->nports; i++) {
422c4df00aeSRoger Quadros /* Clear port mode fields for PHY mode */
423c4df00aeSRoger Quadros reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i);
424c4df00aeSRoger Quadros
425c4df00aeSRoger Quadros if (is_ehci_tll_mode(pdata->port_mode[i]) ||
426c4df00aeSRoger Quadros (is_ohci_port(pdata->port_mode[i])))
427c4df00aeSRoger Quadros reg |= OMAP4_P1_MODE_TLL << 2 * i;
428c4df00aeSRoger Quadros else if (is_ehci_hsic_mode(pdata->port_mode[i]))
429c4df00aeSRoger Quadros reg |= OMAP4_P1_MODE_HSIC << 2 * i;
430c4df00aeSRoger Quadros }
431c4df00aeSRoger Quadros
432c4df00aeSRoger Quadros return reg;
433c4df00aeSRoger Quadros }
434c4df00aeSRoger Quadros
omap_usbhs_init(struct device * dev)4351e7fe1a9SKeshava Munegowda static void omap_usbhs_init(struct device *dev)
4361e7fe1a9SKeshava Munegowda {
4371e7fe1a9SKeshava Munegowda struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
4381e7fe1a9SKeshava Munegowda unsigned reg;
4391e7fe1a9SKeshava Munegowda
4401e7fe1a9SKeshava Munegowda dev_dbg(dev, "starting TI HSUSB Controller\n");
4411e7fe1a9SKeshava Munegowda
442760189b3SKeshava Munegowda pm_runtime_get_sync(dev);
44317cdd29dSKeshava Munegowda
44417cdd29dSKeshava Munegowda reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
44517cdd29dSKeshava Munegowda /* setup ULPI bypass and burst configurations */
44617cdd29dSKeshava Munegowda reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
44717cdd29dSKeshava Munegowda | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
44817cdd29dSKeshava Munegowda | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
44917cdd29dSKeshava Munegowda reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK;
45017cdd29dSKeshava Munegowda reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
45117cdd29dSKeshava Munegowda
452c4df00aeSRoger Quadros switch (omap->usbhs_rev) {
453c4df00aeSRoger Quadros case OMAP_USBHS_REV1:
45426bacba1SRoger Quadros reg = omap_usbhs_rev1_hostconfig(omap, reg);
455c4df00aeSRoger Quadros break;
45617cdd29dSKeshava Munegowda
457c4df00aeSRoger Quadros case OMAP_USBHS_REV2:
45826bacba1SRoger Quadros reg = omap_usbhs_rev2_hostconfig(omap, reg);
459c4df00aeSRoger Quadros break;
46017cdd29dSKeshava Munegowda
461c4df00aeSRoger Quadros default: /* newer revisions */
46226bacba1SRoger Quadros reg = omap_usbhs_rev2_hostconfig(omap, reg);
463c4df00aeSRoger Quadros break;
46417cdd29dSKeshava Munegowda }
46517cdd29dSKeshava Munegowda
46617cdd29dSKeshava Munegowda usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg);
46717cdd29dSKeshava Munegowda dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg);
46817cdd29dSKeshava Munegowda
469760189b3SKeshava Munegowda pm_runtime_put_sync(dev);
47017cdd29dSKeshava Munegowda }
4718f2df014SKeshava Munegowda
usbhs_omap_get_dt_pdata(struct device * dev,struct usbhs_omap_platform_data * pdata)47203a8f438SRoger Quadros static int usbhs_omap_get_dt_pdata(struct device *dev,
47303a8f438SRoger Quadros struct usbhs_omap_platform_data *pdata)
47403a8f438SRoger Quadros {
47503a8f438SRoger Quadros int ret, i;
47603a8f438SRoger Quadros struct device_node *node = dev->of_node;
47703a8f438SRoger Quadros
47803a8f438SRoger Quadros ret = of_property_read_u32(node, "num-ports", &pdata->nports);
47903a8f438SRoger Quadros if (ret)
48003a8f438SRoger Quadros pdata->nports = 0;
48103a8f438SRoger Quadros
48203a8f438SRoger Quadros if (pdata->nports > OMAP3_HS_USB_PORTS) {
48303a8f438SRoger Quadros dev_warn(dev, "Too many num_ports <%d> in device tree. Max %d\n",
48403a8f438SRoger Quadros pdata->nports, OMAP3_HS_USB_PORTS);
48503a8f438SRoger Quadros return -ENODEV;
48603a8f438SRoger Quadros }
48703a8f438SRoger Quadros
48803a8f438SRoger Quadros /* get port modes */
48903a8f438SRoger Quadros for (i = 0; i < OMAP3_HS_USB_PORTS; i++) {
49003a8f438SRoger Quadros char prop[11];
49103a8f438SRoger Quadros const char *mode;
49203a8f438SRoger Quadros
49303a8f438SRoger Quadros pdata->port_mode[i] = OMAP_USBHS_PORT_MODE_UNUSED;
49403a8f438SRoger Quadros
49503a8f438SRoger Quadros snprintf(prop, sizeof(prop), "port%d-mode", i + 1);
49603a8f438SRoger Quadros ret = of_property_read_string(node, prop, &mode);
49703a8f438SRoger Quadros if (ret < 0)
49803a8f438SRoger Quadros continue;
49903a8f438SRoger Quadros
5004c74a1fcSYisheng Xie /* get 'enum usbhs_omap_port_mode' from port mode string */
5014c74a1fcSYisheng Xie ret = match_string(port_modes, ARRAY_SIZE(port_modes), mode);
50203a8f438SRoger Quadros if (ret < 0) {
50303a8f438SRoger Quadros dev_warn(dev, "Invalid port%d-mode \"%s\" in device tree\n",
50403a8f438SRoger Quadros i, mode);
50503a8f438SRoger Quadros return -ENODEV;
50603a8f438SRoger Quadros }
50703a8f438SRoger Quadros
50803a8f438SRoger Quadros dev_dbg(dev, "port%d-mode: %s -> %d\n", i, mode, ret);
50903a8f438SRoger Quadros pdata->port_mode[i] = ret;
51003a8f438SRoger Quadros }
51103a8f438SRoger Quadros
51203a8f438SRoger Quadros /* get flags */
51303a8f438SRoger Quadros pdata->single_ulpi_bypass = of_property_read_bool(node,
51403a8f438SRoger Quadros "single-ulpi-bypass");
51503a8f438SRoger Quadros
51603a8f438SRoger Quadros return 0;
51703a8f438SRoger Quadros }
51803a8f438SRoger Quadros
519a7cfee81SKrzysztof Kozlowski static const struct of_device_id usbhs_child_match_table[] = {
52010492ee8STony Lindgren { .compatible = "ti,ehci-omap", },
52110492ee8STony Lindgren { .compatible = "ti,ohci-omap3", },
52203a8f438SRoger Quadros { }
52303a8f438SRoger Quadros };
52403a8f438SRoger Quadros
5251e7fe1a9SKeshava Munegowda /**
5261e7fe1a9SKeshava Munegowda * usbhs_omap_probe - initialize TI-based HCDs
5271e7fe1a9SKeshava Munegowda *
5281e7fe1a9SKeshava Munegowda * Allocates basic resources for this USB host controller.
5293fc65627SLee Jones *
5303fc65627SLee Jones * @pdev: Pointer to this device's platform device structure
5311e7fe1a9SKeshava Munegowda */
usbhs_omap_probe(struct platform_device * pdev)532f791be49SBill Pemberton static int usbhs_omap_probe(struct platform_device *pdev)
53317cdd29dSKeshava Munegowda {
5341e7fe1a9SKeshava Munegowda struct device *dev = &pdev->dev;
535334a41ceSJingoo Han struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev);
5361e7fe1a9SKeshava Munegowda struct usbhs_hcd_omap *omap;
5371e7fe1a9SKeshava Munegowda int ret = 0;
5381e7fe1a9SKeshava Munegowda int i;
53906ba7dc7SRoger Quadros bool need_logic_fck;
54017cdd29dSKeshava Munegowda
54103a8f438SRoger Quadros if (dev->of_node) {
54203a8f438SRoger Quadros /* For DT boot we populate platform data from OF node */
54303a8f438SRoger Quadros pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
54403a8f438SRoger Quadros if (!pdata)
54503a8f438SRoger Quadros return -ENOMEM;
54603a8f438SRoger Quadros
54703a8f438SRoger Quadros ret = usbhs_omap_get_dt_pdata(dev, pdata);
54803a8f438SRoger Quadros if (ret)
54903a8f438SRoger Quadros return ret;
55003a8f438SRoger Quadros
55103a8f438SRoger Quadros dev->platform_data = pdata;
55203a8f438SRoger Quadros }
55303a8f438SRoger Quadros
5541e7fe1a9SKeshava Munegowda if (!pdata) {
5551e7fe1a9SKeshava Munegowda dev_err(dev, "Missing platform data\n");
55627d4f2c6SRoger Quadros return -ENODEV;
55717cdd29dSKeshava Munegowda }
5581e7fe1a9SKeshava Munegowda
55903a8f438SRoger Quadros if (pdata->nports > OMAP3_HS_USB_PORTS) {
56003a8f438SRoger Quadros dev_info(dev, "Too many num_ports <%d> in platform_data. Max %d\n",
56103a8f438SRoger Quadros pdata->nports, OMAP3_HS_USB_PORTS);
56203a8f438SRoger Quadros return -ENODEV;
56303a8f438SRoger Quadros }
56403a8f438SRoger Quadros
56527d4f2c6SRoger Quadros omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
5661e7fe1a9SKeshava Munegowda if (!omap) {
5671e7fe1a9SKeshava Munegowda dev_err(dev, "Memory allocation failed\n");
56827d4f2c6SRoger Quadros return -ENOMEM;
56927d4f2c6SRoger Quadros }
57027d4f2c6SRoger Quadros
571*390a3549SYangtao Li omap->uhh_base = devm_platform_ioremap_resource(pdev, 0);
572d011c450SSachin Kamat if (IS_ERR(omap->uhh_base))
573d011c450SSachin Kamat return PTR_ERR(omap->uhh_base);
5741e7fe1a9SKeshava Munegowda
5759d9c6ae7SRoger Quadros omap->pdata = pdata;
5761e7fe1a9SKeshava Munegowda
5779f4a3eceSRoger Quadros /* Initialize the TLL subsystem */
5789f4a3eceSRoger Quadros omap_tll_init(pdata);
5799f4a3eceSRoger Quadros
5801e7fe1a9SKeshava Munegowda pm_runtime_enable(dev);
5811e7fe1a9SKeshava Munegowda
582d7eaf866SRoger Quadros platform_set_drvdata(pdev, omap);
583d7eaf866SRoger Quadros pm_runtime_get_sync(dev);
584d7eaf866SRoger Quadros
585d7eaf866SRoger Quadros omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
586d7eaf866SRoger Quadros
587d7eaf866SRoger Quadros /* we need to call runtime suspend before we update omap->nports
588d7eaf866SRoger Quadros * to prevent unbalanced clk_disable()
589d7eaf866SRoger Quadros */
590d7eaf866SRoger Quadros pm_runtime_put_sync(dev);
591d7eaf866SRoger Quadros
592ccac71a7SRoger Quadros /*
593ccac71a7SRoger Quadros * If platform data contains nports then use that
594ccac71a7SRoger Quadros * else make out number of ports from USBHS revision
595ccac71a7SRoger Quadros */
596ccac71a7SRoger Quadros if (pdata->nports) {
597ccac71a7SRoger Quadros omap->nports = pdata->nports;
598ccac71a7SRoger Quadros } else {
599d7eaf866SRoger Quadros switch (omap->usbhs_rev) {
600d7eaf866SRoger Quadros case OMAP_USBHS_REV1:
601d7eaf866SRoger Quadros omap->nports = 3;
602d7eaf866SRoger Quadros break;
603d7eaf866SRoger Quadros case OMAP_USBHS_REV2:
604d7eaf866SRoger Quadros omap->nports = 2;
605d7eaf866SRoger Quadros break;
606d7eaf866SRoger Quadros default:
607d7eaf866SRoger Quadros omap->nports = OMAP3_HS_USB_PORTS;
608d7eaf866SRoger Quadros dev_dbg(dev,
609ddde06b1SHans Wennborg "USB HOST Rev:0x%x not recognized, assuming %d ports\n",
610d7eaf866SRoger Quadros omap->usbhs_rev, omap->nports);
611d7eaf866SRoger Quadros break;
612d7eaf866SRoger Quadros }
613662e469eSRoger Quadros pdata->nports = omap->nports;
614ccac71a7SRoger Quadros }
615d7eaf866SRoger Quadros
61606ba7dc7SRoger Quadros i = sizeof(struct clk *) * omap->nports;
61706ba7dc7SRoger Quadros omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL);
618340c64eaSRoger Quadros omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
619340c64eaSRoger Quadros omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL);
620340c64eaSRoger Quadros
621340c64eaSRoger Quadros if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) {
62206ba7dc7SRoger Quadros dev_err(dev, "Memory allocation failed\n");
62306ba7dc7SRoger Quadros ret = -ENOMEM;
62406ba7dc7SRoger Quadros goto err_mem;
62506ba7dc7SRoger Quadros }
62606ba7dc7SRoger Quadros
6273aca446aSRoger Quadros /* Set all clocks as invalid to begin with */
6283aca446aSRoger Quadros omap->ehci_logic_fck = ERR_PTR(-ENODEV);
6293aca446aSRoger Quadros omap->init_60m_fclk = ERR_PTR(-ENODEV);
6303aca446aSRoger Quadros omap->utmi_p1_gfclk = ERR_PTR(-ENODEV);
6313aca446aSRoger Quadros omap->utmi_p2_gfclk = ERR_PTR(-ENODEV);
6323aca446aSRoger Quadros omap->xclk60mhsp1_ck = ERR_PTR(-ENODEV);
6333aca446aSRoger Quadros omap->xclk60mhsp2_ck = ERR_PTR(-ENODEV);
6343aca446aSRoger Quadros
6353aca446aSRoger Quadros for (i = 0; i < omap->nports; i++) {
6363aca446aSRoger Quadros omap->utmi_clk[i] = ERR_PTR(-ENODEV);
6373aca446aSRoger Quadros omap->hsic480m_clk[i] = ERR_PTR(-ENODEV);
6383aca446aSRoger Quadros omap->hsic60m_clk[i] = ERR_PTR(-ENODEV);
6393aca446aSRoger Quadros }
6403aca446aSRoger Quadros
6413aca446aSRoger Quadros /* for OMAP3 i.e. USBHS REV1 */
6423aca446aSRoger Quadros if (omap->usbhs_rev == OMAP_USBHS_REV1) {
64306ba7dc7SRoger Quadros need_logic_fck = false;
64406ba7dc7SRoger Quadros for (i = 0; i < omap->nports; i++) {
6453aca446aSRoger Quadros if (is_ehci_phy_mode(pdata->port_mode[i]) ||
6463aca446aSRoger Quadros is_ehci_tll_mode(pdata->port_mode[i]) ||
6473aca446aSRoger Quadros is_ehci_hsic_mode(pdata->port_mode[i]))
6483aca446aSRoger Quadros
64906ba7dc7SRoger Quadros need_logic_fck |= true;
65006ba7dc7SRoger Quadros }
65106ba7dc7SRoger Quadros
65206ba7dc7SRoger Quadros if (need_logic_fck) {
6533aca446aSRoger Quadros omap->ehci_logic_fck = devm_clk_get(dev,
654775bb078SRoger Quadros "usbhost_120m_fck");
6551e7fe1a9SKeshava Munegowda if (IS_ERR(omap->ehci_logic_fck)) {
6561e7fe1a9SKeshava Munegowda ret = PTR_ERR(omap->ehci_logic_fck);
657775bb078SRoger Quadros dev_err(dev, "usbhost_120m_fck failed:%d\n",
658775bb078SRoger Quadros ret);
659fedb2e7cSRoger Quadros goto err_mem;
6601e7fe1a9SKeshava Munegowda }
6611e7fe1a9SKeshava Munegowda }
6623aca446aSRoger Quadros goto initialize;
6633aca446aSRoger Quadros }
6641e7fe1a9SKeshava Munegowda
6653aca446aSRoger Quadros /* for OMAP4+ i.e. USBHS REV2+ */
66661b7025fSRoger Quadros omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk");
66706ba7dc7SRoger Quadros if (IS_ERR(omap->utmi_p1_gfclk)) {
66806ba7dc7SRoger Quadros ret = PTR_ERR(omap->utmi_p1_gfclk);
6691e7fe1a9SKeshava Munegowda dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
67061b7025fSRoger Quadros goto err_mem;
67106ba7dc7SRoger Quadros }
67206ba7dc7SRoger Quadros
67361b7025fSRoger Quadros omap->utmi_p2_gfclk = devm_clk_get(dev, "utmi_p2_gfclk");
67406ba7dc7SRoger Quadros if (IS_ERR(omap->utmi_p2_gfclk)) {
67506ba7dc7SRoger Quadros ret = PTR_ERR(omap->utmi_p2_gfclk);
67606ba7dc7SRoger Quadros dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
67761b7025fSRoger Quadros goto err_mem;
6781e7fe1a9SKeshava Munegowda }
6791e7fe1a9SKeshava Munegowda
680051fc06dSRoger Quadros omap->xclk60mhsp1_ck = devm_clk_get(dev, "refclk_60m_ext_p1");
6811e7fe1a9SKeshava Munegowda if (IS_ERR(omap->xclk60mhsp1_ck)) {
6821e7fe1a9SKeshava Munegowda ret = PTR_ERR(omap->xclk60mhsp1_ck);
683051fc06dSRoger Quadros dev_err(dev, "refclk_60m_ext_p1 failed error:%d\n", ret);
68461b7025fSRoger Quadros goto err_mem;
6851e7fe1a9SKeshava Munegowda }
6861e7fe1a9SKeshava Munegowda
687051fc06dSRoger Quadros omap->xclk60mhsp2_ck = devm_clk_get(dev, "refclk_60m_ext_p2");
6881e7fe1a9SKeshava Munegowda if (IS_ERR(omap->xclk60mhsp2_ck)) {
6891e7fe1a9SKeshava Munegowda ret = PTR_ERR(omap->xclk60mhsp2_ck);
690051fc06dSRoger Quadros dev_err(dev, "refclk_60m_ext_p2 failed error:%d\n", ret);
69161b7025fSRoger Quadros goto err_mem;
6921e7fe1a9SKeshava Munegowda }
6931e7fe1a9SKeshava Munegowda
694051fc06dSRoger Quadros omap->init_60m_fclk = devm_clk_get(dev, "refclk_60m_int");
6951e7fe1a9SKeshava Munegowda if (IS_ERR(omap->init_60m_fclk)) {
6961e7fe1a9SKeshava Munegowda ret = PTR_ERR(omap->init_60m_fclk);
697051fc06dSRoger Quadros dev_err(dev, "refclk_60m_int failed error:%d\n", ret);
69861b7025fSRoger Quadros goto err_mem;
69906ba7dc7SRoger Quadros }
70006ba7dc7SRoger Quadros
70106ba7dc7SRoger Quadros for (i = 0; i < omap->nports; i++) {
702340c64eaSRoger Quadros char clkname[30];
70306ba7dc7SRoger Quadros
70406ba7dc7SRoger Quadros /* clock names are indexed from 1*/
70506ba7dc7SRoger Quadros snprintf(clkname, sizeof(clkname),
70606ba7dc7SRoger Quadros "usb_host_hs_utmi_p%d_clk", i + 1);
70706ba7dc7SRoger Quadros
70806ba7dc7SRoger Quadros /* If a clock is not found we won't bail out as not all
70906ba7dc7SRoger Quadros * platforms have all clocks and we can function without
71006ba7dc7SRoger Quadros * them
71106ba7dc7SRoger Quadros */
71261b7025fSRoger Quadros omap->utmi_clk[i] = devm_clk_get(dev, clkname);
713fedb2e7cSRoger Quadros if (IS_ERR(omap->utmi_clk[i])) {
714fedb2e7cSRoger Quadros ret = PTR_ERR(omap->utmi_clk[i]);
715fedb2e7cSRoger Quadros dev_err(dev, "Failed to get clock : %s : %d\n",
716fedb2e7cSRoger Quadros clkname, ret);
717fedb2e7cSRoger Quadros goto err_mem;
718fedb2e7cSRoger Quadros }
719340c64eaSRoger Quadros
720340c64eaSRoger Quadros snprintf(clkname, sizeof(clkname),
721340c64eaSRoger Quadros "usb_host_hs_hsic480m_p%d_clk", i + 1);
72261b7025fSRoger Quadros omap->hsic480m_clk[i] = devm_clk_get(dev, clkname);
723fedb2e7cSRoger Quadros if (IS_ERR(omap->hsic480m_clk[i])) {
724fedb2e7cSRoger Quadros ret = PTR_ERR(omap->hsic480m_clk[i]);
725fedb2e7cSRoger Quadros dev_err(dev, "Failed to get clock : %s : %d\n",
726fedb2e7cSRoger Quadros clkname, ret);
727fedb2e7cSRoger Quadros goto err_mem;
728fedb2e7cSRoger Quadros }
729340c64eaSRoger Quadros
730340c64eaSRoger Quadros snprintf(clkname, sizeof(clkname),
731340c64eaSRoger Quadros "usb_host_hs_hsic60m_p%d_clk", i + 1);
73261b7025fSRoger Quadros omap->hsic60m_clk[i] = devm_clk_get(dev, clkname);
733fedb2e7cSRoger Quadros if (IS_ERR(omap->hsic60m_clk[i])) {
734fedb2e7cSRoger Quadros ret = PTR_ERR(omap->hsic60m_clk[i]);
735fedb2e7cSRoger Quadros dev_err(dev, "Failed to get clock : %s : %d\n",
736fedb2e7cSRoger Quadros clkname, ret);
737fedb2e7cSRoger Quadros goto err_mem;
738fedb2e7cSRoger Quadros }
7391e7fe1a9SKeshava Munegowda }
7401e7fe1a9SKeshava Munegowda
7411e7fe1a9SKeshava Munegowda if (is_ehci_phy_mode(pdata->port_mode[0])) {
74206ba7dc7SRoger Quadros ret = clk_set_parent(omap->utmi_p1_gfclk,
7431e7fe1a9SKeshava Munegowda omap->xclk60mhsp1_ck);
744fedb2e7cSRoger Quadros if (ret != 0) {
745fedb2e7cSRoger Quadros dev_err(dev, "xclk60mhsp1_ck set parent failed: %d\n",
746a8c4e9e1SRoger Quadros ret);
747fedb2e7cSRoger Quadros goto err_mem;
748fedb2e7cSRoger Quadros }
7491e7fe1a9SKeshava Munegowda } else if (is_ehci_tll_mode(pdata->port_mode[0])) {
75006ba7dc7SRoger Quadros ret = clk_set_parent(omap->utmi_p1_gfclk,
7511e7fe1a9SKeshava Munegowda omap->init_60m_fclk);
752fedb2e7cSRoger Quadros if (ret != 0) {
753fedb2e7cSRoger Quadros dev_err(dev, "P0 init_60m_fclk set parent failed: %d\n",
754a8c4e9e1SRoger Quadros ret);
755fedb2e7cSRoger Quadros goto err_mem;
756fedb2e7cSRoger Quadros }
7571e7fe1a9SKeshava Munegowda }
7581e7fe1a9SKeshava Munegowda
7591e7fe1a9SKeshava Munegowda if (is_ehci_phy_mode(pdata->port_mode[1])) {
76006ba7dc7SRoger Quadros ret = clk_set_parent(omap->utmi_p2_gfclk,
7611e7fe1a9SKeshava Munegowda omap->xclk60mhsp2_ck);
762fedb2e7cSRoger Quadros if (ret != 0) {
763fedb2e7cSRoger Quadros dev_err(dev, "xclk60mhsp2_ck set parent failed: %d\n",
764a8c4e9e1SRoger Quadros ret);
765fedb2e7cSRoger Quadros goto err_mem;
766fedb2e7cSRoger Quadros }
7671e7fe1a9SKeshava Munegowda } else if (is_ehci_tll_mode(pdata->port_mode[1])) {
76806ba7dc7SRoger Quadros ret = clk_set_parent(omap->utmi_p2_gfclk,
7691e7fe1a9SKeshava Munegowda omap->init_60m_fclk);
770fedb2e7cSRoger Quadros if (ret != 0) {
771fedb2e7cSRoger Quadros dev_err(dev, "P1 init_60m_fclk set parent failed: %d\n",
772a8c4e9e1SRoger Quadros ret);
773fedb2e7cSRoger Quadros goto err_mem;
774fedb2e7cSRoger Quadros }
7751e7fe1a9SKeshava Munegowda }
7761e7fe1a9SKeshava Munegowda
7773aca446aSRoger Quadros initialize:
778f0447a69SGovindraj.R omap_usbhs_init(dev);
77903a8f438SRoger Quadros
78003a8f438SRoger Quadros if (dev->of_node) {
78103a8f438SRoger Quadros ret = of_platform_populate(dev->of_node,
78203a8f438SRoger Quadros usbhs_child_match_table, NULL, dev);
78303a8f438SRoger Quadros
78403a8f438SRoger Quadros if (ret) {
78503a8f438SRoger Quadros dev_err(dev, "Failed to create DT children: %d\n", ret);
78661b7025fSRoger Quadros goto err_mem;
78703a8f438SRoger Quadros }
78803a8f438SRoger Quadros
78903a8f438SRoger Quadros } else {
7901e7fe1a9SKeshava Munegowda ret = omap_usbhs_alloc_children(pdev);
7911e7fe1a9SKeshava Munegowda if (ret) {
79203a8f438SRoger Quadros dev_err(dev, "omap_usbhs_alloc_children failed: %d\n",
79303a8f438SRoger Quadros ret);
79461b7025fSRoger Quadros goto err_mem;
7951e7fe1a9SKeshava Munegowda }
79603a8f438SRoger Quadros }
7971e7fe1a9SKeshava Munegowda
79827d4f2c6SRoger Quadros return 0;
7991e7fe1a9SKeshava Munegowda
80006ba7dc7SRoger Quadros err_mem:
8011e7fe1a9SKeshava Munegowda pm_runtime_disable(dev);
8021e7fe1a9SKeshava Munegowda
8031e7fe1a9SKeshava Munegowda return ret;
8041e7fe1a9SKeshava Munegowda }
8051e7fe1a9SKeshava Munegowda
usbhs_omap_remove_child(struct device * dev,void * data)80603a8f438SRoger Quadros static int usbhs_omap_remove_child(struct device *dev, void *data)
80703a8f438SRoger Quadros {
80803a8f438SRoger Quadros dev_info(dev, "unregistering\n");
80903a8f438SRoger Quadros platform_device_unregister(to_platform_device(dev));
81003a8f438SRoger Quadros return 0;
81103a8f438SRoger Quadros }
81203a8f438SRoger Quadros
8131e7fe1a9SKeshava Munegowda /**
8141e7fe1a9SKeshava Munegowda * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs
8151e7fe1a9SKeshava Munegowda * @pdev: USB Host Controller being removed
8161e7fe1a9SKeshava Munegowda *
8171e7fe1a9SKeshava Munegowda * Reverses the effect of usbhs_omap_probe().
8181e7fe1a9SKeshava Munegowda */
usbhs_omap_remove(struct platform_device * pdev)8194740f73fSBill Pemberton static int usbhs_omap_remove(struct platform_device *pdev)
8201e7fe1a9SKeshava Munegowda {
8211e7fe1a9SKeshava Munegowda pm_runtime_disable(&pdev->dev);
8221e7fe1a9SKeshava Munegowda
82303a8f438SRoger Quadros /* remove children */
82403a8f438SRoger Quadros device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child);
8251e7fe1a9SKeshava Munegowda return 0;
8261e7fe1a9SKeshava Munegowda }
8271e7fe1a9SKeshava Munegowda
8281e7fe1a9SKeshava Munegowda static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
8291e7fe1a9SKeshava Munegowda .runtime_suspend = usbhs_runtime_suspend,
8301e7fe1a9SKeshava Munegowda .runtime_resume = usbhs_runtime_resume,
8311e7fe1a9SKeshava Munegowda };
83217cdd29dSKeshava Munegowda
83303a8f438SRoger Quadros static const struct of_device_id usbhs_omap_dt_ids[] = {
83403a8f438SRoger Quadros { .compatible = "ti,usbhs-host" },
83503a8f438SRoger Quadros { }
83603a8f438SRoger Quadros };
83703a8f438SRoger Quadros
83803a8f438SRoger Quadros MODULE_DEVICE_TABLE(of, usbhs_omap_dt_ids);
83903a8f438SRoger Quadros
84003a8f438SRoger Quadros
84117cdd29dSKeshava Munegowda static struct platform_driver usbhs_omap_driver = {
84217cdd29dSKeshava Munegowda .driver = {
8439a153b0eSCorentin Labbe .name = usbhs_driver_name,
8441e7fe1a9SKeshava Munegowda .pm = &usbhsomap_dev_pm_ops,
8450f54e1e1SSachin Kamat .of_match_table = usbhs_omap_dt_ids,
84617cdd29dSKeshava Munegowda },
84710492ee8STony Lindgren .probe = usbhs_omap_probe,
848ab3f2a86SRoger Quadros .remove = usbhs_omap_remove,
84917cdd29dSKeshava Munegowda };
85017cdd29dSKeshava Munegowda
85117cdd29dSKeshava Munegowda MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>");
85203a8f438SRoger Quadros MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
85317cdd29dSKeshava Munegowda MODULE_ALIAS("platform:" USBHS_DRIVER_NAME);
85417cdd29dSKeshava Munegowda MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI");
85517cdd29dSKeshava Munegowda
omap_usbhs_drvinit(void)85610492ee8STony Lindgren static int omap_usbhs_drvinit(void)
85717cdd29dSKeshava Munegowda {
85810492ee8STony Lindgren return platform_driver_register(&usbhs_omap_driver);
85917cdd29dSKeshava Munegowda }
86017cdd29dSKeshava Munegowda
86117cdd29dSKeshava Munegowda /*
86217cdd29dSKeshava Munegowda * init before ehci and ohci drivers;
86317cdd29dSKeshava Munegowda * The usbhs core driver should be initialized much before
86417cdd29dSKeshava Munegowda * the omap ehci and ohci probe functions are called.
8654dc2ccebSKeshava Munegowda * This usbhs core driver should be initialized after
8664dc2ccebSKeshava Munegowda * usb tll driver
86717cdd29dSKeshava Munegowda */
8684dc2ccebSKeshava Munegowda fs_initcall_sync(omap_usbhs_drvinit);
86917cdd29dSKeshava Munegowda
omap_usbhs_drvexit(void)87010492ee8STony Lindgren static void omap_usbhs_drvexit(void)
87117cdd29dSKeshava Munegowda {
87217cdd29dSKeshava Munegowda platform_driver_unregister(&usbhs_omap_driver);
87317cdd29dSKeshava Munegowda }
87417cdd29dSKeshava Munegowda module_exit(omap_usbhs_drvexit);
875