xref: /openbmc/linux/drivers/mfd/omap-usb-host.c (revision 390a3549)
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