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 #include <linux/regulator/consumer.h> 44 #include <linux/pm_runtime.h> 45 46 /* EHCI Register Set */ 47 #define EHCI_INSNREG04 (0xA0) 48 #define EHCI_INSNREG04_DISABLE_UNSUSPEND (1 << 5) 49 #define EHCI_INSNREG05_ULPI (0xA4) 50 #define EHCI_INSNREG05_ULPI_CONTROL_SHIFT 31 51 #define EHCI_INSNREG05_ULPI_PORTSEL_SHIFT 24 52 #define EHCI_INSNREG05_ULPI_OPSEL_SHIFT 22 53 #define EHCI_INSNREG05_ULPI_REGADD_SHIFT 16 54 #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 55 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 56 57 /*-------------------------------------------------------------------------*/ 58 59 static const struct hc_driver ehci_omap_hc_driver; 60 61 62 static inline void ehci_write(void __iomem *base, u32 reg, u32 val) 63 { 64 __raw_writel(val, base + reg); 65 } 66 67 static inline u32 ehci_read(void __iomem *base, u32 reg) 68 { 69 return __raw_readl(base + reg); 70 } 71 72 static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port) 73 { 74 struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); 75 unsigned long timeout = jiffies + msecs_to_jiffies(1000); 76 unsigned reg = 0; 77 78 reg = ULPI_FUNC_CTRL_RESET 79 /* FUNCTION_CTRL_SET register */ 80 | (ULPI_SET(ULPI_FUNC_CTRL) << EHCI_INSNREG05_ULPI_REGADD_SHIFT) 81 /* Write */ 82 | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT) 83 /* PORTn */ 84 | ((port + 1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT) 85 /* start ULPI access*/ 86 | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT); 87 88 ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg); 89 90 /* Wait for ULPI access completion */ 91 while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI) 92 & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) { 93 cpu_relax(); 94 95 if (time_after(jiffies, timeout)) { 96 dev_dbg(&pdev->dev, "phy reset operation timed out\n"); 97 break; 98 } 99 } 100 } 101 102 static void disable_put_regulator( 103 struct ehci_hcd_omap_platform_data *pdata) 104 { 105 int i; 106 107 for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { 108 if (pdata->regulator[i]) { 109 regulator_disable(pdata->regulator[i]); 110 regulator_put(pdata->regulator[i]); 111 } 112 } 113 } 114 115 /* configure so an HC device and id are always provided */ 116 /* always called with process context; sleeping is OK */ 117 118 /** 119 * ehci_hcd_omap_probe - initialize TI-based HCDs 120 * 121 * Allocates basic resources for this USB host controller, and 122 * then invokes the start() method for the HCD associated with it 123 * through the hotplug entry's driver_data. 124 */ 125 static int ehci_hcd_omap_probe(struct platform_device *pdev) 126 { 127 struct device *dev = &pdev->dev; 128 struct ehci_hcd_omap_platform_data *pdata = dev->platform_data; 129 struct resource *res; 130 struct usb_hcd *hcd; 131 void __iomem *regs; 132 struct ehci_hcd *omap_ehci; 133 int ret = -ENODEV; 134 int irq; 135 int i; 136 char supply[7]; 137 138 if (usb_disabled()) 139 return -ENODEV; 140 141 if (!dev->parent) { 142 dev_err(dev, "Missing parent device\n"); 143 return -ENODEV; 144 } 145 146 irq = platform_get_irq_byname(pdev, "ehci-irq"); 147 if (irq < 0) { 148 dev_err(dev, "EHCI irq failed\n"); 149 return -ENODEV; 150 } 151 152 res = platform_get_resource_byname(pdev, 153 IORESOURCE_MEM, "ehci"); 154 if (!res) { 155 dev_err(dev, "UHH EHCI get resource failed\n"); 156 return -ENODEV; 157 } 158 159 regs = ioremap(res->start, resource_size(res)); 160 if (!regs) { 161 dev_err(dev, "UHH EHCI ioremap failed\n"); 162 return -ENOMEM; 163 } 164 165 hcd = usb_create_hcd(&ehci_omap_hc_driver, dev, 166 dev_name(dev)); 167 if (!hcd) { 168 dev_err(dev, "failed to create hcd with err %d\n", ret); 169 ret = -ENOMEM; 170 goto err_io; 171 } 172 173 hcd->rsrc_start = res->start; 174 hcd->rsrc_len = resource_size(res); 175 hcd->regs = regs; 176 177 /* get ehci regulator and enable */ 178 for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { 179 if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) { 180 pdata->regulator[i] = NULL; 181 continue; 182 } 183 snprintf(supply, sizeof(supply), "hsusb%d", i); 184 pdata->regulator[i] = regulator_get(dev, supply); 185 if (IS_ERR(pdata->regulator[i])) { 186 pdata->regulator[i] = NULL; 187 dev_dbg(dev, 188 "failed to get ehci port%d regulator\n", i); 189 } else { 190 regulator_enable(pdata->regulator[i]); 191 } 192 } 193 194 pm_runtime_enable(dev); 195 pm_runtime_get_sync(dev); 196 197 /* 198 * An undocumented "feature" in the OMAP3 EHCI controller, 199 * causes suspended ports to be taken out of suspend when 200 * the USBCMD.Run/Stop bit is cleared (for example when 201 * we do ehci_bus_suspend). 202 * This breaks suspend-resume if the root-hub is allowed 203 * to suspend. Writing 1 to this undocumented register bit 204 * disables this feature and restores normal behavior. 205 */ 206 ehci_write(regs, EHCI_INSNREG04, 207 EHCI_INSNREG04_DISABLE_UNSUSPEND); 208 209 /* Soft reset the PHY using PHY reset command over ULPI */ 210 if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) 211 omap_ehci_soft_phy_reset(pdev, 0); 212 if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) 213 omap_ehci_soft_phy_reset(pdev, 1); 214 215 omap_ehci = hcd_to_ehci(hcd); 216 omap_ehci->sbrn = 0x20; 217 218 /* we know this is the memory we want, no need to ioremap again */ 219 omap_ehci->caps = hcd->regs; 220 omap_ehci->regs = hcd->regs 221 + HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase)); 222 223 dbg_hcs_params(omap_ehci, "reset"); 224 dbg_hcc_params(omap_ehci, "reset"); 225 226 /* cache this readonly data; minimize chip reads */ 227 omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); 228 229 ehci_reset(omap_ehci); 230 231 ret = usb_add_hcd(hcd, irq, IRQF_SHARED); 232 if (ret) { 233 dev_err(dev, "failed to add hcd with err %d\n", ret); 234 goto err_add_hcd; 235 } 236 237 /* root ports should always stay powered */ 238 ehci_port_power(omap_ehci, 1); 239 240 return 0; 241 242 err_add_hcd: 243 disable_put_regulator(pdata); 244 pm_runtime_put_sync(dev); 245 246 err_io: 247 iounmap(regs); 248 return ret; 249 } 250 251 252 /** 253 * ehci_hcd_omap_remove - shutdown processing for EHCI HCDs 254 * @pdev: USB Host Controller being removed 255 * 256 * Reverses the effect of usb_ehci_hcd_omap_probe(), first invoking 257 * the HCD's stop() method. It is always called from a thread 258 * context, normally "rmmod", "apmd", or something similar. 259 */ 260 static int ehci_hcd_omap_remove(struct platform_device *pdev) 261 { 262 struct device *dev = &pdev->dev; 263 struct usb_hcd *hcd = dev_get_drvdata(dev); 264 265 usb_remove_hcd(hcd); 266 disable_put_regulator(dev->platform_data); 267 iounmap(hcd->regs); 268 usb_put_hcd(hcd); 269 pm_runtime_put_sync(dev); 270 pm_runtime_disable(dev); 271 272 return 0; 273 } 274 275 static void ehci_hcd_omap_shutdown(struct platform_device *pdev) 276 { 277 struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev); 278 279 if (hcd->driver->shutdown) 280 hcd->driver->shutdown(hcd); 281 } 282 283 static struct platform_driver ehci_hcd_omap_driver = { 284 .probe = ehci_hcd_omap_probe, 285 .remove = ehci_hcd_omap_remove, 286 .shutdown = ehci_hcd_omap_shutdown, 287 /*.suspend = ehci_hcd_omap_suspend, */ 288 /*.resume = ehci_hcd_omap_resume, */ 289 .driver = { 290 .name = "ehci-omap", 291 } 292 }; 293 294 /*-------------------------------------------------------------------------*/ 295 296 static const struct hc_driver ehci_omap_hc_driver = { 297 .description = hcd_name, 298 .product_desc = "OMAP-EHCI Host Controller", 299 .hcd_priv_size = sizeof(struct ehci_hcd), 300 301 /* 302 * generic hardware linkage 303 */ 304 .irq = ehci_irq, 305 .flags = HCD_MEMORY | HCD_USB2, 306 307 /* 308 * basic lifecycle operations 309 */ 310 .reset = ehci_init, 311 .start = ehci_run, 312 .stop = ehci_stop, 313 .shutdown = ehci_shutdown, 314 315 /* 316 * managing i/o requests and associated device resources 317 */ 318 .urb_enqueue = ehci_urb_enqueue, 319 .urb_dequeue = ehci_urb_dequeue, 320 .endpoint_disable = ehci_endpoint_disable, 321 .endpoint_reset = ehci_endpoint_reset, 322 323 /* 324 * scheduling support 325 */ 326 .get_frame_number = ehci_get_frame, 327 328 /* 329 * root hub support 330 */ 331 .hub_status_data = ehci_hub_status_data, 332 .hub_control = ehci_hub_control, 333 .bus_suspend = ehci_bus_suspend, 334 .bus_resume = ehci_bus_resume, 335 336 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 337 }; 338 339 MODULE_ALIAS("platform:omap-ehci"); 340 MODULE_AUTHOR("Texas Instruments, Inc."); 341 MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>"); 342 343