1 /* 2 * ehci-omap.c - driver for USBHOST on OMAP3/4 processors 3 * 4 * Bus Glue for the EHCI controllers in OMAP3/4 5 * Tested on several OMAP3 boards, and OMAP4 Pandaboard 6 * 7 * Copyright (C) 2007-2011 Texas Instruments, Inc. 8 * Author: Vikram Pandita <vikram.pandita@ti.com> 9 * Author: Anand Gadiyar <gadiyar@ti.com> 10 * Author: Keshava Munegowda <keshava_mgowda@ti.com> 11 * 12 * Copyright (C) 2009 Nokia Corporation 13 * Contact: Felipe Balbi <felipe.balbi@nokia.com> 14 * 15 * Based on "ehci-fsl.c" and "ehci-au1xxx.c" ehci glue layers 16 * 17 * This program is free software; you can redistribute it and/or modify 18 * it under the terms of the GNU General Public License as published by 19 * the Free Software Foundation; either version 2 of the License, or 20 * (at your option) any later version. 21 * 22 * This program is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * GNU General Public License for more details. 26 * 27 * You should have received a copy of the GNU General Public License 28 * along with this program; if not, write to the Free Software 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 30 * 31 * TODO (last updated Feb 27, 2010): 32 * - add kernel-doc 33 * - enable AUTOIDLE 34 * - add suspend/resume 35 * - add HSIC and TLL support 36 * - convert to use hwmod and runtime PM 37 */ 38 39 #include <linux/platform_device.h> 40 #include <linux/slab.h> 41 #include <linux/usb/ulpi.h> 42 #include <plat/usb.h> 43 44 /* EHCI Register Set */ 45 #define EHCI_INSNREG04 (0xA0) 46 #define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5) 47 #define EHCI_INSNREG05_ULPI (0xA4) 48 #define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31 49 #define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24 50 #define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22 51 #define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16 52 #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 53 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 54 55 /*-------------------------------------------------------------------------*/ 56 57 static const struct hc_driver ehci_omap_hc_driver; 58 59 60 static inline void ehci_write(void __iomem *base, u32 reg, u32 val) 61 { 62 __raw_writel(val, base + reg); 63 } 64 65 static inline u32 ehci_read(void __iomem *base, u32 reg) 66 { 67 return __raw_readl(base + reg); 68 } 69 70 static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) 71 { 72 struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); 73 unsigned long timeout = jiffies + msecs_to_jiffies(1000); 74 unsigned reg = 0; 75 76 reg = ULPI_FUNC_CTRL_RESET 77 /* FUNCTION_CTRL_SET register */ 78 | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT) 79 /* Write */ 80 | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) 81 /* PORTn */ 82 | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) 83 /* start ULPI access*/ 84 | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT); 85 86 ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg); 87 88 /* Wait for ULPI access completion */ 89 while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI) 90 & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) { 91 cpu_relax(); 92 93 if (time_after(jiffies, timeout)) { 94 dev_dbg(&pdev->dev, "phy reset operation timed out\n"); 95 break; 96 } 97 } 98 } 99 100 101 /* configure so an HC device and id are always provided */ 102 /* always called with process context; sleeping is OK */ 103 104 /** 105 * ehci_hcd_omap_probe - initialize TI-based HCDs 106 * 107 * Allocates basic resources for this USB host controller, and 108 * then invokes the start() method for the HCD associated with it 109 * through the hotplug entry's driver_data. 110 */ 111 static int ehci_hcd_omap_probe(struct platform_device *pdev) 112 { 113 struct device *dev = &pdev->dev; 114 struct ehci_hcd_omap_platform_data *pdata = dev->platform_data; 115 struct resource *res; 116 struct usb_hcd *hcd; 117 void __iomem *regs; 118 struct ehci_hcd *omap_ehci; 119 int ret = -ENODEV; 120 int irq; 121 122 if (usb_disabled()) 123 return -ENODEV; 124 125 if (!dev->parent) { 126 dev_err(dev, "Missing parent device\n"); 127 return -ENODEV; 128 } 129 130 irq = platform_get_irq_byname(pdev, "ehci-irq"); 131 if (irq < 0) { 132 dev_err(dev, "EHCI irq failed\n"); 133 return -ENODEV; 134 } 135 136 res = platform_get_resource_byname(pdev, 137 IORESOURCE_MEM, "ehci"); 138 if (!res) { 139 dev_err(dev, "UHH EHCI get resource failed\n"); 140 return -ENODEV; 141 } 142 143 regs = ioremap(res->start, resource_size(res)); 144 if (!regs) { 145 dev_err(dev, "UHH EHCI ioremap failed\n"); 146 return -ENOMEM; 147 } 148 149 hcd = usb_create_hcd(&ehci_omap_hc_driver, dev, 150 dev_name(dev)); 151 if (!hcd) { 152 dev_err(dev, "failed to create hcd with err %d\n", ret); 153 ret = -ENOMEM; 154 goto err_io; 155 } 156 157 hcd->rsrc_start = res->start; 158 hcd->rsrc_len = resource_size(res); 159 hcd->regs = regs; 160 161 ret = omap_usbhs_enable(dev); 162 if (ret) { 163 dev_err(dev, "failed to start usbhs with err %d\n", ret); 164 goto err_enable; 165 } 166 167 /* 168 * An undocumented "feature" in the OMAP3 EHCI controller, 169 * causes suspended ports to be taken out of suspend when 170 * the USBCMD.Run/Stop bit is cleared (for example when 171 * we do ehci_bus_suspend). 172 * This breaks suspend-resume if the root-hub is allowed 173 * to suspend. Writing 1 to this undocumented register bit 174 * disables this feature and restores normal behavior. 175 */ 176 ehci_write(regs, EHCI_INSNREG04, 177 EHCI_INSNREG04_DISABLE_UNSUSPEND); 178 179 /* Soft reset the PHY using PHY reset command over ULPI */ 180 if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) 181 omap_ehci_soft_phy_reset(pdev, 0); 182 if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) 183 omap_ehci_soft_phy_reset(pdev, 1); 184 185 omap_ehci = hcd_to_ehci(hcd); 186 omap_ehci->sbrn = 0x20; 187 188 /* we know this is the memory we want, no need to ioremap again */ 189 omap_ehci->caps = hcd->regs; 190 omap_ehci->regs = hcd->regs 191 + HC_LENGTH(readl(&omap_ehci->caps->hc_capbase)); 192 193 dbg_hcs_params(omap_ehci, "reset"); 194 dbg_hcc_params(omap_ehci, "reset"); 195 196 /* cache this readonly data; minimize chip reads */ 197 omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); 198 199 ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); 200 if (ret) { 201 dev_err(dev, "failed to add hcd with err %d\n", ret); 202 goto err_add_hcd; 203 } 204 205 /* root ports should always stay powered */ 206 ehci_port_power(omap_ehci, 1); 207 208 return 0; 209 210 err_add_hcd: 211 omap_usbhs_disable(dev); 212 213 err_enable: 214 usb_put_hcd(hcd); 215 216 err_io: 217 return ret; 218 } 219 220 221 /** 222 * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs 223 * @pdev: USB Host Controller being removed 224 * 225 * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking 226 * the HCD's stop() method. It is always called from a thread 227 * context, normally "rmmod", "apmd", or something similar. 228 */ 229 static int ehci_hcd_omap_remove(struct platform_device *pdev) 230 { 231 struct device *dev = &pdev->dev; 232 struct usb_hcd *hcd = dev_get_drvdata(dev); 233 234 usb_remove_hcd(hcd); 235 omap_usbhs_disable(dev); 236 usb_put_hcd(hcd); 237 return 0; 238 } 239 240 static void ehci_hcd_omap_shutdown(struct platform_device *pdev) 241 { 242 struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); 243 244 if (hcd->driver->shutdown) 245 hcd->driver->shutdown(hcd); 246 } 247 248 static struct platform_driver ehci_hcd_omap_driver = { 249 .probe = ehci_hcd_omap_probe, 250 .remove = ehci_hcd_omap_remove, 251 .shutdown = ehci_hcd_omap_shutdown, 252 /*.suspend = ehci_hcd_omap_suspend, */ 253 /*.resume = ehci_hcd_omap_resume, */ 254 .driver = { 255 .name = "ehci-omap", 256 } 257 }; 258 259 /*-------------------------------------------------------------------------*/ 260 261 static const struct hc_driver ehci_omap_hc_driver = { 262 .description = hcd_name, 263 .product_desc = "OMAP-EHCI Host Controller", 264 .hcd_priv_size = sizeof(struct ehci_hcd), 265 266 /* 267 * generic hardware linkage 268 */ 269 .irq = ehci_irq, 270 .flags = HCD_MEMORY | HCD_USB2, 271 272 /* 273 * basic lifecycle operations 274 */ 275 .reset = ehci_init, 276 .start = ehci_run, 277 .stop = ehci_stop, 278 .shutdown = ehci_shutdown, 279 280 /* 281 * managing i/o requests and associated device resources 282 */ 283 .urb_enqueue = ehci_urb_enqueue, 284 .urb_dequeue = ehci_urb_dequeue, 285 .endpoint_disable = ehci_endpoint_disable, 286 .endpoint_reset = ehci_endpoint_reset, 287 288 /* 289 * scheduling support 290 */ 291 .get_frame_number = ehci_get_frame, 292 293 /* 294 * root hub support 295 */ 296 .hub_status_data = ehci_hub_status_data, 297 .hub_control = ehci_hub_control, 298 .bus_suspend = ehci_bus_suspend, 299 .bus_resume = ehci_bus_resume, 300 301 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 302 }; 303 304 MODULE_ALIAS("platform:omap-ehci"); 305 MODULE_AUTHOR("Texas Instruments, Inc."); 306 MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>"); 307 308