16b1baefeSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 217cdd29dSKeshava Munegowda /** 317cdd29dSKeshava Munegowda * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI 417cdd29dSKeshava Munegowda * 503a8f438SRoger Quadros * Copyright (C) 2011-2013 Texas Instruments Incorporated - http://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 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 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 12303a8f438SRoger Quadros /** 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 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 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 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 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 } 311340c64eaSRoger Quadros /* Fall through 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 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]); 347340c64eaSRoger Quadros /* Fall through 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 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 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 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 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. 5291e7fe1a9SKeshava Munegowda */ 530f791be49SBill Pemberton static int usbhs_omap_probe(struct platform_device *pdev) 53117cdd29dSKeshava Munegowda { 5321e7fe1a9SKeshava Munegowda struct device *dev = &pdev->dev; 533334a41ceSJingoo Han struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev); 5341e7fe1a9SKeshava Munegowda struct usbhs_hcd_omap *omap; 5351e7fe1a9SKeshava Munegowda struct resource *res; 5361e7fe1a9SKeshava Munegowda int ret = 0; 5371e7fe1a9SKeshava Munegowda int i; 53806ba7dc7SRoger Quadros bool need_logic_fck; 53917cdd29dSKeshava Munegowda 54003a8f438SRoger Quadros if (dev->of_node) { 54103a8f438SRoger Quadros /* For DT boot we populate platform data from OF node */ 54203a8f438SRoger Quadros pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 54303a8f438SRoger Quadros if (!pdata) 54403a8f438SRoger Quadros return -ENOMEM; 54503a8f438SRoger Quadros 54603a8f438SRoger Quadros ret = usbhs_omap_get_dt_pdata(dev, pdata); 54703a8f438SRoger Quadros if (ret) 54803a8f438SRoger Quadros return ret; 54903a8f438SRoger Quadros 55003a8f438SRoger Quadros dev->platform_data = pdata; 55103a8f438SRoger Quadros } 55203a8f438SRoger Quadros 5531e7fe1a9SKeshava Munegowda if (!pdata) { 5541e7fe1a9SKeshava Munegowda dev_err(dev, "Missing platform data\n"); 55527d4f2c6SRoger Quadros return -ENODEV; 55617cdd29dSKeshava Munegowda } 5571e7fe1a9SKeshava Munegowda 55803a8f438SRoger Quadros if (pdata->nports > OMAP3_HS_USB_PORTS) { 55903a8f438SRoger Quadros dev_info(dev, "Too many num_ports <%d> in platform_data. Max %d\n", 56003a8f438SRoger Quadros pdata->nports, OMAP3_HS_USB_PORTS); 56103a8f438SRoger Quadros return -ENODEV; 56203a8f438SRoger Quadros } 56303a8f438SRoger Quadros 56427d4f2c6SRoger Quadros omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); 5651e7fe1a9SKeshava Munegowda if (!omap) { 5661e7fe1a9SKeshava Munegowda dev_err(dev, "Memory allocation failed\n"); 56727d4f2c6SRoger Quadros return -ENOMEM; 56827d4f2c6SRoger Quadros } 56927d4f2c6SRoger Quadros 57003a8f438SRoger Quadros res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 571d011c450SSachin Kamat omap->uhh_base = devm_ioremap_resource(dev, res); 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 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 */ 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 = { 84317cdd29dSKeshava Munegowda .name = (char *)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_LICENSE("GPL v2"); 85517cdd29dSKeshava Munegowda MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI"); 85617cdd29dSKeshava Munegowda 85710492ee8STony Lindgren static int omap_usbhs_drvinit(void) 85817cdd29dSKeshava Munegowda { 85910492ee8STony Lindgren return platform_driver_register(&usbhs_omap_driver); 86017cdd29dSKeshava Munegowda } 86117cdd29dSKeshava Munegowda 86217cdd29dSKeshava Munegowda /* 86317cdd29dSKeshava Munegowda * init before ehci and ohci drivers; 86417cdd29dSKeshava Munegowda * The usbhs core driver should be initialized much before 86517cdd29dSKeshava Munegowda * the omap ehci and ohci probe functions are called. 8664dc2ccebSKeshava Munegowda * This usbhs core driver should be initialized after 8674dc2ccebSKeshava Munegowda * usb tll driver 86817cdd29dSKeshava Munegowda */ 8694dc2ccebSKeshava Munegowda fs_initcall_sync(omap_usbhs_drvinit); 87017cdd29dSKeshava Munegowda 87110492ee8STony Lindgren static void omap_usbhs_drvexit(void) 87217cdd29dSKeshava Munegowda { 87317cdd29dSKeshava Munegowda platform_driver_unregister(&usbhs_omap_driver); 87417cdd29dSKeshava Munegowda } 87517cdd29dSKeshava Munegowda module_exit(omap_usbhs_drvexit); 876