Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.7-rc5 370 lines 8.8 kB view raw
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#ifdef CONFIG_MSP_HAS_DUAL_USB 72 gpio_direction_output(MSP_PIN_USB1_HOST_DEV, 1); 73#endif 74} 75 76/* called during probe() after chip reset completes */ 77static int ehci_msp_setup(struct usb_hcd *hcd) 78{ 79 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 80 int retval; 81 82 ehci->big_endian_mmio = 1; 83 ehci->big_endian_desc = 1; 84 85 ehci->caps = hcd->regs; 86 hcd->has_tt = 1; 87 88 retval = ehci_setup(hcd); 89 if (retval) 90 return retval; 91 92 usb_hcd_tdi_set_mode(ehci); 93 ehci_port_power(ehci, 0); 94 95 return retval; 96} 97 98 99/* configure so an HC device and id are always provided 100 * always called with process context; sleeping is OK 101 */ 102 103static int usb_hcd_msp_map_regs(struct mspusb_device *dev) 104{ 105 struct resource *res; 106 struct platform_device *pdev = &dev->dev; 107 u32 res_len; 108 int retval; 109 110 /* MAB register space */ 111 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 112 if (res == NULL) 113 return -ENOMEM; 114 res_len = resource_size(res); 115 if (!request_mem_region(res->start, res_len, "mab regs")) 116 return -EBUSY; 117 118 dev->mab_regs = ioremap_nocache(res->start, res_len); 119 if (dev->mab_regs == NULL) { 120 retval = -ENOMEM; 121 goto err1; 122 } 123 124 /* MSP USB register space */ 125 res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 126 if (res == NULL) { 127 retval = -ENOMEM; 128 goto err2; 129 } 130 res_len = resource_size(res); 131 if (!request_mem_region(res->start, res_len, "usbid regs")) { 132 retval = -EBUSY; 133 goto err2; 134 } 135 dev->usbid_regs = ioremap_nocache(res->start, res_len); 136 if (dev->usbid_regs == NULL) { 137 retval = -ENOMEM; 138 goto err3; 139 } 140 141 return 0; 142err3: 143 res = platform_get_resource(pdev, IORESOURCE_MEM, 2); 144 res_len = resource_size(res); 145 release_mem_region(res->start, res_len); 146err2: 147 iounmap(dev->mab_regs); 148err1: 149 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 150 res_len = resource_size(res); 151 release_mem_region(res->start, res_len); 152 dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n"); 153 return retval; 154} 155 156/** 157 * usb_hcd_msp_probe - initialize PMC MSP-based HCDs 158 * Context: !in_interrupt() 159 * 160 * Allocates basic resources for this USB host controller, and 161 * then invokes the start() method for the HCD associated with it 162 * through the hotplug entry's driver_data. 163 * 164 */ 165int usb_hcd_msp_probe(const struct hc_driver *driver, 166 struct platform_device *dev) 167{ 168 int retval; 169 struct usb_hcd *hcd; 170 struct resource *res; 171 struct ehci_hcd *ehci ; 172 173 hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp"); 174 if (!hcd) 175 return -ENOMEM; 176 177 res = platform_get_resource(dev, IORESOURCE_MEM, 0); 178 if (res == NULL) { 179 pr_debug("No IOMEM resource info for %s.\n", dev->name); 180 retval = -ENOMEM; 181 goto err1; 182 } 183 hcd->rsrc_start = res->start; 184 hcd->rsrc_len = resource_size(res); 185 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) { 186 retval = -EBUSY; 187 goto err1; 188 } 189 hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); 190 if (!hcd->regs) { 191 pr_debug("ioremap failed"); 192 retval = -ENOMEM; 193 goto err2; 194 } 195 196 res = platform_get_resource(dev, IORESOURCE_IRQ, 0); 197 if (res == NULL) { 198 dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name); 199 retval = -ENOMEM; 200 goto err3; 201 } 202 203 /* Map non-EHCI register spaces */ 204 retval = usb_hcd_msp_map_regs(to_mspusb_device(dev)); 205 if (retval != 0) 206 goto err3; 207 208 ehci = hcd_to_ehci(hcd); 209 ehci->big_endian_mmio = 1; 210 ehci->big_endian_desc = 1; 211 212 213 retval = usb_add_hcd(hcd, res->start, IRQF_SHARED); 214 if (retval == 0) 215 return 0; 216 217 usb_remove_hcd(hcd); 218err3: 219 iounmap(hcd->regs); 220err2: 221 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 222err1: 223 usb_put_hcd(hcd); 224 225 return retval; 226} 227 228 229 230/** 231 * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs 232 * @dev: USB Host Controller being removed 233 * Context: !in_interrupt() 234 * 235 * Reverses the effect of usb_hcd_msp_probe(), first invoking 236 * the HCD's stop() method. It is always called from a thread 237 * context, normally "rmmod", "apmd", or something similar. 238 * 239 * may be called without controller electrically present 240 * may be called with controller, bus, and devices active 241 */ 242void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev) 243{ 244 usb_remove_hcd(hcd); 245 iounmap(hcd->regs); 246 release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 247 usb_put_hcd(hcd); 248} 249 250#ifdef CONFIG_MSP_HAS_DUAL_USB 251/* 252 * Wrapper around the main ehci_irq. Since both USB host controllers are 253 * sharing the same IRQ, need to first determine whether we're the intended 254 * recipient of this interrupt. 255 */ 256static irqreturn_t ehci_msp_irq(struct usb_hcd *hcd) 257{ 258 u32 int_src; 259 struct device *dev = hcd->self.controller; 260 struct platform_device *pdev; 261 struct mspusb_device *mdev; 262 struct ehci_hcd *ehci = hcd_to_ehci(hcd); 263 /* need to reverse-map a couple of containers to get our device */ 264 pdev = to_platform_device(dev); 265 mdev = to_mspusb_device(pdev); 266 267 /* Check to see if this interrupt is for this host controller */ 268 int_src = ehci_readl(ehci, &mdev->mab_regs->int_stat); 269 if (int_src & (1 << pdev->id)) 270 return ehci_irq(hcd); 271 272 /* Not for this device */ 273 return IRQ_NONE; 274} 275#endif /* DUAL_USB */ 276 277static const struct hc_driver ehci_msp_hc_driver = { 278 .description = hcd_name, 279 .product_desc = "PMC MSP EHCI", 280 .hcd_priv_size = sizeof(struct ehci_hcd), 281 282 /* 283 * generic hardware linkage 284 */ 285#ifdef CONFIG_MSP_HAS_DUAL_USB 286 .irq = ehci_msp_irq, 287#else 288 .irq = ehci_irq, 289#endif 290 .flags = HCD_MEMORY | HCD_USB2, 291 292 /* 293 * basic lifecycle operations 294 */ 295 .reset = ehci_msp_setup, 296 .start = ehci_run, 297 .shutdown = ehci_shutdown, 298 .start = ehci_run, 299 .stop = ehci_stop, 300 301 /* 302 * managing i/o requests and associated device resources 303 */ 304 .urb_enqueue = ehci_urb_enqueue, 305 .urb_dequeue = ehci_urb_dequeue, 306 .endpoint_disable = ehci_endpoint_disable, 307 .endpoint_reset = ehci_endpoint_reset, 308 309 /* 310 * scheduling support 311 */ 312 .get_frame_number = ehci_get_frame, 313 314 /* 315 * root hub support 316 */ 317 .hub_status_data = ehci_hub_status_data, 318 .hub_control = ehci_hub_control, 319 .bus_suspend = ehci_bus_suspend, 320 .bus_resume = ehci_bus_resume, 321 .relinquish_port = ehci_relinquish_port, 322 .port_handed_over = ehci_port_handed_over, 323 324 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, 325}; 326 327static int ehci_hcd_msp_drv_probe(struct platform_device *pdev) 328{ 329 int ret; 330 331 pr_debug("In ehci_hcd_msp_drv_probe"); 332 333 if (usb_disabled()) 334 return -ENODEV; 335 336 gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO"); 337#ifdef CONFIG_MSP_HAS_DUAL_USB 338 gpio_request(MSP_PIN_USB1_HOST_DEV, "USB1_HOST_DEV_GPIO"); 339#endif 340 341 ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev); 342 343 return ret; 344} 345 346static int ehci_hcd_msp_drv_remove(struct platform_device *pdev) 347{ 348 struct usb_hcd *hcd = platform_get_drvdata(pdev); 349 350 usb_hcd_msp_remove(hcd, pdev); 351 352 /* free TWI GPIO USB_HOST_DEV pin */ 353 gpio_free(MSP_PIN_USB0_HOST_DEV); 354#ifdef CONFIG_MSP_HAS_DUAL_USB 355 gpio_free(MSP_PIN_USB1_HOST_DEV); 356#endif 357 358 return 0; 359} 360 361MODULE_ALIAS("pmcmsp-ehci"); 362 363static struct platform_driver ehci_hcd_msp_driver = { 364 .probe = ehci_hcd_msp_drv_probe, 365 .remove = ehci_hcd_msp_drv_remove, 366 .driver = { 367 .name = "pmcmsp-ehci", 368 .owner = THIS_MODULE, 369 }, 370};