18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011-2013 Texas Instruments Incorporated - https://www.ti.com 68c2ecf20Sopenharmony_ci * Author: Keshava Munegowda <keshava_mgowda@ti.com> 78c2ecf20Sopenharmony_ci * Author: Roger Quadros <rogerq@ti.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 168c2ecf20Sopenharmony_ci#include <linux/gpio.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_data/usb-omap.h> 198c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "omap-usb.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define USBHS_DRIVER_NAME "usbhs_omap" 278c2ecf20Sopenharmony_ci#define OMAP_EHCI_DEVICE "ehci-omap" 288c2ecf20Sopenharmony_ci#define OMAP_OHCI_DEVICE "ohci-omap3" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* OMAP USBHOST Register addresses */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* UHH Register Set */ 338c2ecf20Sopenharmony_ci#define OMAP_UHH_REVISION (0x00) 348c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSCONFIG (0x10) 358c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12) 368c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8) 378c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3) 388c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2) 398c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1) 408c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define OMAP_UHH_SYSSTATUS (0x14) 438c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG (0x40) 448c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0) 458c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0) 468c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11) 478c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12) 488c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2) 498c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3) 508c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4) 518c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5) 528c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8) 538c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) 548c2ecf20Sopenharmony_ci#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) 558c2ecf20Sopenharmony_ci#define OMAP4_UHH_HOSTCONFIG_APP_START_CLK (1 << 31) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* OMAP4-specific defines */ 588c2ecf20Sopenharmony_ci#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2) 598c2ecf20Sopenharmony_ci#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2) 608c2ecf20Sopenharmony_ci#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4) 618c2ecf20Sopenharmony_ci#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4) 628c2ecf20Sopenharmony_ci#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0) 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define OMAP4_P1_MODE_CLEAR (3 << 16) 658c2ecf20Sopenharmony_ci#define OMAP4_P1_MODE_TLL (1 << 16) 668c2ecf20Sopenharmony_ci#define OMAP4_P1_MODE_HSIC (3 << 16) 678c2ecf20Sopenharmony_ci#define OMAP4_P2_MODE_CLEAR (3 << 18) 688c2ecf20Sopenharmony_ci#define OMAP4_P2_MODE_TLL (1 << 18) 698c2ecf20Sopenharmony_ci#define OMAP4_P2_MODE_HSIC (3 << 18) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define OMAP_UHH_DEBUG_CSR (0x44) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* Values of UHH_REVISION - Note: these are not given in the TRM */ 748c2ecf20Sopenharmony_ci#define OMAP_USBHS_REV1 0x00000010 /* OMAP3 */ 758c2ecf20Sopenharmony_ci#define OMAP_USBHS_REV2 0x50700100 /* OMAP4 */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define is_omap_usbhs_rev1(x) (x->usbhs_rev == OMAP_USBHS_REV1) 788c2ecf20Sopenharmony_ci#define is_omap_usbhs_rev2(x) (x->usbhs_rev == OMAP_USBHS_REV2) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define is_ehci_phy_mode(x) (x == OMAP_EHCI_PORT_MODE_PHY) 818c2ecf20Sopenharmony_ci#define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) 828c2ecf20Sopenharmony_ci#define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistruct usbhs_hcd_omap { 868c2ecf20Sopenharmony_ci int nports; 878c2ecf20Sopenharmony_ci struct clk **utmi_clk; 888c2ecf20Sopenharmony_ci struct clk **hsic60m_clk; 898c2ecf20Sopenharmony_ci struct clk **hsic480m_clk; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci struct clk *xclk60mhsp1_ck; 928c2ecf20Sopenharmony_ci struct clk *xclk60mhsp2_ck; 938c2ecf20Sopenharmony_ci struct clk *utmi_p1_gfclk; 948c2ecf20Sopenharmony_ci struct clk *utmi_p2_gfclk; 958c2ecf20Sopenharmony_ci struct clk *init_60m_fclk; 968c2ecf20Sopenharmony_ci struct clk *ehci_logic_fck; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci void __iomem *uhh_base; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci u32 usbhs_rev; 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const char usbhs_driver_name[] = USBHS_DRIVER_NAME; 1078c2ecf20Sopenharmony_cistatic u64 usbhs_dmamask = DMA_BIT_MASK(32); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic inline void usbhs_write(void __iomem *base, u32 reg, u32 val) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci writel_relaxed(val, base + reg); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic inline u32 usbhs_read(void __iomem *base, u32 reg) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci return readl_relaxed(base + reg); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/* 1248c2ecf20Sopenharmony_ci * Map 'enum usbhs_omap_port_mode' found in <linux/platform_data/usb-omap.h> 1258c2ecf20Sopenharmony_ci * to the device tree binding portN-mode found in 1268c2ecf20Sopenharmony_ci * 'Documentation/devicetree/bindings/mfd/omap-usb-host.txt' 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic const char * const port_modes[] = { 1298c2ecf20Sopenharmony_ci [OMAP_USBHS_PORT_MODE_UNUSED] = "", 1308c2ecf20Sopenharmony_ci [OMAP_EHCI_PORT_MODE_PHY] = "ehci-phy", 1318c2ecf20Sopenharmony_ci [OMAP_EHCI_PORT_MODE_TLL] = "ehci-tll", 1328c2ecf20Sopenharmony_ci [OMAP_EHCI_PORT_MODE_HSIC] = "ehci-hsic", 1338c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0] = "ohci-phy-6pin-datse0", 1348c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM] = "ohci-phy-6pin-dpdm", 1358c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0] = "ohci-phy-3pin-datse0", 1368c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM] = "ohci-phy-4pin-dpdm", 1378c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0] = "ohci-tll-6pin-datse0", 1388c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM] = "ohci-tll-6pin-dpdm", 1398c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0] = "ohci-tll-3pin-datse0", 1408c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM] = "ohci-tll-4pin-dpdm", 1418c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0] = "ohci-tll-2pin-datse0", 1428c2ecf20Sopenharmony_ci [OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM] = "ohci-tll-2pin-dpdm", 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic struct platform_device *omap_usbhs_alloc_child(const char *name, 1468c2ecf20Sopenharmony_ci struct resource *res, int num_resources, void *pdata, 1478c2ecf20Sopenharmony_ci size_t pdata_size, struct device *dev) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct platform_device *child; 1508c2ecf20Sopenharmony_ci int ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci child = platform_device_alloc(name, 0); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!child) { 1558c2ecf20Sopenharmony_ci dev_err(dev, "platform_device_alloc %s failed\n", name); 1568c2ecf20Sopenharmony_ci goto err_end; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = platform_device_add_resources(child, res, num_resources); 1608c2ecf20Sopenharmony_ci if (ret) { 1618c2ecf20Sopenharmony_ci dev_err(dev, "platform_device_add_resources failed\n"); 1628c2ecf20Sopenharmony_ci goto err_alloc; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = platform_device_add_data(child, pdata, pdata_size); 1668c2ecf20Sopenharmony_ci if (ret) { 1678c2ecf20Sopenharmony_ci dev_err(dev, "platform_device_add_data failed\n"); 1688c2ecf20Sopenharmony_ci goto err_alloc; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci child->dev.dma_mask = &usbhs_dmamask; 1728c2ecf20Sopenharmony_ci dma_set_coherent_mask(&child->dev, DMA_BIT_MASK(32)); 1738c2ecf20Sopenharmony_ci child->dev.parent = dev; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ret = platform_device_add(child); 1768c2ecf20Sopenharmony_ci if (ret) { 1778c2ecf20Sopenharmony_ci dev_err(dev, "platform_device_add failed\n"); 1788c2ecf20Sopenharmony_ci goto err_alloc; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return child; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cierr_alloc: 1848c2ecf20Sopenharmony_ci platform_device_put(child); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cierr_end: 1878c2ecf20Sopenharmony_ci return NULL; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int omap_usbhs_alloc_children(struct platform_device *pdev) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1938c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev); 1948c2ecf20Sopenharmony_ci struct platform_device *ehci; 1958c2ecf20Sopenharmony_ci struct platform_device *ohci; 1968c2ecf20Sopenharmony_ci struct resource *res; 1978c2ecf20Sopenharmony_ci struct resource resources[2]; 1988c2ecf20Sopenharmony_ci int ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci"); 2018c2ecf20Sopenharmony_ci if (!res) { 2028c2ecf20Sopenharmony_ci dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n"); 2038c2ecf20Sopenharmony_ci ret = -ENODEV; 2048c2ecf20Sopenharmony_ci goto err_end; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci resources[0] = *res; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ehci-irq"); 2098c2ecf20Sopenharmony_ci if (!res) { 2108c2ecf20Sopenharmony_ci dev_err(dev, " EHCI get resource IORESOURCE_IRQ failed\n"); 2118c2ecf20Sopenharmony_ci ret = -ENODEV; 2128c2ecf20Sopenharmony_ci goto err_end; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci resources[1] = *res; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, pdata, 2178c2ecf20Sopenharmony_ci sizeof(*pdata), dev); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (!ehci) { 2208c2ecf20Sopenharmony_ci dev_err(dev, "omap_usbhs_alloc_child failed\n"); 2218c2ecf20Sopenharmony_ci ret = -ENOMEM; 2228c2ecf20Sopenharmony_ci goto err_end; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci"); 2268c2ecf20Sopenharmony_ci if (!res) { 2278c2ecf20Sopenharmony_ci dev_err(dev, "OHCI get resource IORESOURCE_MEM failed\n"); 2288c2ecf20Sopenharmony_ci ret = -ENODEV; 2298c2ecf20Sopenharmony_ci goto err_ehci; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci resources[0] = *res; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ohci-irq"); 2348c2ecf20Sopenharmony_ci if (!res) { 2358c2ecf20Sopenharmony_ci dev_err(dev, "OHCI get resource IORESOURCE_IRQ failed\n"); 2368c2ecf20Sopenharmony_ci ret = -ENODEV; 2378c2ecf20Sopenharmony_ci goto err_ehci; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci resources[1] = *res; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, pdata, 2428c2ecf20Sopenharmony_ci sizeof(*pdata), dev); 2438c2ecf20Sopenharmony_ci if (!ohci) { 2448c2ecf20Sopenharmony_ci dev_err(dev, "omap_usbhs_alloc_child failed\n"); 2458c2ecf20Sopenharmony_ci ret = -ENOMEM; 2468c2ecf20Sopenharmony_ci goto err_ehci; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cierr_ehci: 2528c2ecf20Sopenharmony_ci platform_device_unregister(ehci); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cierr_end: 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic bool is_ohci_port(enum usbhs_omap_port_mode pmode) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci switch (pmode) { 2618c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0: 2628c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM: 2638c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0: 2648c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM: 2658c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0: 2668c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM: 2678c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0: 2688c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM: 2698c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0: 2708c2ecf20Sopenharmony_ci case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM: 2718c2ecf20Sopenharmony_ci return true; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci default: 2748c2ecf20Sopenharmony_ci return false; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int usbhs_runtime_resume(struct device *dev) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); 2818c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata = omap->pdata; 2828c2ecf20Sopenharmony_ci int i, r; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci dev_dbg(dev, "usbhs_runtime_resume\n"); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci omap_tll_enable(pdata); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (!IS_ERR(omap->ehci_logic_fck)) 2898c2ecf20Sopenharmony_ci clk_prepare_enable(omap->ehci_logic_fck); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 2928c2ecf20Sopenharmony_ci switch (pdata->port_mode[i]) { 2938c2ecf20Sopenharmony_ci case OMAP_EHCI_PORT_MODE_HSIC: 2948c2ecf20Sopenharmony_ci if (!IS_ERR(omap->hsic60m_clk[i])) { 2958c2ecf20Sopenharmony_ci r = clk_prepare_enable(omap->hsic60m_clk[i]); 2968c2ecf20Sopenharmony_ci if (r) { 2978c2ecf20Sopenharmony_ci dev_err(dev, 2988c2ecf20Sopenharmony_ci "Can't enable port %d hsic60m clk:%d\n", 2998c2ecf20Sopenharmony_ci i, r); 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (!IS_ERR(omap->hsic480m_clk[i])) { 3048c2ecf20Sopenharmony_ci r = clk_prepare_enable(omap->hsic480m_clk[i]); 3058c2ecf20Sopenharmony_ci if (r) { 3068c2ecf20Sopenharmony_ci dev_err(dev, 3078c2ecf20Sopenharmony_ci "Can't enable port %d hsic480m clk:%d\n", 3088c2ecf20Sopenharmony_ci i, r); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci fallthrough; /* as HSIC mode needs utmi_clk */ 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci case OMAP_EHCI_PORT_MODE_TLL: 3148c2ecf20Sopenharmony_ci if (!IS_ERR(omap->utmi_clk[i])) { 3158c2ecf20Sopenharmony_ci r = clk_prepare_enable(omap->utmi_clk[i]); 3168c2ecf20Sopenharmony_ci if (r) { 3178c2ecf20Sopenharmony_ci dev_err(dev, 3188c2ecf20Sopenharmony_ci "Can't enable port %d clk : %d\n", 3198c2ecf20Sopenharmony_ci i, r); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci default: 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int usbhs_runtime_suspend(struct device *dev) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); 3348c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata = omap->pdata; 3358c2ecf20Sopenharmony_ci int i; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci dev_dbg(dev, "usbhs_runtime_suspend\n"); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 3408c2ecf20Sopenharmony_ci switch (pdata->port_mode[i]) { 3418c2ecf20Sopenharmony_ci case OMAP_EHCI_PORT_MODE_HSIC: 3428c2ecf20Sopenharmony_ci if (!IS_ERR(omap->hsic60m_clk[i])) 3438c2ecf20Sopenharmony_ci clk_disable_unprepare(omap->hsic60m_clk[i]); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (!IS_ERR(omap->hsic480m_clk[i])) 3468c2ecf20Sopenharmony_ci clk_disable_unprepare(omap->hsic480m_clk[i]); 3478c2ecf20Sopenharmony_ci fallthrough; /* as utmi_clks were used in HSIC mode */ 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci case OMAP_EHCI_PORT_MODE_TLL: 3508c2ecf20Sopenharmony_ci if (!IS_ERR(omap->utmi_clk[i])) 3518c2ecf20Sopenharmony_ci clk_disable_unprepare(omap->utmi_clk[i]); 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci default: 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!IS_ERR(omap->ehci_logic_fck)) 3598c2ecf20Sopenharmony_ci clk_disable_unprepare(omap->ehci_logic_fck); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci omap_tll_disable(pdata); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic unsigned omap_usbhs_rev1_hostconfig(struct usbhs_hcd_omap *omap, 3678c2ecf20Sopenharmony_ci unsigned reg) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata = omap->pdata; 3708c2ecf20Sopenharmony_ci int i; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 3738c2ecf20Sopenharmony_ci switch (pdata->port_mode[i]) { 3748c2ecf20Sopenharmony_ci case OMAP_USBHS_PORT_MODE_UNUSED: 3758c2ecf20Sopenharmony_ci reg &= ~(OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS << i); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case OMAP_EHCI_PORT_MODE_PHY: 3788c2ecf20Sopenharmony_ci if (pdata->single_ulpi_bypass) 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (i == 0) 3828c2ecf20Sopenharmony_ci reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; 3838c2ecf20Sopenharmony_ci else 3848c2ecf20Sopenharmony_ci reg &= ~(OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS 3858c2ecf20Sopenharmony_ci << (i-1)); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci default: 3888c2ecf20Sopenharmony_ci if (pdata->single_ulpi_bypass) 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (i == 0) 3928c2ecf20Sopenharmony_ci reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; 3938c2ecf20Sopenharmony_ci else 3948c2ecf20Sopenharmony_ci reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS 3958c2ecf20Sopenharmony_ci << (i-1); 3968c2ecf20Sopenharmony_ci break; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (pdata->single_ulpi_bypass) { 4018c2ecf20Sopenharmony_ci /* bypass ULPI only if none of the ports use PHY mode */ 4028c2ecf20Sopenharmony_ci reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 4058c2ecf20Sopenharmony_ci if (is_ehci_phy_mode(pdata->port_mode[i])) { 4068c2ecf20Sopenharmony_ci reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return reg; 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap, 4168c2ecf20Sopenharmony_ci unsigned reg) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata = omap->pdata; 4198c2ecf20Sopenharmony_ci int i; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 4228c2ecf20Sopenharmony_ci /* Clear port mode fields for PHY mode */ 4238c2ecf20Sopenharmony_ci reg &= ~(OMAP4_P1_MODE_CLEAR << 2 * i); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (is_ehci_tll_mode(pdata->port_mode[i]) || 4268c2ecf20Sopenharmony_ci (is_ohci_port(pdata->port_mode[i]))) 4278c2ecf20Sopenharmony_ci reg |= OMAP4_P1_MODE_TLL << 2 * i; 4288c2ecf20Sopenharmony_ci else if (is_ehci_hsic_mode(pdata->port_mode[i])) 4298c2ecf20Sopenharmony_ci reg |= OMAP4_P1_MODE_HSIC << 2 * i; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return reg; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic void omap_usbhs_init(struct device *dev) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); 4388c2ecf20Sopenharmony_ci unsigned reg; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci dev_dbg(dev, "starting TI HSUSB Controller\n"); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); 4458c2ecf20Sopenharmony_ci /* setup ULPI bypass and burst configurations */ 4468c2ecf20Sopenharmony_ci reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN 4478c2ecf20Sopenharmony_ci | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN 4488c2ecf20Sopenharmony_ci | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); 4498c2ecf20Sopenharmony_ci reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK; 4508c2ecf20Sopenharmony_ci reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci switch (omap->usbhs_rev) { 4538c2ecf20Sopenharmony_ci case OMAP_USBHS_REV1: 4548c2ecf20Sopenharmony_ci reg = omap_usbhs_rev1_hostconfig(omap, reg); 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci case OMAP_USBHS_REV2: 4588c2ecf20Sopenharmony_ci reg = omap_usbhs_rev2_hostconfig(omap, reg); 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci default: /* newer revisions */ 4628c2ecf20Sopenharmony_ci reg = omap_usbhs_rev2_hostconfig(omap, reg); 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); 4678c2ecf20Sopenharmony_ci dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int usbhs_omap_get_dt_pdata(struct device *dev, 4738c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci int ret, i; 4768c2ecf20Sopenharmony_ci struct device_node *node = dev->of_node; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "num-ports", &pdata->nports); 4798c2ecf20Sopenharmony_ci if (ret) 4808c2ecf20Sopenharmony_ci pdata->nports = 0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (pdata->nports > OMAP3_HS_USB_PORTS) { 4838c2ecf20Sopenharmony_ci dev_warn(dev, "Too many num_ports <%d> in device tree. Max %d\n", 4848c2ecf20Sopenharmony_ci pdata->nports, OMAP3_HS_USB_PORTS); 4858c2ecf20Sopenharmony_ci return -ENODEV; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* get port modes */ 4898c2ecf20Sopenharmony_ci for (i = 0; i < OMAP3_HS_USB_PORTS; i++) { 4908c2ecf20Sopenharmony_ci char prop[11]; 4918c2ecf20Sopenharmony_ci const char *mode; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci pdata->port_mode[i] = OMAP_USBHS_PORT_MODE_UNUSED; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci snprintf(prop, sizeof(prop), "port%d-mode", i + 1); 4968c2ecf20Sopenharmony_ci ret = of_property_read_string(node, prop, &mode); 4978c2ecf20Sopenharmony_ci if (ret < 0) 4988c2ecf20Sopenharmony_ci continue; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* get 'enum usbhs_omap_port_mode' from port mode string */ 5018c2ecf20Sopenharmony_ci ret = match_string(port_modes, ARRAY_SIZE(port_modes), mode); 5028c2ecf20Sopenharmony_ci if (ret < 0) { 5038c2ecf20Sopenharmony_ci dev_warn(dev, "Invalid port%d-mode \"%s\" in device tree\n", 5048c2ecf20Sopenharmony_ci i, mode); 5058c2ecf20Sopenharmony_ci return -ENODEV; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci dev_dbg(dev, "port%d-mode: %s -> %d\n", i, mode, ret); 5098c2ecf20Sopenharmony_ci pdata->port_mode[i] = ret; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* get flags */ 5138c2ecf20Sopenharmony_ci pdata->single_ulpi_bypass = of_property_read_bool(node, 5148c2ecf20Sopenharmony_ci "single-ulpi-bypass"); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct of_device_id usbhs_child_match_table[] = { 5208c2ecf20Sopenharmony_ci { .compatible = "ti,ehci-omap", }, 5218c2ecf20Sopenharmony_ci { .compatible = "ti,ohci-omap3", }, 5228c2ecf20Sopenharmony_ci { } 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci/** 5268c2ecf20Sopenharmony_ci * usbhs_omap_probe - initialize TI-based HCDs 5278c2ecf20Sopenharmony_ci * 5288c2ecf20Sopenharmony_ci * Allocates basic resources for this USB host controller. 5298c2ecf20Sopenharmony_ci * 5308c2ecf20Sopenharmony_ci * @pdev: Pointer to this device's platform device structure 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_cistatic int usbhs_omap_probe(struct platform_device *pdev) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 5358c2ecf20Sopenharmony_ci struct usbhs_omap_platform_data *pdata = dev_get_platdata(dev); 5368c2ecf20Sopenharmony_ci struct usbhs_hcd_omap *omap; 5378c2ecf20Sopenharmony_ci struct resource *res; 5388c2ecf20Sopenharmony_ci int ret = 0; 5398c2ecf20Sopenharmony_ci int i; 5408c2ecf20Sopenharmony_ci bool need_logic_fck; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (dev->of_node) { 5438c2ecf20Sopenharmony_ci /* For DT boot we populate platform data from OF node */ 5448c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 5458c2ecf20Sopenharmony_ci if (!pdata) 5468c2ecf20Sopenharmony_ci return -ENOMEM; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci ret = usbhs_omap_get_dt_pdata(dev, pdata); 5498c2ecf20Sopenharmony_ci if (ret) 5508c2ecf20Sopenharmony_ci return ret; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci dev->platform_data = pdata; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!pdata) { 5568c2ecf20Sopenharmony_ci dev_err(dev, "Missing platform data\n"); 5578c2ecf20Sopenharmony_ci return -ENODEV; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (pdata->nports > OMAP3_HS_USB_PORTS) { 5618c2ecf20Sopenharmony_ci dev_info(dev, "Too many num_ports <%d> in platform_data. Max %d\n", 5628c2ecf20Sopenharmony_ci pdata->nports, OMAP3_HS_USB_PORTS); 5638c2ecf20Sopenharmony_ci return -ENODEV; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); 5678c2ecf20Sopenharmony_ci if (!omap) { 5688c2ecf20Sopenharmony_ci dev_err(dev, "Memory allocation failed\n"); 5698c2ecf20Sopenharmony_ci return -ENOMEM; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5738c2ecf20Sopenharmony_ci omap->uhh_base = devm_ioremap_resource(dev, res); 5748c2ecf20Sopenharmony_ci if (IS_ERR(omap->uhh_base)) 5758c2ecf20Sopenharmony_ci return PTR_ERR(omap->uhh_base); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci omap->pdata = pdata; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* Initialize the TLL subsystem */ 5808c2ecf20Sopenharmony_ci omap_tll_init(pdata); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, omap); 5858c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci /* we need to call runtime suspend before we update omap->nports 5908c2ecf20Sopenharmony_ci * to prevent unbalanced clk_disable() 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* 5958c2ecf20Sopenharmony_ci * If platform data contains nports then use that 5968c2ecf20Sopenharmony_ci * else make out number of ports from USBHS revision 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_ci if (pdata->nports) { 5998c2ecf20Sopenharmony_ci omap->nports = pdata->nports; 6008c2ecf20Sopenharmony_ci } else { 6018c2ecf20Sopenharmony_ci switch (omap->usbhs_rev) { 6028c2ecf20Sopenharmony_ci case OMAP_USBHS_REV1: 6038c2ecf20Sopenharmony_ci omap->nports = 3; 6048c2ecf20Sopenharmony_ci break; 6058c2ecf20Sopenharmony_ci case OMAP_USBHS_REV2: 6068c2ecf20Sopenharmony_ci omap->nports = 2; 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci default: 6098c2ecf20Sopenharmony_ci omap->nports = OMAP3_HS_USB_PORTS; 6108c2ecf20Sopenharmony_ci dev_dbg(dev, 6118c2ecf20Sopenharmony_ci "USB HOST Rev:0x%x not recognized, assuming %d ports\n", 6128c2ecf20Sopenharmony_ci omap->usbhs_rev, omap->nports); 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci pdata->nports = omap->nports; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci i = sizeof(struct clk *) * omap->nports; 6198c2ecf20Sopenharmony_ci omap->utmi_clk = devm_kzalloc(dev, i, GFP_KERNEL); 6208c2ecf20Sopenharmony_ci omap->hsic480m_clk = devm_kzalloc(dev, i, GFP_KERNEL); 6218c2ecf20Sopenharmony_ci omap->hsic60m_clk = devm_kzalloc(dev, i, GFP_KERNEL); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (!omap->utmi_clk || !omap->hsic480m_clk || !omap->hsic60m_clk) { 6248c2ecf20Sopenharmony_ci dev_err(dev, "Memory allocation failed\n"); 6258c2ecf20Sopenharmony_ci ret = -ENOMEM; 6268c2ecf20Sopenharmony_ci goto err_mem; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* Set all clocks as invalid to begin with */ 6308c2ecf20Sopenharmony_ci omap->ehci_logic_fck = ERR_PTR(-ENODEV); 6318c2ecf20Sopenharmony_ci omap->init_60m_fclk = ERR_PTR(-ENODEV); 6328c2ecf20Sopenharmony_ci omap->utmi_p1_gfclk = ERR_PTR(-ENODEV); 6338c2ecf20Sopenharmony_ci omap->utmi_p2_gfclk = ERR_PTR(-ENODEV); 6348c2ecf20Sopenharmony_ci omap->xclk60mhsp1_ck = ERR_PTR(-ENODEV); 6358c2ecf20Sopenharmony_ci omap->xclk60mhsp2_ck = ERR_PTR(-ENODEV); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 6388c2ecf20Sopenharmony_ci omap->utmi_clk[i] = ERR_PTR(-ENODEV); 6398c2ecf20Sopenharmony_ci omap->hsic480m_clk[i] = ERR_PTR(-ENODEV); 6408c2ecf20Sopenharmony_ci omap->hsic60m_clk[i] = ERR_PTR(-ENODEV); 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* for OMAP3 i.e. USBHS REV1 */ 6448c2ecf20Sopenharmony_ci if (omap->usbhs_rev == OMAP_USBHS_REV1) { 6458c2ecf20Sopenharmony_ci need_logic_fck = false; 6468c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 6478c2ecf20Sopenharmony_ci if (is_ehci_phy_mode(pdata->port_mode[i]) || 6488c2ecf20Sopenharmony_ci is_ehci_tll_mode(pdata->port_mode[i]) || 6498c2ecf20Sopenharmony_ci is_ehci_hsic_mode(pdata->port_mode[i])) 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci need_logic_fck |= true; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (need_logic_fck) { 6558c2ecf20Sopenharmony_ci omap->ehci_logic_fck = devm_clk_get(dev, 6568c2ecf20Sopenharmony_ci "usbhost_120m_fck"); 6578c2ecf20Sopenharmony_ci if (IS_ERR(omap->ehci_logic_fck)) { 6588c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->ehci_logic_fck); 6598c2ecf20Sopenharmony_ci dev_err(dev, "usbhost_120m_fck failed:%d\n", 6608c2ecf20Sopenharmony_ci ret); 6618c2ecf20Sopenharmony_ci goto err_mem; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci goto initialize; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* for OMAP4+ i.e. USBHS REV2+ */ 6688c2ecf20Sopenharmony_ci omap->utmi_p1_gfclk = devm_clk_get(dev, "utmi_p1_gfclk"); 6698c2ecf20Sopenharmony_ci if (IS_ERR(omap->utmi_p1_gfclk)) { 6708c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->utmi_p1_gfclk); 6718c2ecf20Sopenharmony_ci dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); 6728c2ecf20Sopenharmony_ci goto err_mem; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci omap->utmi_p2_gfclk = devm_clk_get(dev, "utmi_p2_gfclk"); 6768c2ecf20Sopenharmony_ci if (IS_ERR(omap->utmi_p2_gfclk)) { 6778c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->utmi_p2_gfclk); 6788c2ecf20Sopenharmony_ci dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); 6798c2ecf20Sopenharmony_ci goto err_mem; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci omap->xclk60mhsp1_ck = devm_clk_get(dev, "refclk_60m_ext_p1"); 6838c2ecf20Sopenharmony_ci if (IS_ERR(omap->xclk60mhsp1_ck)) { 6848c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->xclk60mhsp1_ck); 6858c2ecf20Sopenharmony_ci dev_err(dev, "refclk_60m_ext_p1 failed error:%d\n", ret); 6868c2ecf20Sopenharmony_ci goto err_mem; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci omap->xclk60mhsp2_ck = devm_clk_get(dev, "refclk_60m_ext_p2"); 6908c2ecf20Sopenharmony_ci if (IS_ERR(omap->xclk60mhsp2_ck)) { 6918c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->xclk60mhsp2_ck); 6928c2ecf20Sopenharmony_ci dev_err(dev, "refclk_60m_ext_p2 failed error:%d\n", ret); 6938c2ecf20Sopenharmony_ci goto err_mem; 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci omap->init_60m_fclk = devm_clk_get(dev, "refclk_60m_int"); 6978c2ecf20Sopenharmony_ci if (IS_ERR(omap->init_60m_fclk)) { 6988c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->init_60m_fclk); 6998c2ecf20Sopenharmony_ci dev_err(dev, "refclk_60m_int failed error:%d\n", ret); 7008c2ecf20Sopenharmony_ci goto err_mem; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci for (i = 0; i < omap->nports; i++) { 7048c2ecf20Sopenharmony_ci char clkname[30]; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* clock names are indexed from 1*/ 7078c2ecf20Sopenharmony_ci snprintf(clkname, sizeof(clkname), 7088c2ecf20Sopenharmony_ci "usb_host_hs_utmi_p%d_clk", i + 1); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* If a clock is not found we won't bail out as not all 7118c2ecf20Sopenharmony_ci * platforms have all clocks and we can function without 7128c2ecf20Sopenharmony_ci * them 7138c2ecf20Sopenharmony_ci */ 7148c2ecf20Sopenharmony_ci omap->utmi_clk[i] = devm_clk_get(dev, clkname); 7158c2ecf20Sopenharmony_ci if (IS_ERR(omap->utmi_clk[i])) { 7168c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->utmi_clk[i]); 7178c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get clock : %s : %d\n", 7188c2ecf20Sopenharmony_ci clkname, ret); 7198c2ecf20Sopenharmony_ci goto err_mem; 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci snprintf(clkname, sizeof(clkname), 7238c2ecf20Sopenharmony_ci "usb_host_hs_hsic480m_p%d_clk", i + 1); 7248c2ecf20Sopenharmony_ci omap->hsic480m_clk[i] = devm_clk_get(dev, clkname); 7258c2ecf20Sopenharmony_ci if (IS_ERR(omap->hsic480m_clk[i])) { 7268c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->hsic480m_clk[i]); 7278c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get clock : %s : %d\n", 7288c2ecf20Sopenharmony_ci clkname, ret); 7298c2ecf20Sopenharmony_ci goto err_mem; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci snprintf(clkname, sizeof(clkname), 7338c2ecf20Sopenharmony_ci "usb_host_hs_hsic60m_p%d_clk", i + 1); 7348c2ecf20Sopenharmony_ci omap->hsic60m_clk[i] = devm_clk_get(dev, clkname); 7358c2ecf20Sopenharmony_ci if (IS_ERR(omap->hsic60m_clk[i])) { 7368c2ecf20Sopenharmony_ci ret = PTR_ERR(omap->hsic60m_clk[i]); 7378c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get clock : %s : %d\n", 7388c2ecf20Sopenharmony_ci clkname, ret); 7398c2ecf20Sopenharmony_ci goto err_mem; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (is_ehci_phy_mode(pdata->port_mode[0])) { 7448c2ecf20Sopenharmony_ci ret = clk_set_parent(omap->utmi_p1_gfclk, 7458c2ecf20Sopenharmony_ci omap->xclk60mhsp1_ck); 7468c2ecf20Sopenharmony_ci if (ret != 0) { 7478c2ecf20Sopenharmony_ci dev_err(dev, "xclk60mhsp1_ck set parent failed: %d\n", 7488c2ecf20Sopenharmony_ci ret); 7498c2ecf20Sopenharmony_ci goto err_mem; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci } else if (is_ehci_tll_mode(pdata->port_mode[0])) { 7528c2ecf20Sopenharmony_ci ret = clk_set_parent(omap->utmi_p1_gfclk, 7538c2ecf20Sopenharmony_ci omap->init_60m_fclk); 7548c2ecf20Sopenharmony_ci if (ret != 0) { 7558c2ecf20Sopenharmony_ci dev_err(dev, "P0 init_60m_fclk set parent failed: %d\n", 7568c2ecf20Sopenharmony_ci ret); 7578c2ecf20Sopenharmony_ci goto err_mem; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (is_ehci_phy_mode(pdata->port_mode[1])) { 7628c2ecf20Sopenharmony_ci ret = clk_set_parent(omap->utmi_p2_gfclk, 7638c2ecf20Sopenharmony_ci omap->xclk60mhsp2_ck); 7648c2ecf20Sopenharmony_ci if (ret != 0) { 7658c2ecf20Sopenharmony_ci dev_err(dev, "xclk60mhsp2_ck set parent failed: %d\n", 7668c2ecf20Sopenharmony_ci ret); 7678c2ecf20Sopenharmony_ci goto err_mem; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci } else if (is_ehci_tll_mode(pdata->port_mode[1])) { 7708c2ecf20Sopenharmony_ci ret = clk_set_parent(omap->utmi_p2_gfclk, 7718c2ecf20Sopenharmony_ci omap->init_60m_fclk); 7728c2ecf20Sopenharmony_ci if (ret != 0) { 7738c2ecf20Sopenharmony_ci dev_err(dev, "P1 init_60m_fclk set parent failed: %d\n", 7748c2ecf20Sopenharmony_ci ret); 7758c2ecf20Sopenharmony_ci goto err_mem; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ciinitialize: 7808c2ecf20Sopenharmony_ci omap_usbhs_init(dev); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (dev->of_node) { 7838c2ecf20Sopenharmony_ci ret = of_platform_populate(dev->of_node, 7848c2ecf20Sopenharmony_ci usbhs_child_match_table, NULL, dev); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (ret) { 7878c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create DT children: %d\n", ret); 7888c2ecf20Sopenharmony_ci goto err_mem; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci } else { 7928c2ecf20Sopenharmony_ci ret = omap_usbhs_alloc_children(pdev); 7938c2ecf20Sopenharmony_ci if (ret) { 7948c2ecf20Sopenharmony_ci dev_err(dev, "omap_usbhs_alloc_children failed: %d\n", 7958c2ecf20Sopenharmony_ci ret); 7968c2ecf20Sopenharmony_ci goto err_mem; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cierr_mem: 8038c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return ret; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic int usbhs_omap_remove_child(struct device *dev, void *data) 8098c2ecf20Sopenharmony_ci{ 8108c2ecf20Sopenharmony_ci dev_info(dev, "unregistering\n"); 8118c2ecf20Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 8128c2ecf20Sopenharmony_ci return 0; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/** 8168c2ecf20Sopenharmony_ci * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs 8178c2ecf20Sopenharmony_ci * @pdev: USB Host Controller being removed 8188c2ecf20Sopenharmony_ci * 8198c2ecf20Sopenharmony_ci * Reverses the effect of usbhs_omap_probe(). 8208c2ecf20Sopenharmony_ci */ 8218c2ecf20Sopenharmony_cistatic int usbhs_omap_remove(struct platform_device *pdev) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* remove children */ 8268c2ecf20Sopenharmony_ci device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child); 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic const struct dev_pm_ops usbhsomap_dev_pm_ops = { 8318c2ecf20Sopenharmony_ci .runtime_suspend = usbhs_runtime_suspend, 8328c2ecf20Sopenharmony_ci .runtime_resume = usbhs_runtime_resume, 8338c2ecf20Sopenharmony_ci}; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic const struct of_device_id usbhs_omap_dt_ids[] = { 8368c2ecf20Sopenharmony_ci { .compatible = "ti,usbhs-host" }, 8378c2ecf20Sopenharmony_ci { } 8388c2ecf20Sopenharmony_ci}; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, usbhs_omap_dt_ids); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic struct platform_driver usbhs_omap_driver = { 8448c2ecf20Sopenharmony_ci .driver = { 8458c2ecf20Sopenharmony_ci .name = usbhs_driver_name, 8468c2ecf20Sopenharmony_ci .pm = &usbhsomap_dev_pm_ops, 8478c2ecf20Sopenharmony_ci .of_match_table = usbhs_omap_dt_ids, 8488c2ecf20Sopenharmony_ci }, 8498c2ecf20Sopenharmony_ci .probe = usbhs_omap_probe, 8508c2ecf20Sopenharmony_ci .remove = usbhs_omap_remove, 8518c2ecf20Sopenharmony_ci}; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); 8548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); 8558c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" USBHS_DRIVER_NAME); 8568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 8578c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI"); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic int omap_usbhs_drvinit(void) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci return platform_driver_register(&usbhs_omap_driver); 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci/* 8658c2ecf20Sopenharmony_ci * init before ehci and ohci drivers; 8668c2ecf20Sopenharmony_ci * The usbhs core driver should be initialized much before 8678c2ecf20Sopenharmony_ci * the omap ehci and ohci probe functions are called. 8688c2ecf20Sopenharmony_ci * This usbhs core driver should be initialized after 8698c2ecf20Sopenharmony_ci * usb tll driver 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_cifs_initcall_sync(omap_usbhs_drvinit); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic void omap_usbhs_drvexit(void) 8748c2ecf20Sopenharmony_ci{ 8758c2ecf20Sopenharmony_ci platform_driver_unregister(&usbhs_omap_driver); 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_cimodule_exit(omap_usbhs_drvexit); 878