1/* 2 * PMC MSP EHCI (Host Controller Driver) for USB. 3 * 4 * (C) Copyright 2006-2010 PMC-Sierra Inc 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file "COPYING" in the main directory of this archive 8 * for more details. 9 * 10 */ 11 12/* includes */ 13#include <linux/platform_device.h> 14#include <linux/gpio.h> 15#include <linux/usb.h> 16#include <msp_usb.h> 17 18/* stream disable*/ 19#define USB_CTRL_MODE_STREAM_DISABLE 0x10 20 21/* threshold */ 22#define USB_CTRL_FIFO_THRESH 0x00300000 23 24/* register offset for usb_mode */ 25#define USB_EHCI_REG_USB_MODE 0x68 26 27/* register offset for usb fifo */ 28#define USB_EHCI_REG_USB_FIFO 0x24 29 30/* register offset for usb status */ 31#define USB_EHCI_REG_USB_STATUS 0x44 32 33/* serial/parallel transceiver */ 34#define USB_EHCI_REG_BIT_STAT_STS (1<<29) 35 36/* TWI USB0 host device pin */ 37#define MSP_PIN_USB0_HOST_DEV 49 38 39/* TWI USB1 host device pin */ 40#define MSP_PIN_USB1_HOST_DEV 50 41 42 43static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci) 44{ 45 u8 *base; 46 u8 *statreg; 47 u8 *fiforeg; 48 u32 val; 49 struct ehci_regs *reg_base = ehci->regs; 50 51 /* get register base */ 52 base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE; 53 statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS; 54 fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO; 55 56 /* Disable controller mode stream */ 57 val = ehci_readl(ehci, (u32 *)base); 58 ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE), 59 (u32 *)base); 60 61 /* clear STS to select parallel transceiver interface */ 62 val = ehci_readl(ehci, (u32 *)statreg); 63 val = val & ~USB_EHCI_REG_BIT_STAT_STS; 64 ehci_writel(ehci, val, (u32 *)statreg); 65 66 /* write to set the proper fifo threshold */ 67 ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg); 68 69 /* set TWI GPIO USB_HOST_DEV pin high */ 70 gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1); 71} 72 73/* called during probe() after chip reset completes */ 74static int ehci_msp_setup(struct usb_hcd *hcd) 75{ 76 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 77 int retval; 78 79 ehci->big_endian_mmio = 1; 80 ehci->big_endian_desc = 1; 81 82 ehci->caps = hcd->regs; 83 hcd->has_tt = 1; 84 85 retval = ehci_setup(hcd); 86 if (retval) 87 return retval; 88 89 usb_hcd_tdi_set_mode(ehci); 90 91 return retval; 92} 93 94 95/* configure so an HC device and id are always provided 96 * always called with process context; sleeping is OK 97 */ 98 99static int usb_hcd_msp_map_regs(struct mspusb_device *dev) 100{ 101 struct resource *res; 102 struct platform_device *pdev = &dev->dev; 103 u32 res_len; 104 int retval; 105 106 /* MAB register space */ 107 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 108 if (res == NULL) 109 return -ENOMEM; 110 res_len = resource_size(res); 111 if (!request_mem_region(res->start, res_len, "mab regs")) 112 return -EBUSY; 113 114 dev->mab_regs = ioremap_nocache(res->start, res_len); 115 if (dev->mab_regs == NULL) { 116 retval = -ENOMEM; 117 goto err1; 118 } 119 120 /* MSP USB register space */ 121 res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 122 if (res == NULL) { 123 retval = -ENOMEM; 124 goto err2; 125 } 126 res_len = resource_size(res); 127 if (!request_mem_region(res->start, res_len, "usbid regs")) { 128 retval = -EBUSY; 129 goto err2; 130 } 131 dev->usbid_regs = ioremap_nocache(res->start, res_len); 132 if (dev->usbid_regs == NULL) { 133 retval = -ENOMEM; 134 goto err3; 135 } 136 137 return 0; 138err3: 139 res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 140 res_len = resource_size(res); 141 release_mem_region(res->start, res_len); 142err2: 143 iounmap(dev->mab_regs); 144err1: 145 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 146 res_len = resource_size(res); 147 release_mem_region(res->start, res_len); 148 dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n"); 149 return retval; 150} 151 152/** 153 * usb_hcd_msp_probe - initialize PMC MSP-based HCDs 154 * Context: !in_interrupt() 155 * 156 * Allocates basic resources for this USB host controller, and 157 * then invokes the start() method for the HCD associated with it 158 * through the hotplug entry's driver_data. 159 * 160 */ 161int usb_hcd_msp_probe(const struct hc_driver *driver, 162 struct platform_device *dev) 163{ 164 int retval; 165 struct usb_hcd *hcd; 166 struct resource *res; 167 struct ehci_hcd *ehci ; 168 169 hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp"); 170 if (!hcd) 171 return -ENOMEM; 172 173 res = platform_get_resource(dev, IORESOURCE_MEM, 0); 174 if (res == NULL) { 175 pr_debug("No IOMEM resource info for %s.\n", dev->name); 176 retval = -ENOMEM; 177 goto err1; 178 } 179 hcd->rsrc_start = res->start; 180 hcd->rsrc_len = resource_size(res); 181 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) { 182 retval = -EBUSY; 183 goto err1; 184 } 185 hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); 186 if (!hcd->regs) { 187 pr_debug("ioremap failed"); 188 retval = -ENOMEM; 189 goto err2; 190 } 191 192 res = platform_get_resource(dev, IORESOURCE_IRQ, 0); 193 if (res == NULL) { 194 dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name); 195 retval = -ENOMEM; 196 goto err3; 197 } 198 199 /* Map non-EHCI register spaces */ 200 retval = usb_hcd_msp_map_regs(to_mspusb_device(dev)); 201 if (retval != 0) 202 goto err3; 203 204 ehci = hcd_to_ehci(hcd); 205 ehci->big_endian_mmio = 1; 206 ehci->big_endian_desc = 1; 207 208 209 retval = usb_add_hcd(hcd, res->start, IRQF_SHARED); 210 if (retval == 0) { 211 device_wakeup_enable(hcd->self.controller); 212 return 0; 213 } 214 215 usb_remove_hcd(hcd); 216err3: 217 iounmap(hcd->regs); 218err2: 219 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 220err1: 221 usb_put_hcd(hcd); 222 223 return retval; 224} 225 226 227 228/** 229 * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs 230 * @dev: USB Host Controller being removed 231 * Context: !in_interrupt() 232 * 233 * Reverses the effect of usb_hcd_msp_probe(), first invoking 234 * the HCD's stop() method. It is always called from a thread 235 * context, normally "rmmod", "apmd", or something similar. 236 * 237 * may be called without controller electrically present 238 * may be called with controller, bus, and devices active 239 */ 240void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev) 241{ 242 usb_remove_hcd(hcd); 243 iounmap(hcd->regs); 244 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 245 usb_put_hcd(hcd); 246} 247 248static const struct hc_driver ehci_msp_hc_driver = { 249 .description = hcd_name, 250 .product_desc = "PMC MSP EHCI", 251 .hcd_priv_size = sizeof(struct ehci_hcd), 252 253 /* 254 * generic hardware linkage 255 */ 256 .irq = ehci_irq, 257 .flags = HCD_MEMORY | HCD_USB2 | HCD_BH, 258 259 /* 260 * basic lifecycle operations 261 */ 262 .reset = ehci_msp_setup, 263 .shutdown = ehci_shutdown, 264 .start = ehci_run, 265 .stop = ehci_stop, 266 267 /* 268 * managing i/o requests and associated device resources 269 */ 270 .urb_enqueue = ehci_urb_enqueue, 271 .urb_dequeue = ehci_urb_dequeue, 272 .endpoint_disable = ehci_endpoint_disable, 273 .endpoint_reset = ehci_endpoint_reset, 274 275 /* 276 * scheduling support 277 */ 278 .get_frame_number = ehci_get_frame, 279 280 /* 281 * root hub support 282 */ 283 .hub_status_data = ehci_hub_status_data, 284 .hub_control = ehci_hub_control, 285 .bus_suspend = ehci_bus_suspend, 286 .bus_resume = ehci_bus_resume, 287 .relinquish_port = ehci_relinquish_port, 288 .port_handed_over = ehci_port_handed_over, 289 290 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 291}; 292 293static int ehci_hcd_msp_drv_probe(struct platform_device *pdev) 294{ 295 int ret; 296 297 pr_debug("In ehci_hcd_msp_drv_probe"); 298 299 if (usb_disabled()) 300 return -ENODEV; 301 302 gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO"); 303 304 ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev); 305 306 return ret; 307} 308 309static int ehci_hcd_msp_drv_remove(struct platform_device *pdev) 310{ 311 struct usb_hcd *hcd = platform_get_drvdata(pdev); 312 313 usb_hcd_msp_remove(hcd, pdev); 314 315 /* free TWI GPIO USB_HOST_DEV pin */ 316 gpio_free(MSP_PIN_USB0_HOST_DEV); 317 318 return 0; 319} 320 321MODULE_ALIAS("pmcmsp-ehci"); 322 323static struct platform_driver ehci_hcd_msp_driver = { 324 .probe = ehci_hcd_msp_drv_probe, 325 .remove = ehci_hcd_msp_drv_remove, 326 .driver = { 327 .name = "pmcmsp-ehci", 328 }, 329}; 330