diff options
author | David Brownell <david-b@pacbell.net> | 2006-09-02 06:13:45 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 14:58:57 -0400 |
commit | 901b3d75e71535f29b64f352e94ff474d95df475 (patch) | |
tree | bc9dd49a1184bac831afb76943661e47f6366ad0 /drivers/usb/gadget/net2280.c | |
parent | 80f8af0c59385b41564a3ae670f94a1b4caa43b2 (diff) |
USB: net2280: update dma buffer allocation
This updates the code handling dma-coherent buffer allocations, basically
reusing code from the musb_hdrc driver. Instead of trying to work around two
significant limitations of the dma framework (memory wastage for buffers
smaller than a page, and inconsistency between calling context requirements
for allocation and free) this just works around one of them (the latter).
So count this as two steps forward (bugfixes: the latter issue could cause
errors on some platforms, and some MIPS changes broke code for the former),
and one step back (increasing cross-platform memory wastage).
Plus linelength and whitespace fixes; and minor data segment shrinkage.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget/net2280.c')
-rw-r--r-- | drivers/usb/gadget/net2280.c | 156 |
1 files changed, 88 insertions, 68 deletions
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 09243239d948..3bda37f9a35f 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Driver for the PLX NET2280 USB device controller. | 2 | * Driver for the PLX NET2280 USB device controller. |
3 | * Specs and errata are available from <http://www.plxtech.com>. | 3 | * Specs and errata are available from <http://www.plxtech.com>. |
4 | * | 4 | * |
5 | * PLX Technology Inc. (formerly NetChip Technology) supported the | 5 | * PLX Technology Inc. (formerly NetChip Technology) supported the |
6 | * development of this driver. | 6 | * development of this driver. |
7 | * | 7 | * |
8 | * | 8 | * |
@@ -26,7 +26,8 @@ | |||
26 | * Copyright (C) 2003 David Brownell | 26 | * Copyright (C) 2003 David Brownell |
27 | * Copyright (C) 2003-2005 PLX Technology, Inc. | 27 | * Copyright (C) 2003-2005 PLX Technology, Inc. |
28 | * | 28 | * |
29 | * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility with 2282 chip | 29 | * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility |
30 | * with 2282 chip | ||
30 | * | 31 | * |
31 | * This program is free software; you can redistribute it and/or modify | 32 | * This program is free software; you can redistribute it and/or modify |
32 | * it under the terms of the GNU General Public License as published by | 33 | * it under the terms of the GNU General Public License as published by |
@@ -85,7 +86,7 @@ static const char driver_name [] = "net2280"; | |||
85 | static const char driver_desc [] = DRIVER_DESC; | 86 | static const char driver_desc [] = DRIVER_DESC; |
86 | 87 | ||
87 | static const char ep0name [] = "ep0"; | 88 | static const char ep0name [] = "ep0"; |
88 | static const char *ep_name [] = { | 89 | static const char *const ep_name [] = { |
89 | ep0name, | 90 | ep0name, |
90 | "ep-a", "ep-b", "ep-c", "ep-d", | 91 | "ep-a", "ep-b", "ep-c", "ep-d", |
91 | "ep-e", "ep-f", | 92 | "ep-e", "ep-f", |
@@ -225,7 +226,9 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) | |||
225 | if (!ep->is_in) | 226 | if (!ep->is_in) |
226 | writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); | 227 | writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); |
227 | else if (dev->pdev->device != 0x2280) { | 228 | else if (dev->pdev->device != 0x2280) { |
228 | /* Added for 2282, Don't use nak packets on an in endpoint, this was ignored on 2280 */ | 229 | /* Added for 2282, Don't use nak packets on an in endpoint, |
230 | * this was ignored on 2280 | ||
231 | */ | ||
229 | writel ((1 << CLEAR_NAK_OUT_PACKETS) | 232 | writel ((1 << CLEAR_NAK_OUT_PACKETS) |
230 | | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); | 233 | | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); |
231 | } | 234 | } |
@@ -288,7 +291,7 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec) | |||
288 | return -ETIMEDOUT; | 291 | return -ETIMEDOUT; |
289 | } | 292 | } |
290 | 293 | ||
291 | static struct usb_ep_ops net2280_ep_ops; | 294 | static const struct usb_ep_ops net2280_ep_ops; |
292 | 295 | ||
293 | static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) | 296 | static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep) |
294 | { | 297 | { |
@@ -449,34 +452,15 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) | |||
449 | 452 | ||
450 | /*-------------------------------------------------------------------------*/ | 453 | /*-------------------------------------------------------------------------*/ |
451 | 454 | ||
452 | #undef USE_KMALLOC | 455 | /* |
453 | 456 | * dma-coherent memory allocation (for dma-capable endpoints) | |
454 | /* many common platforms have dma-coherent caches, which means that it's | ||
455 | * safe to use kmalloc() memory for all i/o buffers without using any | ||
456 | * cache flushing calls. (unless you're trying to share cache lines | ||
457 | * between dma and non-dma activities, which is a slow idea in any case.) | ||
458 | * | 457 | * |
459 | * other platforms need more care, with 2.5 having a moderately general | 458 | * NOTE: the dma_*_coherent() API calls suck. Most implementations are |
460 | * solution (which falls down for allocations smaller than one page) | 459 | * (a) page-oriented, so small buffers lose big; and (b) asymmetric with |
461 | * that improves significantly on the 2.4 PCI allocators by removing | 460 | * respect to calls with irqs disabled: alloc is safe, free is not. |
462 | * the restriction that memory never be freed in_interrupt(). | 461 | * We currently work around (b), but not (a). |
463 | */ | 462 | */ |
464 | #if defined(CONFIG_X86) | ||
465 | #define USE_KMALLOC | ||
466 | |||
467 | #elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) | ||
468 | #define USE_KMALLOC | ||
469 | 463 | ||
470 | #elif defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT) | ||
471 | #define USE_KMALLOC | ||
472 | |||
473 | /* FIXME there are other cases, including an x86-64 one ... */ | ||
474 | #endif | ||
475 | |||
476 | /* allocating buffers this way eliminates dma mapping overhead, which | ||
477 | * on some platforms will mean eliminating a per-io buffer copy. with | ||
478 | * some kinds of system caches, further tweaks may still be needed. | ||
479 | */ | ||
480 | static void * | 464 | static void * |
481 | net2280_alloc_buffer ( | 465 | net2280_alloc_buffer ( |
482 | struct usb_ep *_ep, | 466 | struct usb_ep *_ep, |
@@ -493,43 +477,71 @@ net2280_alloc_buffer ( | |||
493 | return NULL; | 477 | return NULL; |
494 | *dma = DMA_ADDR_INVALID; | 478 | *dma = DMA_ADDR_INVALID; |
495 | 479 | ||
496 | #if defined(USE_KMALLOC) | 480 | if (ep->dma) |
497 | retval = kmalloc(bytes, gfp_flags); | ||
498 | if (retval) | ||
499 | *dma = virt_to_phys(retval); | ||
500 | #else | ||
501 | if (ep->dma) { | ||
502 | /* the main problem with this call is that it wastes memory | ||
503 | * on typical 1/N page allocations: it allocates 1-N pages. | ||
504 | */ | ||
505 | #warning Using dma_alloc_coherent even with buffers smaller than a page. | ||
506 | retval = dma_alloc_coherent(&ep->dev->pdev->dev, | 481 | retval = dma_alloc_coherent(&ep->dev->pdev->dev, |
507 | bytes, dma, gfp_flags); | 482 | bytes, dma, gfp_flags); |
508 | } else | 483 | else |
509 | retval = kmalloc(bytes, gfp_flags); | 484 | retval = kmalloc(bytes, gfp_flags); |
510 | #endif | ||
511 | return retval; | 485 | return retval; |
512 | } | 486 | } |
513 | 487 | ||
488 | static DEFINE_SPINLOCK(buflock); | ||
489 | static LIST_HEAD(buffers); | ||
490 | |||
491 | struct free_record { | ||
492 | struct list_head list; | ||
493 | struct device *dev; | ||
494 | unsigned bytes; | ||
495 | dma_addr_t dma; | ||
496 | }; | ||
497 | |||
498 | static void do_free(unsigned long ignored) | ||
499 | { | ||
500 | spin_lock_irq(&buflock); | ||
501 | while (!list_empty(&buffers)) { | ||
502 | struct free_record *buf; | ||
503 | |||
504 | buf = list_entry(buffers.next, struct free_record, list); | ||
505 | list_del(&buf->list); | ||
506 | spin_unlock_irq(&buflock); | ||
507 | |||
508 | dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma); | ||
509 | |||
510 | spin_lock_irq(&buflock); | ||
511 | } | ||
512 | spin_unlock_irq(&buflock); | ||
513 | } | ||
514 | |||
515 | static DECLARE_TASKLET(deferred_free, do_free, 0); | ||
516 | |||
514 | static void | 517 | static void |
515 | net2280_free_buffer ( | 518 | net2280_free_buffer ( |
516 | struct usb_ep *_ep, | 519 | struct usb_ep *_ep, |
517 | void *buf, | 520 | void *address, |
518 | dma_addr_t dma, | 521 | dma_addr_t dma, |
519 | unsigned bytes | 522 | unsigned bytes |
520 | ) { | 523 | ) { |
521 | /* free memory into the right allocator */ | 524 | /* free memory into the right allocator */ |
522 | #ifndef USE_KMALLOC | ||
523 | if (dma != DMA_ADDR_INVALID) { | 525 | if (dma != DMA_ADDR_INVALID) { |
524 | struct net2280_ep *ep; | 526 | struct net2280_ep *ep; |
527 | struct free_record *buf = address; | ||
528 | unsigned long flags; | ||
525 | 529 | ||
526 | ep = container_of(_ep, struct net2280_ep, ep); | 530 | ep = container_of(_ep, struct net2280_ep, ep); |
527 | if (!_ep) | 531 | if (!_ep) |
528 | return; | 532 | return; |
529 | dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma); | 533 | |
534 | ep = container_of (_ep, struct net2280_ep, ep); | ||
535 | buf->dev = &ep->dev->pdev->dev; | ||
536 | buf->bytes = bytes; | ||
537 | buf->dma = dma; | ||
538 | |||
539 | spin_lock_irqsave(&buflock, flags); | ||
540 | list_add_tail(&buf->list, &buffers); | ||
541 | tasklet_schedule(&deferred_free); | ||
542 | spin_unlock_irqrestore(&buflock, flags); | ||
530 | } else | 543 | } else |
531 | #endif | 544 | kfree (address); |
532 | kfree (buf); | ||
533 | } | 545 | } |
534 | 546 | ||
535 | /*-------------------------------------------------------------------------*/ | 547 | /*-------------------------------------------------------------------------*/ |
@@ -737,7 +749,8 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) | |||
737 | */ | 749 | */ |
738 | if (ep->is_in) | 750 | if (ep->is_in) |
739 | dmacount |= (1 << DMA_DIRECTION); | 751 | dmacount |= (1 << DMA_DIRECTION); |
740 | if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || ep->dev->pdev->device != 0x2280) | 752 | if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) |
753 | || ep->dev->pdev->device != 0x2280) | ||
741 | dmacount |= (1 << END_OF_CHAIN); | 754 | dmacount |= (1 << END_OF_CHAIN); |
742 | 755 | ||
743 | req->valid = valid; | 756 | req->valid = valid; |
@@ -812,7 +825,7 @@ static void start_dma (struct net2280_ep *ep, struct net2280_request *req) | |||
812 | 825 | ||
813 | /* previous OUT packet might have been short */ | 826 | /* previous OUT packet might have been short */ |
814 | if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat)) | 827 | if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat)) |
815 | & (1 << NAK_OUT_PACKETS)) != 0) { | 828 | & (1 << NAK_OUT_PACKETS)) != 0) { |
816 | writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT), | 829 | writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT), |
817 | &ep->regs->ep_stat); | 830 | &ep->regs->ep_stat); |
818 | 831 | ||
@@ -1373,7 +1386,7 @@ net2280_fifo_flush (struct usb_ep *_ep) | |||
1373 | (void) readl (&ep->regs->ep_rsp); | 1386 | (void) readl (&ep->regs->ep_rsp); |
1374 | } | 1387 | } |
1375 | 1388 | ||
1376 | static struct usb_ep_ops net2280_ep_ops = { | 1389 | static const struct usb_ep_ops net2280_ep_ops = { |
1377 | .enable = net2280_enable, | 1390 | .enable = net2280_enable, |
1378 | .disable = net2280_disable, | 1391 | .disable = net2280_disable, |
1379 | 1392 | ||
@@ -1631,7 +1644,7 @@ show_registers (struct device *_dev, struct device_attribute *attr, char *buf) | |||
1631 | } | 1644 | } |
1632 | 1645 | ||
1633 | /* Indexed Registers */ | 1646 | /* Indexed Registers */ |
1634 | // none yet | 1647 | // none yet |
1635 | 1648 | ||
1636 | /* Statistics */ | 1649 | /* Statistics */ |
1637 | t = scnprintf (next, size, "\nirqs: "); | 1650 | t = scnprintf (next, size, "\nirqs: "); |
@@ -1691,11 +1704,11 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf) | |||
1691 | ({ char *val; | 1704 | ({ char *val; |
1692 | switch (d->bmAttributes & 0x03) { | 1705 | switch (d->bmAttributes & 0x03) { |
1693 | case USB_ENDPOINT_XFER_BULK: | 1706 | case USB_ENDPOINT_XFER_BULK: |
1694 | val = "bulk"; break; | 1707 | val = "bulk"; break; |
1695 | case USB_ENDPOINT_XFER_INT: | 1708 | case USB_ENDPOINT_XFER_INT: |
1696 | val = "intr"; break; | 1709 | val = "intr"; break; |
1697 | default: | 1710 | default: |
1698 | val = "iso"; break; | 1711 | val = "iso"; break; |
1699 | }; val; }), | 1712 | }; val; }), |
1700 | le16_to_cpu (d->wMaxPacketSize) & 0x1fff, | 1713 | le16_to_cpu (d->wMaxPacketSize) & 0x1fff, |
1701 | ep->dma ? "dma" : "pio", ep->fifo_size | 1714 | ep->dma ? "dma" : "pio", ep->fifo_size |
@@ -1808,8 +1821,8 @@ extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); | |||
1808 | * net2280_set_fifo_mode - change allocation of fifo buffers | 1821 | * net2280_set_fifo_mode - change allocation of fifo buffers |
1809 | * @gadget: access to the net2280 device that will be updated | 1822 | * @gadget: access to the net2280 device that will be updated |
1810 | * @mode: 0 for default, four 1kB buffers (ep-a through ep-d); | 1823 | * @mode: 0 for default, four 1kB buffers (ep-a through ep-d); |
1811 | * 1 for two 2kB buffers (ep-a and ep-b only); | 1824 | * 1 for two 2kB buffers (ep-a and ep-b only); |
1812 | * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c). | 1825 | * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c). |
1813 | * | 1826 | * |
1814 | * returns zero on success, else negative errno. when this succeeds, | 1827 | * returns zero on success, else negative errno. when this succeeds, |
1815 | * the contents of gadget->ep_list may have changed. | 1828 | * the contents of gadget->ep_list may have changed. |
@@ -2241,7 +2254,8 @@ static void handle_ep_small (struct net2280_ep *ep) | |||
2241 | req->td->dmacount = 0; | 2254 | req->td->dmacount = 0; |
2242 | t = readl (&ep->regs->ep_avail); | 2255 | t = readl (&ep->regs->ep_avail); |
2243 | dma_done (ep, req, count, | 2256 | dma_done (ep, req, count, |
2244 | (ep->out_overflow || t) ? -EOVERFLOW : 0); | 2257 | (ep->out_overflow || t) |
2258 | ? -EOVERFLOW : 0); | ||
2245 | } | 2259 | } |
2246 | 2260 | ||
2247 | /* also flush to prevent erratum 0106 trouble */ | 2261 | /* also flush to prevent erratum 0106 trouble */ |
@@ -2411,7 +2425,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) | |||
2411 | , &ep->regs->ep_stat); | 2425 | , &ep->regs->ep_stat); |
2412 | u.raw [0] = readl (&dev->usb->setup0123); | 2426 | u.raw [0] = readl (&dev->usb->setup0123); |
2413 | u.raw [1] = readl (&dev->usb->setup4567); | 2427 | u.raw [1] = readl (&dev->usb->setup4567); |
2414 | 2428 | ||
2415 | cpu_to_le32s (&u.raw [0]); | 2429 | cpu_to_le32s (&u.raw [0]); |
2416 | cpu_to_le32s (&u.raw [1]); | 2430 | cpu_to_le32s (&u.raw [1]); |
2417 | 2431 | ||
@@ -2578,14 +2592,16 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat) | |||
2578 | 2592 | ||
2579 | /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. | 2593 | /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. |
2580 | * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and | 2594 | * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and |
2581 | * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT | 2595 | * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT |
2582 | * only indicates a change in the reset state). | 2596 | * only indicates a change in the reset state). |
2583 | */ | 2597 | */ |
2584 | if (stat & tmp) { | 2598 | if (stat & tmp) { |
2585 | writel (tmp, &dev->regs->irqstat1); | 2599 | writel (tmp, &dev->regs->irqstat1); |
2586 | if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && | 2600 | if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) |
2587 | ((readl (&dev->usb->usbstat) & mask) == 0)) | 2601 | && ((readl (&dev->usb->usbstat) & mask) |
2588 | || ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0) | 2602 | == 0)) |
2603 | || ((readl (&dev->usb->usbctl) | ||
2604 | & (1 << VBUS_PIN)) == 0) | ||
2589 | ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) { | 2605 | ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) { |
2590 | DEBUG (dev, "disconnect %s\n", | 2606 | DEBUG (dev, "disconnect %s\n", |
2591 | dev->driver->driver.name); | 2607 | dev->driver->driver.name); |
@@ -2852,7 +2868,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) | |||
2852 | 2868 | ||
2853 | /* now all the pci goodies ... */ | 2869 | /* now all the pci goodies ... */ |
2854 | if (pci_enable_device (pdev) < 0) { | 2870 | if (pci_enable_device (pdev) < 0) { |
2855 | retval = -ENODEV; | 2871 | retval = -ENODEV; |
2856 | goto done; | 2872 | goto done; |
2857 | } | 2873 | } |
2858 | dev->enabled = 1; | 2874 | dev->enabled = 1; |
@@ -2870,6 +2886,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) | |||
2870 | } | 2886 | } |
2871 | dev->region = 1; | 2887 | dev->region = 1; |
2872 | 2888 | ||
2889 | /* FIXME provide firmware download interface to put | ||
2890 | * 8051 code into the chip, e.g. to turn on PCI PM. | ||
2891 | */ | ||
2892 | |||
2873 | base = ioremap_nocache (resource, len); | 2893 | base = ioremap_nocache (resource, len); |
2874 | if (base == NULL) { | 2894 | if (base == NULL) { |
2875 | DEBUG (dev, "can't map memory\n"); | 2895 | DEBUG (dev, "can't map memory\n"); |
@@ -2984,16 +3004,16 @@ static void net2280_shutdown (struct pci_dev *pdev) | |||
2984 | 3004 | ||
2985 | /*-------------------------------------------------------------------------*/ | 3005 | /*-------------------------------------------------------------------------*/ |
2986 | 3006 | ||
2987 | static struct pci_device_id pci_ids [] = { { | 3007 | static const struct pci_device_id pci_ids [] = { { |
2988 | .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), | 3008 | .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), |
2989 | .class_mask = ~0, | 3009 | .class_mask = ~0, |
2990 | .vendor = 0x17cc, | 3010 | .vendor = 0x17cc, |
2991 | .device = 0x2280, | 3011 | .device = 0x2280, |
2992 | .subvendor = PCI_ANY_ID, | 3012 | .subvendor = PCI_ANY_ID, |
2993 | .subdevice = PCI_ANY_ID, | 3013 | .subdevice = PCI_ANY_ID, |
2994 | }, { | 3014 | }, { |
2995 | .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), | 3015 | .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), |
2996 | .class_mask = ~0, | 3016 | .class_mask = ~0, |
2997 | .vendor = 0x17cc, | 3017 | .vendor = 0x17cc, |
2998 | .device = 0x2282, | 3018 | .device = 0x2282, |
2999 | .subvendor = PCI_ANY_ID, | 3019 | .subvendor = PCI_ANY_ID, |