Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 901b3d75 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman
Browse files

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: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 80f8af0c
Loading
Loading
Loading
Loading
+88 −68
Original line number Original line Diff line number Diff line
@@ -26,7 +26,8 @@
 * Copyright (C) 2003 David Brownell
 * Copyright (C) 2003 David Brownell
 * Copyright (C) 2003-2005 PLX Technology, Inc.
 * Copyright (C) 2003-2005 PLX Technology, Inc.
 *
 *
 * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility with 2282 chip
 * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
 *	with 2282 chip
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * it under the terms of the GNU General Public License as published by
@@ -85,7 +86,7 @@ static const char driver_name [] = "net2280";
static const char driver_desc [] = DRIVER_DESC;
static const char driver_desc [] = DRIVER_DESC;


static const char ep0name [] = "ep0";
static const char ep0name [] = "ep0";
static const char *ep_name [] = {
static const char *const ep_name [] = {
	ep0name,
	ep0name,
	"ep-a", "ep-b", "ep-c", "ep-d",
	"ep-a", "ep-b", "ep-c", "ep-d",
	"ep-e", "ep-f",
	"ep-e", "ep-f",
@@ -225,7 +226,9 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
	if (!ep->is_in)
	if (!ep->is_in)
		writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
		writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
	else if (dev->pdev->device != 0x2280) {
	else if (dev->pdev->device != 0x2280) {
		/* Added for 2282, Don't use nak packets on an in endpoint, this was ignored on 2280 */
		/* Added for 2282, Don't use nak packets on an in endpoint,
		 * this was ignored on 2280
		 */
		writel ((1 << CLEAR_NAK_OUT_PACKETS)
		writel ((1 << CLEAR_NAK_OUT_PACKETS)
			| (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
			| (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
	}
	}
@@ -288,7 +291,7 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
	return -ETIMEDOUT;
	return -ETIMEDOUT;
}
}


static struct usb_ep_ops net2280_ep_ops;
static const struct usb_ep_ops net2280_ep_ops;


static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
{
{
@@ -449,34 +452,15 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)


/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/


#undef USE_KMALLOC
/*

 * dma-coherent memory allocation (for dma-capable endpoints)
/* many common platforms have dma-coherent caches, which means that it's
 * safe to use kmalloc() memory for all i/o buffers without using any
 * cache flushing calls.  (unless you're trying to share cache lines
 * between dma and non-dma activities, which is a slow idea in any case.)
 *
 *
 * other platforms need more care, with 2.5 having a moderately general
 * NOTE: the dma_*_coherent() API calls suck.  Most implementations are
 * solution (which falls down for allocations smaller than one page)
 * (a) page-oriented, so small buffers lose big; and (b) asymmetric with
 * that improves significantly on the 2.4 PCI allocators by removing
 * respect to calls with irqs disabled:  alloc is safe, free is not.
 * the restriction that memory never be freed in_interrupt().
 * We currently work around (b), but not (a).
 */
 */
#if	defined(CONFIG_X86)
#define USE_KMALLOC

#elif	defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
#define USE_KMALLOC

#elif	defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
#define USE_KMALLOC


/* FIXME there are other cases, including an x86-64 one ...  */
#endif

/* allocating buffers this way eliminates dma mapping overhead, which
 * on some platforms will mean eliminating a per-io buffer copy.  with
 * some kinds of system caches, further tweaks may still be needed.
 */
static void *
static void *
net2280_alloc_buffer (
net2280_alloc_buffer (
	struct usb_ep		*_ep,
	struct usb_ep		*_ep,
@@ -493,43 +477,71 @@ net2280_alloc_buffer (
		return NULL;
		return NULL;
	*dma = DMA_ADDR_INVALID;
	*dma = DMA_ADDR_INVALID;


#if	defined(USE_KMALLOC)
	if (ep->dma)
	retval = kmalloc(bytes, gfp_flags);
	if (retval)
		*dma = virt_to_phys(retval);
#else
	if (ep->dma) {
		/* the main problem with this call is that it wastes memory
		 * on typical 1/N page allocations: it allocates 1-N pages.
		 */
#warning Using dma_alloc_coherent even with buffers smaller than a page.
		retval = dma_alloc_coherent(&ep->dev->pdev->dev,
		retval = dma_alloc_coherent(&ep->dev->pdev->dev,
				bytes, dma, gfp_flags);
				bytes, dma, gfp_flags);
	} else
	else
		retval = kmalloc(bytes, gfp_flags);
		retval = kmalloc(bytes, gfp_flags);
#endif
	return retval;
	return retval;
}
}


static DEFINE_SPINLOCK(buflock);
static LIST_HEAD(buffers);

struct free_record {
	struct list_head	list;
	struct device		*dev;
	unsigned		bytes;
	dma_addr_t		dma;
};

static void do_free(unsigned long ignored)
{
	spin_lock_irq(&buflock);
	while (!list_empty(&buffers)) {
		struct free_record	*buf;

		buf = list_entry(buffers.next, struct free_record, list);
		list_del(&buf->list);
		spin_unlock_irq(&buflock);

		dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);

		spin_lock_irq(&buflock);
	}
	spin_unlock_irq(&buflock);
}

static DECLARE_TASKLET(deferred_free, do_free, 0);

static void
static void
net2280_free_buffer (
net2280_free_buffer (
	struct usb_ep *_ep,
	struct usb_ep *_ep,
	void *buf,
	void *address,
	dma_addr_t dma,
	dma_addr_t dma,
	unsigned bytes
	unsigned bytes
) {
) {
	/* free memory into the right allocator */
	/* free memory into the right allocator */
#ifndef	USE_KMALLOC
	if (dma != DMA_ADDR_INVALID) {
	if (dma != DMA_ADDR_INVALID) {
		struct net2280_ep	*ep;
		struct net2280_ep	*ep;
		struct free_record	*buf = address;
		unsigned long		flags;


		ep = container_of(_ep, struct net2280_ep, ep);
		ep = container_of(_ep, struct net2280_ep, ep);
		if (!_ep)
		if (!_ep)
			return;
			return;
		dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);

		ep = container_of (_ep, struct net2280_ep, ep);
		buf->dev = &ep->dev->pdev->dev;
		buf->bytes = bytes;
		buf->dma = dma;

		spin_lock_irqsave(&buflock, flags);
		list_add_tail(&buf->list, &buffers);
		tasklet_schedule(&deferred_free);
		spin_unlock_irqrestore(&buflock, flags);
	} else
	} else
#endif
		kfree (address);
		kfree (buf);
}
}


/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
@@ -737,7 +749,8 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
	 */
	 */
	if (ep->is_in)
	if (ep->is_in)
		dmacount |= (1 << DMA_DIRECTION);
		dmacount |= (1 << DMA_DIRECTION);
	if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || ep->dev->pdev->device != 0x2280)
	if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)
			|| ep->dev->pdev->device != 0x2280)
		dmacount |= (1 << END_OF_CHAIN);
		dmacount |= (1 << END_OF_CHAIN);


	req->valid = valid;
	req->valid = valid;
@@ -1373,7 +1386,7 @@ net2280_fifo_flush (struct usb_ep *_ep)
	(void) readl (&ep->regs->ep_rsp);
	(void) readl (&ep->regs->ep_rsp);
}
}


static struct usb_ep_ops net2280_ep_ops = {
static const struct usb_ep_ops net2280_ep_ops = {
	.enable		= net2280_enable,
	.enable		= net2280_enable,
	.disable	= net2280_disable,
	.disable	= net2280_disable,


@@ -2241,7 +2254,8 @@ static void handle_ep_small (struct net2280_ep *ep)
				req->td->dmacount = 0;
				req->td->dmacount = 0;
				t = readl (&ep->regs->ep_avail);
				t = readl (&ep->regs->ep_avail);
				dma_done (ep, req, count,
				dma_done (ep, req, count,
					(ep->out_overflow || t) ? -EOVERFLOW : 0);
					(ep->out_overflow || t)
						? -EOVERFLOW : 0);
			}
			}


			/* also flush to prevent erratum 0106 trouble */
			/* also flush to prevent erratum 0106 trouble */
@@ -2583,9 +2597,11 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
	 */
	 */
	if (stat & tmp) {
	if (stat & tmp) {
		writel (tmp, &dev->regs->irqstat1);
		writel (tmp, &dev->regs->irqstat1);
		if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) && 
		if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
				((readl (&dev->usb->usbstat) & mask) == 0))
					&& ((readl (&dev->usb->usbstat) & mask)
				|| ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0) 
							== 0))
				|| ((readl (&dev->usb->usbctl)
					& (1 << VBUS_PIN)) == 0)
			    ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
			    ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
			DEBUG (dev, "disconnect %s\n",
			DEBUG (dev, "disconnect %s\n",
					dev->driver->driver.name);
					dev->driver->driver.name);
@@ -2870,6 +2886,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
	}
	}
	dev->region = 1;
	dev->region = 1;


	/* FIXME provide firmware download interface to put
	 * 8051 code into the chip, e.g. to turn on PCI PM.
	 */

	base = ioremap_nocache (resource, len);
	base = ioremap_nocache (resource, len);
	if (base == NULL) {
	if (base == NULL) {
		DEBUG (dev, "can't map memory\n");
		DEBUG (dev, "can't map memory\n");
@@ -2984,7 +3004,7 @@ static void net2280_shutdown (struct pci_dev *pdev)


/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/


static struct pci_device_id pci_ids [] = { {
static const struct pci_device_id pci_ids [] = { {
	.class =	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
	.class =	((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
	.class_mask =	~0,
	.class_mask =	~0,
	.vendor =	0x17cc,
	.vendor =	0x17cc,