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