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