cregit-Linux how code gets into the kernel

Release 4.11 drivers/usb/gadget/udc/net2280.c

/*
 * Driver for the PLX NET2280 USB device controller.
 * Specs and errata are available from <http://www.plxtech.com>.
 *
 * PLX Technology Inc. (formerly NetChip Technology) supported the
 * development of this driver.
 *
 *
 * CODE STATUS HIGHLIGHTS
 *
 * This driver should work well with most "gadget" drivers, including
 * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
 * as well as Gadget Zero and Gadgetfs.
 *
 * DMA is enabled by default.
 *
 * MSI is enabled by default.  The legacy IRQ is used if MSI couldn't
 * be enabled.
 *
 * Note that almost all the errata workarounds here are only needed for
 * rev1 chips.  Rev1a silicon (0110) fixes almost all of them.
 */

/*
 * Copyright (C) 2003 David Brownell
 * Copyright (C) 2003-2005 PLX Technology, Inc.
 * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
 *
 * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
 *      with 2282 chip
 *
 * Modified Ricardo Ribalda Qtechnology AS  to provide compatibility
 *      with usb 338x chip. Based on PLX driver
 *
 * 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
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/prefetch.h>
#include <linux/io.h>

#include <asm/byteorder.h>
#include <asm/irq.h>
#include <asm/unaligned.h>


#define	DRIVER_DESC		"PLX NET228x/USB338x USB Peripheral Controller"

#define	DRIVER_VERSION		"2005 Sept 27/v3.0"


#define	EP_DONTUSE		13	
/* nonzero */


#define USE_RDK_LEDS		
/* GPIO pins control three LEDs */



static const char driver_name[] = "net2280";

static const char driver_desc[] = DRIVER_DESC;


static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 };

static const char ep0name[] = "ep0";


#define EP_INFO(_name, _caps) \
	{ \
                .name = _name, \
                .caps = _caps, \
        }

static const struct {
	
const char *name;
	
const struct usb_ep_caps caps;
} 
ep_info_dft[] = { /* Default endpoint configuration */
	EP_INFO(ep0name,
		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-a",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-b",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-c",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-d",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-e",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-f",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-g",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep-h",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)),
}
, ep_info_adv[] = { /* Endpoints for usb3380 advance mode */
	EP_INFO(ep0name,
		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
	EP_INFO("ep1in",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
	EP_INFO("ep2out",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
	EP_INFO("ep3in",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
	EP_INFO("ep4out",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
	EP_INFO("ep1out",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
	EP_INFO("ep2in",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
	EP_INFO("ep3out",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)),
	EP_INFO("ep4in",
		USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)),
};


#undef EP_INFO

/* mode 0 == ep-{a,b,c,d} 1K fifo each
 * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
 * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
 */

static ushort fifo_mode;

/* "modprobe net2280 fifo_mode=1" etc */
module_param(fifo_mode, ushort, 0644);

/* enable_suspend -- When enabled, the driver will respond to
 * USB suspend requests by powering down the NET2280.  Otherwise,
 * USB suspend requests will be ignored.  This is acceptable for
 * self-powered devices
 */

static bool enable_suspend;

/* "modprobe net2280 enable_suspend=1" etc */
module_param(enable_suspend, bool, 0444);


#define	DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")


static char *type_string(u8 bmAttributes) { switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: return "bulk"; case USB_ENDPOINT_XFER_ISOC: return "iso"; case USB_ENDPOINT_XFER_INT: return "intr"; } return "control"; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell41100.00%1100.00%
Total41100.00%1100.00%

#include "net2280.h" #define valid_bit cpu_to_le32(BIT(VALID_BIT)) #define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) static void ep_clear_seqnum(struct net2280_ep *ep); static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver); static void ep0_start(struct net2280 *dev); /*-------------------------------------------------------------------------*/
static inline void enable_pciirqenb(struct net2280_ep *ep) { u32 tmp = readl(&ep->dev->regs->pciirqenb0); if (ep->dev->quirks & PLX_LEGACY) tmp |= BIT(ep->num); else tmp |= BIT(ep_bit[ep->num]); writel(tmp, &ep->dev->regs->pciirqenb0); return; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado74100.00%3100.00%
Total74100.00%3100.00%


static int net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct net2280 *dev; struct net2280_ep *ep; u32 max; u32 tmp = 0; u32 type; unsigned long flags; static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; int ret = 0; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) { pr_err("%s: failed at line=%d\n", __func__, __LINE__); return -EINVAL; } dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { ret = -ESHUTDOWN; goto print_err; } /* erratum 0119 workaround ties up an endpoint number */ if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) { ret = -EDOM; goto print_err; } if (dev->quirks & PLX_PCIE) { if ((desc->bEndpointAddress & 0x0f) >= 0x0c) { ret = -EDOM; goto print_err; } ep->is_in = !!usb_endpoint_dir_in(desc); if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) { ret = -EINVAL; goto print_err; } } /* sanity check ep-e/ep-f since their fifos are small */ max = usb_endpoint_maxp(desc); if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) { ret = -ERANGE; goto print_err; } spin_lock_irqsave(&dev->lock, flags); _ep->maxpacket = max; ep->desc = desc; /* ep_reset() has already been called */ ep->stopped = 0; ep->wedged = 0; ep->out_overflow = 0; /* set speed-dependent max packet; may kick in high bandwidth */ set_max_speed(ep, max); /* set type, direction, address; reset fifo counters */ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) { tmp = readl(&ep->cfg->ep_cfg); /* If USB ep number doesn't match hardware ep number */ if ((tmp & 0xf) != usb_endpoint_num(desc)) { ret = -EINVAL; spin_unlock_irqrestore(&dev->lock, flags); goto print_err; } if (ep->is_in) tmp &= ~USB3380_EP_CFG_MASK_IN; else tmp &= ~USB3380_EP_CFG_MASK_OUT; } type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); if (type == USB_ENDPOINT_XFER_INT) { /* erratum 0105 workaround prevents hs NYET */ if (dev->chiprev == 0100 && dev->gadget.speed == USB_SPEED_HIGH && !(desc->bEndpointAddress & USB_DIR_IN)) writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } else if (type == USB_ENDPOINT_XFER_BULK) { /* catch some particularly blatant driver bugs */ if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { spin_unlock_irqrestore(&dev->lock, flags); ret = -ERANGE; goto print_err; } } ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC); /* Enable this endpoint */ if (dev->quirks & PLX_LEGACY) { tmp |= type << ENDPOINT_TYPE; tmp |= desc->bEndpointAddress; /* default full fifo lines */ tmp |= (4 << ENDPOINT_BYTE_COUNT); tmp |= BIT(ENDPOINT_ENABLE); ep->is_in = (tmp & USB_DIR_IN) != 0; } else { /* In Legacy mode, only OUT endpoints are used */ if (dev->enhanced_mode && ep->is_in) { tmp |= type << IN_ENDPOINT_TYPE; tmp |= BIT(IN_ENDPOINT_ENABLE); } else { tmp |= type << OUT_ENDPOINT_TYPE; tmp |= BIT(OUT_ENDPOINT_ENABLE); tmp |= (ep->is_in << ENDPOINT_DIRECTION); } tmp |= (4 << ENDPOINT_BYTE_COUNT); if (!dev->enhanced_mode) tmp |= usb_endpoint_num(desc); tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); } /* Make sure all the registers are written before ep_rsp*/ wmb(); /* for OUT transfers, block the rx fifo until a read is posted */ if (!ep->is_in) writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); else if (!(dev->quirks & PLX_2280)) { /* Added for 2282, Don't use nak packets on an in endpoint, * this was ignored on 2280 */ writel(BIT(CLEAR_NAK_OUT_PACKETS) | BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } if (dev->quirks & PLX_PCIE) ep_clear_seqnum(ep); writel(tmp, &ep->cfg->ep_cfg); /* enable irqs */ if (!ep->dma) { /* pio, per-packet */ enable_pciirqenb(ep); tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) | BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); if (dev->quirks & PLX_2280) tmp |= readl(&ep->regs->ep_irqenb); writel(tmp, &ep->regs->ep_irqenb); } else { /* dma, per-request */ tmp = BIT((8 + ep->num)); /* completion */ tmp |= readl(&dev->regs->pciirqenb1); writel(tmp, &dev->regs->pciirqenb1); /* for short OUT transfers, dma completions can't * advance the queue; do it pio-style, by hand. * NOTE erratum 0112 workaround #2 */ if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); writel(tmp, &ep->regs->ep_irqenb); enable_pciirqenb(ep); } } tmp = desc->bEndpointAddress; ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n", _ep->name, tmp & 0x0f, DIR_STRING(tmp), type_string(desc->bmAttributes), ep->dma ? "dma" : "pio", max); /* pci writes may still be posted */ spin_unlock_irqrestore(&dev->lock, flags); return ret; print_err: dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell52551.32%317.65%
Ricardo Ribalda Delgado24023.46%529.41%
Mian Yousaf Kaukab21020.53%423.53%
Greg Kroah-Hartman262.54%15.88%
Guennadi Liakhovetski121.17%15.88%
Alan Stern60.59%15.88%
Tim Harvey30.29%15.88%
Kuninori Morimoto10.10%15.88%
Total1023100.00%17100.00%


static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec) { u32 result; do { result = readl(ptr); if (result == ~(u32)0) /* "device unplugged" */ return -ENODEV; result &= mask; if (result == done) return 0; udelay(1); usec--; } while (usec > 0); return -ETIMEDOUT; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell7998.75%150.00%
Al Viro11.25%150.00%
Total80100.00%2100.00%

static const struct usb_ep_ops net2280_ep_ops;
static void ep_reset_228x(struct net2280_regs __iomem *regs, struct net2280_ep *ep) { u32 tmp; ep->desc = NULL; INIT_LIST_HEAD(&ep->queue); usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.ops = &net2280_ep_ops; /* disable the dma, irqs, endpoint... */ if (ep->dma) { writel(0, &ep->dma->dmactl); writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | BIT(DMA_TRANSACTION_DONE_INTERRUPT) | BIT(DMA_ABORT), &ep->dma->dmastat); tmp = readl(&regs->pciirqenb0); tmp &= ~BIT(ep->num); writel(tmp, &regs->pciirqenb0); } else { tmp = readl(&regs->pciirqenb1); tmp &= ~BIT((8 + ep->num)); /* completion */ writel(tmp, &regs->pciirqenb1); } writel(0, &ep->regs->ep_irqenb); /* init to our chosen defaults, notably so that we NAK OUT * packets until the driver queues a read (+note erratum 0112) */ if (!ep->is_in || (ep->dev->quirks & PLX_2280)) { tmp = BIT(SET_NAK_OUT_PACKETS_MODE) | BIT(SET_NAK_OUT_PACKETS) | BIT(CLEAR_EP_HIDE_STATUS_PHASE) | BIT(CLEAR_INTERRUPT_MODE); } else { /* added for 2282 */ tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) | BIT(CLEAR_NAK_OUT_PACKETS) | BIT(CLEAR_EP_HIDE_STATUS_PHASE) | BIT(CLEAR_INTERRUPT_MODE); } if (ep->num != 0) { tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) | BIT(CLEAR_ENDPOINT_HALT); } writel(tmp, &ep->regs->ep_rsp); /* scrub most status bits, and flush any fifo state */ if (ep->dev->quirks & PLX_2280) tmp = BIT(FIFO_OVERFLOW) | BIT(FIFO_UNDERFLOW); else tmp = 0; writel(tmp | BIT(TIMEOUT) | BIT(USB_STALL_SENT) | BIT(USB_IN_NAK_SENT) | BIT(USB_IN_ACK_RCVD) | BIT(USB_OUT_PING_NAK_SENT) | BIT(USB_OUT_ACK_SENT) | BIT(FIFO_FLUSH) | BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | BIT(DATA_PACKET_RECEIVED_INTERRUPT) | BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); /* fifo size is handled separately */ }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell22858.76%222.22%
Ricardo Ribalda Delgado9925.52%333.33%
Guennadi Liakhovetski5413.92%111.11%
Robert Baldyga51.29%111.11%
Al Viro20.52%222.22%
Total388100.00%9100.00%


static void ep_reset_338x(struct net2280_regs __iomem *regs, struct net2280_ep *ep) { u32 tmp, dmastat; ep->desc = NULL; INIT_LIST_HEAD(&ep->queue); usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.ops = &net2280_ep_ops; /* disable the dma, irqs, endpoint... */ if (ep->dma) { writel(0, &ep->dma->dmactl); writel(BIT(DMA_ABORT_DONE_INTERRUPT) | BIT(DMA_PAUSE_DONE_INTERRUPT) | BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | BIT(DMA_TRANSACTION_DONE_INTERRUPT), /* | BIT(DMA_ABORT), */ &ep->dma->dmastat); dmastat = readl(&ep->dma->dmastat); if (dmastat == 0x5002) { ep_warn(ep->dev, "The dmastat return = %x!!\n", dmastat); writel(0x5a, &ep->dma->dmastat); } tmp = readl(&regs->pciirqenb0); tmp &= ~BIT(ep_bit[ep->num]); writel(tmp, &regs->pciirqenb0); } else { if (ep->num < 5) { tmp = readl(&regs->pciirqenb1); tmp &= ~BIT((8 + ep->num)); /* completion */ writel(tmp, &regs->pciirqenb1); } } writel(0, &ep->regs->ep_irqenb); writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | BIT(FIFO_OVERFLOW) | BIT(DATA_PACKET_RECEIVED_INTERRUPT) | BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); tmp = readl(&ep->cfg->ep_cfg); if (ep->is_in) tmp &= ~USB3380_EP_CFG_MASK_IN; else tmp &= ~USB3380_EP_CFG_MASK_OUT; writel(tmp, &ep->cfg->ep_cfg); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado24373.64%562.50%
Mian Yousaf Kaukab4112.42%112.50%
David Brownell288.48%112.50%
Greg Kroah-Hartman185.45%112.50%
Total330100.00%8100.00%

static void nuke(struct net2280_ep *);
static int net2280_disable(struct usb_ep *_ep) { struct net2280_ep *ep; unsigned long flags; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || !ep->desc || _ep->name == ep0name) { pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; } spin_lock_irqsave(&ep->dev->lock, flags); nuke(ep); if (ep->dev->quirks & PLX_PCIE) ep_reset_338x(ep->dev->regs, ep); else ep_reset_228x(ep->dev->regs, ep); ep_vdbg(ep->dev, "disabled %s %s\n", ep->dma ? "dma" : "pio", _ep->name); /* synch memory views with the device */ (void)readl(&ep->cfg->ep_cfg); if (!ep->dma && ep->num >= 1 && ep->num <= 4) ep->dma = &ep->dev->dma[ep->num - 1]; spin_unlock_irqrestore(&ep->dev->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado16381.50%342.86%
David Brownell2311.50%114.29%
Mian Yousaf Kaukab115.50%114.29%
Greg Kroah-Hartman21.00%114.29%
Tim Harvey10.50%114.29%
Total200100.00%7100.00%

/*-------------------------------------------------------------------------*/
static struct usb_request *net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct net2280_ep *ep; struct net2280_request *req; if (!_ep) { pr_err("%s: Invalid ep\n", __func__); return NULL; } ep = container_of(_ep, struct net2280_ep, ep); req = kzalloc(sizeof(*req), gfp_flags); if (!req) return NULL; INIT_LIST_HEAD(&req->queue); /* this dma descriptor may be swapped with the previous dummy */ if (ep->dma) { struct net2280_dma *td; td = pci_pool_alloc(ep->dev->requests, gfp_flags, &req->td_dma); if (!td) { kfree(req); return NULL; } td->dmacount = 0; /* not VALID */ td->dmadesc = td->dmaaddr; req->td = td; } return &req->req; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell9157.59%228.57%
Ricardo Ribalda Delgado5534.81%228.57%
Mian Yousaf Kaukab95.70%114.29%
Al Viro21.27%114.29%
Greg Kroah-Hartman10.63%114.29%
Total158100.00%7100.00%


static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) { struct net2280_ep *ep; struct net2280_request *req; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || !_req) { dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n", __func__, _ep, _req); return; } req = container_of(_req, struct net2280_request, req); WARN_ON(!list_empty(&req->queue)); if (req->td) pci_pool_free(ep->dev->requests, req->td, req->td_dma); kfree(req); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell9981.82%250.00%
Mian Yousaf Kaukab2117.36%125.00%
Colin Ian King10.83%125.00%
Total121100.00%4100.00%

/*-------------------------------------------------------------------------*/ /* load a packet into the fifo we use for usb IN transfers. * works for all endpoints. * * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo * at a time, but this code is simpler because it knows it only writes * one packet. ep-a..ep-d should use dma instead. */
static void write_fifo(struct net2280_ep *ep, struct usb_request *req) { struct net2280_ep_regs __iomem *regs = ep->regs; u8 *buf; u32 tmp; unsigned count, total; /* INVARIANT: fifo is currently empty. (testable) */ if (req) { buf = req->buf + req->actual; prefetch(buf); total = req->length - req->actual; } else { total = 0; buf = NULL; } /* write just one packet at a time */ count = ep->ep.maxpacket; if (count > total) /* min() cannot be used on a bitfield */ count = total; ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", ep->ep.name, count, (count != ep->ep.maxpacket) ? " (short)" : "", req); while (count >= 4) { /* NOTE be careful if you try to align these. fifo lines * should normally be full (4 bytes) and successive partial * lines are ok only in certain cases. */ tmp = get_unaligned((u32 *)buf); cpu_to_le32s(&tmp); writel(tmp, &regs->ep_data); buf += 4; count -= 4; } /* last fifo entry is "short" unless we wrote a full packet. * also explicitly validate last word in (periodic) transfers * when maxpacket is not a multiple of 4 bytes. */ if (count || total < ep->ep.maxpacket) { tmp = count ? get_unaligned((u32 *)buf) : count; cpu_to_le32s(&tmp); set_fifo_bytecount(ep, count & 0x03); writel(tmp, &regs->ep_data); } /* pci writes may still be posted */ }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell22194.44%450.00%
Andrew Morton104.27%112.50%
Al Viro20.85%225.00%
Ricardo Ribalda Delgado10.43%112.50%
Total234100.00%8100.00%

/* work around erratum 0106: PCI and USB race over the OUT fifo. * caller guarantees chiprev 0100, out endpoint is NAKing, and * there's no real data in the fifo. * * NOTE: also used in cases where that erratum doesn't apply: * where the host wrote "too much" data to us. */
static void out_flush(struct net2280_ep *ep) { u32 __iomem *statp; u32 tmp; statp = &ep->regs->ep_stat; tmp = readl(statp); if (tmp & BIT(NAK_OUT_PACKETS)) { ep_dbg(ep->dev, "%s %s %08x !NAK\n", ep->ep.name, __func__, tmp); writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); } writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_PACKET_RECEIVED_INTERRUPT), statp); writel(BIT(FIFO_FLUSH), statp); /* Make sure that stap is written */ mb(); tmp = readl(statp); if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) && /* high speed did bulk NYET; fifo isn't filling */ ep->dev->gadget.speed == USB_SPEED_FULL) { unsigned usec; usec = 50; /* 64 byte bulk/interrupt */ handshake(statp, BIT(USB_OUT_PING_NAK_SENT), BIT(USB_OUT_PING_NAK_SENT), usec); /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ } }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell9054.88%228.57%
Ricardo Ribalda Delgado7143.29%457.14%
Al Viro31.83%114.29%
Total164100.00%7100.00%

/* unload packet(s) from the fifo we use for usb OUT transfers. * returns true iff the request completed, because of short packet * or the request buffer having filled with full packets. * * for ep-a..ep-d this will read multiple packets out when they * have been accepted. */
static int read_fifo(struct net2280_ep *ep, struct net2280_request *req) { struct net2280_ep_regs __iomem *regs = ep->regs; u8 *buf = req->req.buf + req->req.actual; unsigned count, tmp, is_short; unsigned cleanup = 0, prevent = 0; /* erratum 0106 ... packets coming in during fifo reads might * be incompletely rejected. not all cases have workarounds. */ if (ep->dev->chiprev == 0x0100 && ep->dev->gadget.speed == USB_SPEED_FULL) { udelay(1); tmp = readl(&ep->regs->ep_stat); if ((tmp & BIT(NAK_OUT_PACKETS))) cleanup = 1; else if ((tmp & BIT(FIFO_FULL))) { start_out_naking(ep); prevent = 1; } /* else: hope we don't see the problem */ } /* never overflow the rx buffer. the fifo reads packets until * it sees a short one; we might not be ready for them all. */ prefetchw(buf); count = readl(&regs->ep_avail); if (unlikely(count == 0)) { udelay(1); tmp = readl(&ep->regs->ep_stat); count = readl(&regs->ep_avail); /* handled that data already? */ if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0) return 0; } tmp = req->req.length - req->req.actual; if (count > tmp) { /* as with DMA, data overflow gets flushed */ if ((tmp % ep->ep.maxpacket) != 0) { ep_err(ep->dev, "%s out fifo %d bytes, expected %d\n", ep->ep.name, count, tmp); req->req.status = -EOVERFLOW; cleanup = 1; /* NAK_OUT_PACKETS will be set, so flushing is safe; * the next read will start with the next packet */ } /* else it's a ZLP, no worries */ count = tmp; } req->req.actual += count; is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", ep->ep.name, count, is_short ? " (short)" : "", cleanup ? " flush" : "", prevent ? " nak" : "", req, req->req.actual, req->req.length); while (count >= 4) { tmp = readl(&regs->ep_data); cpu_to_le32s(&tmp); put_unaligned(tmp, (u32 *)buf); buf += 4; count -= 4; } if (count) { tmp = readl(&regs->ep_data); /* LE conversion is implicit here: */ do { *buf++ = (u8) tmp; tmp >>= 8; } while (--count); } if (cleanup) out_flush(ep); if (prevent) { writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); (void) readl(&ep->regs->ep_rsp); } return is_short || ((req->req.actual == req->req.length) && !req->req.zero); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell50497.11%666.67%
Ricardo Ribalda Delgado142.70%222.22%
Al Viro10.19%111.11%
Total519100.00%9100.00%

/* fill out dma descriptor to match a given request */
static void fill_dma_desc(struct net2280_ep *ep, struct net2280_request *req, int valid) { struct net2280_dma *td = req->td; u32 dmacount = req->req.length; /* don't let DMA continue after a short OUT packet, * so overruns can't affect the next transfer. * in case of overruns on max-size packets, we can't * stop the fifo from filling but we can flush it. */ if (ep->is_in) dmacount |= BIT(DMA_DIRECTION); if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || !(ep->dev->quirks & PLX_2280)) dmacount |= BIT(END_OF_CHAIN); req->valid = valid; if (valid) dmacount |= BIT(VALID_BIT); dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE); /* td->dmadesc = previously set by caller */ td->dmaaddr = cpu_to_le32 (req->req.dma); /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ wmb(); td->dmacount = cpu_to_le32(dmacount); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell11078.01%450.00%
Ricardo Ribalda Delgado1812.77%225.00%
Guennadi Liakhovetski128.51%112.50%
Harvey Harrison10.71%112.50%
Total141100.00%8100.00%

static const u32 dmactl_default = BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | BIT(DMA_CLEAR_COUNT_ENABLE) | /* erratum 0116 workaround part 1 (use POLLING) */ (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) | BIT(DMA_VALID_BIT_POLLING_ENABLE) | BIT(DMA_VALID_BIT_ENABLE) | BIT(DMA_SCATTER_GATHER_ENABLE) | /* erratum 0116 workaround part 2 (no AUTOSTART) */ BIT(DMA_ENABLE);
static inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma) { handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell2686.67%133.33%
Ricardo Ribalda Delgado310.00%133.33%
Al Viro13.33%133.33%
Total30100.00%3100.00%


static inline void stop_dma(struct net2280_dma_regs __iomem *dma) { writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl); spin_stop_dma(dma); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell3690.00%250.00%
Ricardo Ribalda Delgado37.50%125.00%
Al Viro12.50%125.00%
Total40100.00%4100.00%


static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma) { struct net2280_dma_regs __iomem *dma = ep->dma; unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION); if (!(ep->dev->quirks & PLX_2280)) tmp |= BIT(END_OF_CHAIN); writel(tmp, &dma->dmacount); writel(readl(&dma->dmastat), &dma->dmastat); writel(td_dma, &dma->dmadesc); if (ep->dev->quirks & PLX_PCIE) dmactl |= BIT(DMA_REQUEST_OUTSTANDING); writel(dmactl, &dma->dmactl); /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ (void) readl(&ep->dev->pci->pcimstctl); writel(BIT(DMA_START), &dma->dmastat); if (!ep->is_in) stop_out_naking(ep); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell11669.05%114.29%
Ricardo Ribalda Delgado3118.45%342.86%
Guennadi Liakhovetski1911.31%114.29%
Al Viro10.60%114.29%
Tim Harvey10.60%114.29%
Total168100.00%7100.00%


static void start_dma(struct net2280_ep *ep, struct net2280_request *req) { u32 tmp; struct net2280_dma_regs __iomem *dma = ep->dma; /* FIXME can't use DMA for ZLPs */ /* on this path we "know" there's no dma active (yet) */ WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE)); writel(0, &ep->dma->dmactl); /* previous OUT packet might have been short */ if (!ep->is_in && (readl(&ep->regs->ep_stat) & BIT(NAK_OUT_PACKETS))) { writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT), &ep->regs->ep_stat); tmp = readl(&ep->regs->ep_avail); if (tmp) { writel(readl(&dma->dmastat), &dma->dmastat); /* transfer all/some fifo data */ writel(req->req.dma, &dma->dmaaddr); tmp = min(tmp, req->req.length); /* dma irq, faking scatterlist status */ req->td->dmacount = cpu_to_le32(req->req.length - tmp); writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp, &dma->dmacount); req->td->dmadesc = 0; req->valid = 1; writel(BIT(DMA_ENABLE), &dma->dmactl); writel(BIT(DMA_START), &dma->dmastat); return; } } tmp = dmactl_default; /* force packet boundaries between dma requests, but prevent the * controller from automagically writing a last "short" packet * (zero length) unless the driver explicitly said to do that. */ if (ep->is_in) { if (likely((req->req.length % ep->ep.maxpacket) || req->req.zero)){ tmp |= BIT(DMA_FIFO_VALIDATE); ep->in_fifo_validate = 1; } else ep->in_fifo_validate = 0; } /* init req->td, pointing to the current dummy */ req->td->dmadesc = cpu_to_le32 (ep->td_dma); fill_dma_desc(ep, req, 1); req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN)); start_queue(ep, tmp, req->td_dma); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell31992.46%240.00%
Ricardo Ribalda Delgado246.96%120.00%
Al Viro10.29%120.00%
Harvey Harrison10.29%120.00%
Total345100.00%5100.00%


static inline void queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid) { struct net2280_dma *end; dma_addr_t tmp; /* swap new dummy for old, link; fill and maybe activate */ end = ep->dummy; ep->dummy = req->td; req->td = end; tmp = ep->td_dma; ep->td_dma = req->td_dma; req->td_dma = tmp; end->dmadesc = cpu_to_le32 (ep->td_dma); fill_dma_desc(ep, req, valid); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell5662.92%133.33%
Ricardo Ribalda Delgado3337.08%266.67%
Total89100.00%3100.00%


static void done(struct net2280_ep *ep, struct net2280_request *req, int status) { struct net2280 *dev; unsigned stopped = ep->stopped; list_del_init(&req->queue); if (req->req.status == -EINPROGRESS) req->req.status = status; else status = req->req.status; dev = ep->dev; if (ep->dma) usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); if (status && status != -ESHUTDOWN) ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n", ep->ep.name, &req->req, status, req->req.actual, req->req.length); /* don't modify queue heads during completion callback */ ep->stopped = 1; spin_unlock(&dev->lock); usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&dev->lock); ep->stopped = stopped; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell17195.53%125.00%
Felipe Balbi63.35%125.00%
Ricardo Ribalda Delgado10.56%125.00%
Michal Sojka10.56%125.00%
Total179100.00%4100.00%

/*-------------------------------------------------------------------------*/
static int net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { struct net2280_request *req; struct net2280_ep *ep; struct net2280 *dev; unsigned long flags; int ret = 0; /* we always require a cpu-view buffer, so that we can * always use pio (as fallback or whatever). */ ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || (!ep->desc && ep->num != 0)) { pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; } req = container_of(_req, struct net2280_request, req); if (!_req || !_req->complete || !_req->buf || !list_empty(&req->queue)) { ret = -EINVAL; goto print_err; } if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) { ret = -EDOM; goto print_err; } dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { ret = -ESHUTDOWN; goto print_err; } /* FIXME implement PIO fallback for ZLPs with DMA */ if (ep->dma && _req->length == 0) { ret = -EOPNOTSUPP; goto print_err; } /* set up dma mapping in case the caller didn't */ if (ep->dma) { ret = usb_gadget_map_request(&dev->gadget, _req, ep->is_in); if (ret) goto print_err; } ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, _req->buf); spin_lock_irqsave(&dev->lock, flags); _req->status = -EINPROGRESS; _req->actual = 0; /* kickstart this i/o queue? */ if (list_empty(&ep->queue) && !ep->stopped && !((dev->quirks & PLX_PCIE) && ep->dma && (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) { /* use DMA if the endpoint supports it, else pio */ if (ep->dma) start_dma(ep, req); else { /* maybe there's no control data, just status ack */ if (ep->num == 0 && _req->length == 0) { allow_status(ep); done(ep, req, 0); ep_vdbg(dev, "%s status ack\n", ep->ep.name); goto done; } /* PIO ... stuff the fifo, or unblock it. */ if (ep->is_in) write_fifo(ep, _req); else if (list_empty(&ep->queue)) { u32 s; /* OUT FIFO might have packet(s) buffered */ s = readl(&ep->regs->ep_stat); if ((s & BIT(FIFO_EMPTY)) == 0) { /* note: _req->short_not_ok is * ignored here since PIO _always_ * stops queue advance here, and * _req->status doesn't change for * short reads (only _req->actual) */ if (read_fifo(ep, req) && ep->num == 0) { done(ep, req, 0); allow_status(ep); /* don't queue it */ req = NULL; } else if (read_fifo(ep, req) && ep->num != 0) { done(ep, req, 0); req = NULL; } else s = readl(&ep->regs->ep_stat); } /* don't NAK, let the fifo fill */ if (req && (s & BIT(NAK_OUT_PACKETS))) writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); } } } else if (ep->dma) { int valid = 1; if (ep->is_in) { int expect; /* preventing magic zlps is per-engine state, not * per-transfer; irq logic must recover hiccups. */ expect = likely(req->req.zero || (req->req.length % ep->ep.maxpacket)); if (expect != ep->in_fifo_validate) valid = 0; } queue_dma(ep, req, valid); } /* else the irq handler advances the queue. */ ep->responded = 1; if (req) list_add_tail(&req->queue, &ep->queue); done: spin_unlock_irqrestore(&dev->lock, flags); /* pci writes may still be posted */ return ret; print_err: dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell48468.65%425.00%
Mian Yousaf Kaukab10815.32%16.25%
Ricardo Ribalda Delgado9613.62%637.50%
Felipe Balbi81.13%16.25%
Alan Stern60.85%16.25%
Al Viro20.28%212.50%
Tim Harvey10.14%16.25%
Total705100.00%16100.00%


static inline void dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, int status) { req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); done(ep, req, status); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell50100.00%1100.00%
Total50100.00%1100.00%


static int scan_dma_completions(struct net2280_ep *ep) { int num_completed = 0; /* only look at descriptors that were "naturally" retired, * so fifo and list head state won't matter */ while (!list_empty(&ep->queue)) { struct net2280_request *req; u32 req_dma_count; req = list_entry(ep->queue.next, struct net2280_request, queue); if (!req->valid) break; rmb(); req_dma_count = le32_to_cpup(&req->td->dmacount); if ((req_dma_count & BIT(VALID_BIT)) != 0) break; /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" * cases where DMA must be aborted; this code handles * all non-abort DMA completions. */ if (unlikely(req->td->dmadesc == 0)) { /* paranoia */ u32 const ep_dmacount = readl(&ep->dma->dmacount); if (ep_dmacount & DMA_BYTE_COUNT_MASK) break; /* single transfer mode */ dma_done(ep, req, req_dma_count, 0); num_completed++; break; } else if (!ep->is_in && (req->req.length % ep->ep.maxpacket) && !(ep->dev->quirks & PLX_PCIE)) { u32 const ep_stat = readl(&ep->regs->ep_stat); /* AVOID TROUBLE HERE by not issuing short reads from * your gadget driver. That helps avoids errata 0121, * 0122, and 0124; not all cases trigger the warning. */ if ((ep_stat & BIT(NAK_OUT_PACKETS)) == 0) { ep_warn(ep->dev, "%s lost packet sync!\n", ep->ep.name); req->req.status = -EOVERFLOW; } else { u32 const ep_avail = readl(&ep->regs->ep_avail); if (ep_avail) { /* fifo gets flushed later */ ep->out_overflow = 1; ep_dbg(ep->dev, "%s dma, discard %d len %d\n", ep->ep.name, ep_avail, req->req.length); req->req.status = -EOVERFLOW; } } } dma_done(ep, req, req_dma_count, 0); num_completed++; } return num_completed; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell23976.36%216.67%
Ricardo Ribalda Delgado257.99%650.00%
Raz Manor247.67%18.33%
Jussi Kivilinna154.79%18.33%
Mario Schuknecht92.88%18.33%
Tim Harvey10.32%18.33%
Total313100.00%12100.00%


static void restart_dma(struct net2280_ep *ep) { struct net2280_request *req; if (ep->stopped) return; req = list_entry(ep->queue.next, struct net2280_request, queue); start_dma(ep, req); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell4597.83%150.00%
Ricardo Ribalda Delgado12.17%150.00%
Total46100.00%2100.00%


static void abort_dma(struct net2280_ep *ep) { /* abort the current transfer */ if (likely(!list_empty(&ep->queue))) { /* FIXME work around errata 0121, 0122, 0124 */ writel(BIT(DMA_ABORT), &ep->dma->dmastat); spin_stop_dma(ep->dma); } else stop_dma(ep->dma); scan_dma_completions(ep); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell6093.75%250.00%
Ricardo Ribalda Delgado46.25%250.00%
Total64100.00%4100.00%

/* dequeue ALL requests */
static void nuke(struct net2280_ep *ep) { struct net2280_request *req; /* called with spinlock held */ ep->stopped = 1; if (ep->dma) abort_dma(ep); while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct net2280_request, queue); done(ep, req, -ESHUTDOWN); } }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell3446.58%125.00%
Ricardo Ribalda Delgado2736.99%250.00%
Greg Kroah-Hartman1216.44%125.00%
Total73100.00%4100.00%

/* dequeue JUST ONE request */
static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct net2280_ep *ep; struct net2280_request *req; unsigned long flags; u32 dmactl; int stopped; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || (!ep->desc && ep->num != 0) || !_req) { pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n", __func__, _ep, _req); return -EINVAL; } spin_lock_irqsave(&ep->dev->lock, flags); stopped = ep->stopped; /* quiesce dma while we patch the queue */ dmactl = 0; ep->stopped = 1; if (ep->dma) { dmactl = readl(&ep->dma->dmactl); /* WARNING erratum 0127 may kick in ... */ stop_dma(ep->dma); scan_dma_completions(ep); } /* make sure it's still queued on this endpoint */ list_for_each_entry(req, &ep->queue, queue) { if (&req->req == _req) break; } if (&req->req != _req) { spin_unlock_irqrestore(&ep->dev->lock, flags); dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n", __func__); return -EINVAL; } /* queue head may be partially complete. */ if (ep->queue.next == &req->queue) { if (ep->dma) { ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name); _req->status = -ECONNRESET; abort_dma(ep); if (likely(ep->queue.next == &req->queue)) { /* NOTE: misreports single-transfer mode*/ req->td->dmacount = 0; /* invalidate */ dma_done(ep, req, readl(&ep->dma->dmacount), -ECONNRESET); } } else { ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name); done(ep, req, -ECONNRESET); } req = NULL; } if (req) done(ep, req, -ECONNRESET); ep->stopped = stopped; if (ep->dma) { /* turn off dma on inactive queues */ if (list_empty(&ep->queue)) stop_dma(ep->dma); else if (!ep->stopped) { /* resume current request, or start new one */ if (req) writel(dmactl, &ep->dma->dmactl); else start_dma(ep, list_entry(ep->queue.next, struct net2280_request, queue)); } } spin_unlock_irqrestore(&ep->dev->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell41492.62%233.33%
Mian Yousaf Kaukab296.49%116.67%
Ricardo Ribalda Delgado30.67%233.33%
Al Viro10.22%116.67%
Total447100.00%6100.00%

/*-------------------------------------------------------------------------*/ static int net2280_fifo_status(struct usb_ep *_ep);
static int net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) { struct net2280_ep *ep; unsigned long flags; int retval = 0; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || (!ep->desc && ep->num != 0)) { pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -EINVAL; } if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { retval = -ESHUTDOWN; goto print_err; } if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC) { retval = -EINVAL; goto print_err; } spin_lock_irqsave(&ep->dev->lock, flags); if (!list_empty(&ep->queue)) { retval = -EAGAIN; goto print_unlock; } else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) { retval = -EAGAIN; goto print_unlock; } else { ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, value ? "set" : "clear", wedged ? "wedge" : "halt"); /* set/clear, then synch memory views with the device */ if (value) { if (ep->num == 0) ep->dev->protocol_stall = 1; else set_halt(ep); if (wedged) ep->wedged = 1; } else { clear_halt(ep); if (ep->dev->quirks & PLX_PCIE && !list_empty(&ep->queue) && ep->td_dma) restart_dma(ep); ep->wedged = 0; } (void) readl(&ep->regs->ep_rsp); } spin_unlock_irqrestore(&ep->dev->lock, flags); return retval; print_unlock: spin_unlock_irqrestore(&ep->dev->lock, flags); print_err: dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell23564.03%440.00%
Mian Yousaf Kaukab7420.16%110.00%
Alan Stern297.90%110.00%
Ricardo Ribalda Delgado287.63%330.00%
Tim Harvey10.27%110.00%
Total367100.00%10100.00%


static int net2280_set_halt(struct usb_ep *_ep, int value) { return net2280_set_halt_and_wedge(_ep, value, 0); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern2291.67%150.00%
David Brownell28.33%150.00%
Total24100.00%2100.00%


static int net2280_set_wedge(struct usb_ep *_ep) { if (!_ep || _ep->name == ep0name) { pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep); return -EINVAL; } return net2280_set_halt_and_wedge(_ep, 1, 1); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern3676.60%150.00%
Mian Yousaf Kaukab1123.40%150.00%
Total47100.00%2100.00%


static int net2280_fifo_status(struct usb_ep *_ep) { struct net2280_ep *ep; u32 avail; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || (!ep->desc && ep->num != 0)) { pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return -ENODEV; } if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { dev_err(&ep->dev->pdev->dev, "%s: Invalid driver=%p or speed=%d\n", __func__, ep->dev->driver, ep->dev->gadget.speed); return -ESHUTDOWN; } avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1); if (avail > ep->fifo_size) { dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__); return -EOVERFLOW; } if (ep->is_in) avail = ep->fifo_size - avail; return avail; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell12164.71%240.00%
Mian Yousaf Kaukab6132.62%120.00%
Ricardo Ribalda Delgado31.60%120.00%
Alan Stern21.07%120.00%
Total187100.00%5100.00%


static void net2280_fifo_flush(struct usb_ep *_ep) { struct net2280_ep *ep; ep = container_of(_ep, struct net2280_ep, ep); if (!_ep || (!ep->desc && ep->num != 0)) { pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); return; } if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { dev_err(&ep->dev->pdev->dev, "%s: Invalid driver=%p or speed=%d\n", __func__, ep->dev->driver, ep->dev->gadget.speed); return; } writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); (void) readl(&ep->regs->ep_rsp); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell9266.67%133.33%
Mian Yousaf Kaukab4331.16%133.33%
Ricardo Ribalda Delgado32.17%133.33%
Total138100.00%3100.00%

static const struct usb_ep_ops net2280_ep_ops = { .enable = net2280_enable, .disable = net2280_disable, .alloc_request = net2280_alloc_request, .free_request = net2280_free_request, .queue = net2280_queue, .dequeue = net2280_dequeue, .set_halt = net2280_set_halt, .set_wedge = net2280_set_wedge, .fifo_status = net2280_fifo_status, .fifo_flush = net2280_fifo_flush, }; /*-------------------------------------------------------------------------*/
static int net2280_get_frame(struct usb_gadget *_gadget) { struct net2280 *dev; unsigned long flags; u16 retval; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct net2280, gadget); spin_lock_irqsave(&dev->lock, flags); retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff; spin_unlock_irqrestore(&dev->lock, flags); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell80100.00%1100.00%
Total80100.00%1100.00%


static int net2280_wakeup(struct usb_gadget *_gadget) { struct net2280 *dev; u32 tmp; unsigned long flags; if (!_gadget) return 0; dev = container_of(_gadget, struct net2280, gadget); spin_lock_irqsave(&dev->lock, flags); tmp = readl(&dev->usb->usbctl); if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE)) writel(BIT(GENERATE_RESUME), &dev->usb->usbstat); spin_unlock_irqrestore(&dev->lock, flags); /* pci writes may still be posted */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell9794.17%266.67%
Ricardo Ribalda Delgado65.83%133.33%
Total103100.00%3100.00%


static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value) { struct net2280 *dev; u32 tmp; unsigned long flags; if (!_gadget) return 0; dev = container_of(_gadget, struct net2280, gadget); spin_lock_irqsave(&dev->lock, flags); tmp = readl(&dev->usb->usbctl); if (value) { tmp |= BIT(SELF_POWERED_STATUS); _gadget->is_selfpowered = 1; } else { tmp &= ~BIT(SELF_POWERED_STATUS); _gadget->is_selfpowered = 0; } writel(tmp, &dev->usb->usbctl); spin_unlock_irqrestore(&dev->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell10682.17%233.33%
Ricardo Ribalda Delgado1813.95%233.33%
Peter Chen43.10%116.67%
Andrew Morton10.78%116.67%
Total129100.00%6100.00%


static int net2280_pullup(struct usb_gadget *_gadget, int is_on) { struct net2280 *dev; u32 tmp; unsigned long flags; if (!_gadget) return -ENODEV; dev = container_of(_gadget, struct net2280, gadget); spin_lock_irqsave(&dev->lock, flags); tmp = readl(&dev->usb->usbctl); dev->softconnect = (is_on != 0); if (is_on) { ep0_start(dev); writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); } else { writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); stop_activity(dev, dev->driver); } spin_unlock_irqrestore(&dev->lock, flags); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell11274.67%133.33%
Mian Yousaf Kaukab3221.33%133.33%
Ricardo Ribalda Delgado64.00%133.33%
Total150100.00%3100.00%


static struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget, struct usb_endpoint_descriptor *desc, struct usb_ss_ep_comp_descriptor *ep_comp) { char name[8]; struct usb_ep *ep; if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT) { /* ep-e, ep-f are PIO with only 64 byte fifos */ ep = gadget_find_ep_by_name(_gadget, "ep-e"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; ep = gadget_find_ep_by_name(_gadget, "ep-f"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; } /* USB3380: Only first four endpoints have DMA channels. Allocate * slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc * endpoints use DMA hw endpoints. */ if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && usb_endpoint_dir_in(desc)) { ep = gadget_find_ep_by_name(_gadget, "ep2in"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; ep = gadget_find_ep_by_name(_gadget, "ep4in"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; } else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && !usb_endpoint_dir_in(desc)) { ep = gadget_find_ep_by_name(_gadget, "ep1out"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; ep = gadget_find_ep_by_name(_gadget, "ep3out"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && usb_endpoint_dir_in(desc)) { ep = gadget_find_ep_by_name(_gadget, "ep1in"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; ep = gadget_find_ep_by_name(_gadget, "ep3in"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && !usb_endpoint_dir_in(desc)) { ep = gadget_find_ep_by_name(_gadget, "ep2out"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; ep = gadget_find_ep_by_name(_gadget, "ep4out"); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; } /* USB3380: use same address for usb and hardware endpoints */ snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), usb_endpoint_dir_in(desc) ? "in" : "out"); ep = gadget_find_ep_by_name(_gadget, name); if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) return ep; return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Jussi Kivilinna28664.56%150.00%
Robert Baldyga15735.44%150.00%
Total443100.00%2100.00%

static int net2280_start(struct usb_gadget *_gadget, struct usb_gadget_driver *driver); static int net2280_stop(struct usb_gadget *_gadget); static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, .set_selfpowered = net2280_set_selfpowered, .pullup = net2280_pullup, .udc_start = net2280_start, .udc_stop = net2280_stop, .match_ep = net2280_match_ep, }; /*-------------------------------------------------------------------------*/ #ifdef CONFIG_USB_GADGET_DEBUG_FILES /* FIXME move these into procfs, and use seq_file. * Sysfs _still_ doesn't behave for arbitrarily sized files, * and also doesn't help products using this with 2.4 kernels. */ /* "function" sysfs attribute */
static ssize_t function_show(struct device *_dev, struct device_attribute *attr, char *buf) { struct net2280 *dev = dev_get_drvdata(_dev); if (!dev->driver || !dev->driver->function || strlen(dev->driver->function) > PAGE_SIZE) return 0; return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell6790.54%125.00%
Yani Ioannou56.76%125.00%
Greg Kroah-Hartman11.35%125.00%
Andrew Morton11.35%125.00%
Total74100.00%4100.00%

static DEVICE_ATTR_RO(function);
static ssize_t registers_show(struct device *_dev, struct device_attribute *attr, char *buf) { struct net2280 *dev; char *next; unsigned size, t; unsigned long flags; int i; u32 t1, t2; const char *s; dev = dev_get_drvdata(_dev); next = buf; size = PAGE_SIZE; spin_lock_irqsave(&dev->lock, flags); if (dev->driver) s = dev->driver->driver.name; else s = "(none)"; /* Main Control Registers */ t = scnprintf(next, size, "%s version " DRIVER_VERSION ", chiprev %04x\n\n" "devinit %03x fifoctl %08x gadget '%s'\n" "pci irqenb0 %02x irqenb1 %08x " "irqstat0 %04x irqstat1 %08x\n", driver_name, dev->chiprev, readl(&dev->regs->devinit), readl(&dev->regs->fifoctl), s, readl(&dev->regs->pciirqenb0), readl(&dev->regs->pciirqenb1), readl(&dev->regs->irqstat0), readl(&dev->regs->irqstat1)); size -= t; next += t; /* USB Control Registers */ t1 = readl(&dev->usb->usbctl); t2 = readl(&dev->usb->usbstat); if (t1 & BIT(VBUS_PIN)) { if (t2 & BIT(HIGH_SPEED)) s = "high speed"; else if (dev->gadget.speed == USB_SPEED_UNKNOWN) s = "powered"; else s = "full speed"; /* full speed bit (6) not working?? */ } else s = "not attached"; t = scnprintf(next, size, "stdrsp %08x usbctl %08x usbstat %08x " "addr 0x%02x (%s)\n", readl(&dev->usb->stdrsp), t1, t2, readl(&dev->usb->ouraddr), s); size -= t; next += t; /* PCI Master Control Registers */ /* DMA Control Registers */ /* Configurable EP Control Registers */ for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep; ep = &dev->ep[i]; if (i && !ep->desc) continue; t1 = readl(&ep->cfg->ep_cfg); t2 = readl(&ep->regs->ep_rsp) & 0xff; t = scnprintf(next, size, "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" "irqenb %02x\n", ep->ep.name, t1, t2, (t2 & BIT(CLEAR_NAK_OUT_PACKETS)) ? "NAK " : "", (t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE)) ? "hide " : "", (t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR)) ? "CRC " : "", (t2 & BIT(CLEAR_INTERRUPT_MODE)) ? "interrupt " : "", (t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) ? "status " : "", (t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE)) ? "NAKmode " : "", (t2 & BIT(CLEAR_ENDPOINT_TOGGLE)) ? "DATA1 " : "DATA0 ", (t2 & BIT(CLEAR_ENDPOINT_HALT)) ? "HALT " : "", readl(&ep->regs->ep_irqenb)); size -= t; next += t; t = scnprintf(next, size, "\tstat %08x avail %04x " "(ep%d%s-%s)%s\n", readl(&ep->regs->ep_stat), readl(&ep->regs->ep_avail), t1 & 0x0f, DIR_STRING(t1), type_string(t1 >> 8), ep->stopped ? "*" : ""); size -= t; next += t; if (!ep->dma) continue; t = scnprintf(next, size, " dma\tctl %08x stat %08x count %08x\n" "\taddr %08x desc %08x\n", readl(&ep->dma->dmactl), readl(&ep->dma->dmastat), readl(&ep->dma->dmacount), readl(&ep->dma->dmaaddr), readl(&ep->dma->dmadesc)); size -= t; next += t; } /* Indexed Registers (none yet) */ /* Statistics */ t = scnprintf(next, size, "\nirqs: "); size -= t; next += t; for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep; ep = &dev->ep[i]; if (i && !ep->irqs) continue; t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs); size -= t; next += t; } t = scnprintf(next, size, "\n"); size -= t; next += t; spin_unlock_irqrestore(&dev->lock, flags); return PAGE_SIZE - size; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell74093.20%327.27%
Ricardo Ribalda Delgado394.91%436.36%
Andrew Morton91.13%218.18%
Yani Ioannou50.63%19.09%
Greg Kroah-Hartman10.13%19.09%
Total794100.00%11100.00%

static DEVICE_ATTR_RO(registers);
static ssize_t queues_show(struct device *_dev, struct device_attribute *attr, char *buf) { struct net2280 *dev; char *next; unsigned size; unsigned long flags; int i; dev = dev_get_drvdata(_dev); next = buf; size = PAGE_SIZE; spin_lock_irqsave(&dev->lock, flags); for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep = &dev->ep[i]; struct net2280_request *req; int t; if (i != 0) { const struct usb_endpoint_descriptor *d; d = ep->desc; if (!d) continue; t = d->bEndpointAddress; t = scnprintf(next, size, "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, (t & USB_DIR_IN) ? "in" : "out", type_string(d->bmAttributes), usb_endpoint_maxp(d), ep->dma ? "dma" : "pio", ep->fifo_size ); } else /* ep0 should only have one transfer queued */ t = scnprintf(next, size, "ep0 max 64 pio %s\n", ep->is_in ? "in" : "out"); if (t <= 0 || t > size) goto done; size -= t; next += t; if (list_empty(&ep->queue)) { t = scnprintf(next, size, "\t(nothing queued)\n"); if (t <= 0 || t > size) goto done; size -= t; next += t; continue; } list_for_each_entry(req, &ep->queue, queue) { if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc)) t = scnprintf(next, size, "\treq %p len %d/%d " "buf %p (dmacount %08x)\n", &req->req, req->req.actual, req->req.length, req->req.buf, readl(&ep->dma->dmacount)); else t = scnprintf(next, size, "\treq %p len %d/%d buf %p\n", &req->req, req->req.actual, req->req.length, req->req.buf); if (t <= 0 || t > size) goto done; size -= t; next += t; if (ep->dma) { struct net2280_dma *td; td = req->td; t = scnprintf(next, size, "\t td %08x " " count %08x buf %08x desc %08x\n", (u32) req->td_dma, le32_to_cpu(td->dmacount), le32_to_cpu(td->dmaaddr), le32_to_cpu(td->dmadesc)); if (t <= 0 || t > size) goto done; size -= t; next += t; } } } done: spin_unlock_irqrestore(&dev->lock, flags); return PAGE_SIZE - size; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell45490.08%337.50%
Ricardo Ribalda Delgado387.54%225.00%
Andrew Morton61.19%112.50%
Yani Ioannou50.99%112.50%
Greg Kroah-Hartman10.20%112.50%
Total504100.00%8100.00%

static DEVICE_ATTR_RO(queues); #else #define device_create_file(a, b) (0) #define device_remove_file(a, b) do { } while (0) #endif /*-------------------------------------------------------------------------*/ /* another driver-specific mode might be a request type doing dma * to/from another device fifo instead of to/from memory. */
static void set_fifo_mode(struct net2280 *dev, int mode) { /* keeping high bits preserves BAR2 */ writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ INIT_LIST_HEAD(&dev->gadget.ep_list); list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); switch (mode) { case 0: list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list); dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; break; case 1: dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048; break; case 2: list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); dev->ep[1].fifo_size = 2048; dev->ep[2].fifo_size = 1024; break; } /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list); list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell279100.00%1100.00%
Total279100.00%1100.00%


static void defect7374_disable_data_eps(struct net2280 *dev) { /* * For Defect 7374, disable data EPs (and more): * - This phase undoes the earlier phase of the Defect 7374 workaround, * returing ep regs back to normal. */ struct net2280_ep *ep; int i; unsigned char ep_sel; u32 tmp_reg; for (i = 1; i < 5; i++) { ep = &dev->ep[i]; writel(i, &ep->cfg->ep_cfg); } /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */ for (i = 0; i < 6; i++) writel(0, &dev->dep[i].dep_cfg); for (ep_sel = 0; ep_sel <= 21; ep_sel++) { /* Select an endpoint for subsequent operations: */ tmp_reg = readl(&dev->plregs->pl_ep_ctrl); writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl); if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) || ep_sel == 18 || ep_sel == 20) continue; /* Change settings on some selected endpoints */ tmp_reg = readl(&dev->plregs->pl_ep_cfg_4); tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR); writel(tmp_reg, &dev->plregs->pl_ep_cfg_4); tmp_reg = readl(&dev->plregs->pl_ep_ctrl); tmp_reg |= BIT(EP_INITIALIZED); writel(tmp_reg, &dev->plregs->pl_ep_ctrl); } }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado15165.37%350.00%
David Brownell7130.74%116.67%
Greg Kroah-Hartman83.46%116.67%
Mian Yousaf Kaukab10.43%116.67%
Total231100.00%6100.00%


static void defect7374_enable_data_eps_zero(struct net2280 *dev) { u32 tmp = 0, tmp_reg; u32 scratch; int i; unsigned char ep_sel; scratch = get_idx_reg(dev->regs, SCRATCH); WARN_ON((scratch & (0xf << DEFECT7374_FSM_FIELD)) == DEFECT7374_FSM_SS_CONTROL_READ); scratch &= ~(0xf << DEFECT7374_FSM_FIELD); ep_warn(dev, "Operate Defect 7374 workaround soft this time"); ep_warn(dev, "It will operate on cold-reboot and SS connect"); /*GPEPs:*/ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | ((dev->enhanced_mode) ? BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE))); for (i = 1; i < 5; i++) writel(tmp, &dev->ep[i].cfg->ep_cfg); /* CSRIN, PCIIN, STATIN, RCIN*/ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE)); writel(tmp, &dev->dep[1].dep_cfg); writel(tmp, &dev->dep[3].dep_cfg); writel(tmp, &dev->dep[4].dep_cfg); writel(tmp, &dev->dep[5].dep_cfg); /*Implemented for development and debug. * Can be refined/tuned later.*/ for (ep_sel = 0; ep_sel <= 21; ep_sel++) { /* Select an endpoint for subsequent operations: */ tmp_reg = readl(&dev->plregs->pl_ep_ctrl); writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl); if (ep_sel == 1) { tmp = (readl(&dev->plregs->pl_ep_ctrl) | BIT(CLEAR_ACK_ERROR_CODE) | 0); writel(tmp, &dev->plregs->pl_ep_ctrl); continue; } if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || ep_sel == 18 || ep_sel == 20) continue; tmp = (readl(&dev->plregs->pl_ep_cfg_4) | BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); writel(tmp, &dev->plregs->pl_ep_cfg_4); tmp = readl(&dev->plregs->pl_ep_ctrl) & ~BIT(EP_INITIALIZED); writel(tmp, &dev->plregs->pl_ep_ctrl); } /* Set FSM to focus on the first Control Read: * - Tip: Connection speed is known upon the first * setup request.*/ scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; set_idx_reg(dev->regs, SCRATCH, scratch); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado30470.70%562.50%
David Brownell8018.60%112.50%
Greg Kroah-Hartman419.53%112.50%
Mian Yousaf Kaukab51.16%112.50%
Total430100.00%8100.00%

/* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second * * most of the work to support multiple net2280 controllers would * be to associate this gadget driver (yes?) with all of them, or * perhaps to bind specific drivers to specific devices. */
static void usb_reset_228x(struct net2280 *dev) { u32 tmp; dev->gadget.speed = USB_SPEED_UNKNOWN; (void) readl(&dev->usb->usbctl); net2280_led_init(dev); /* disable automatic responses, and irqs */ writel(0, &dev->usb->stdrsp); writel(0, &dev->regs->pciirqenb0); writel(0, &dev->regs->pciirqenb1); /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { struct net2280_ep *ep = &dev->ep[tmp + 1]; if (ep->dma) abort_dma(ep); } writel(~0, &dev->regs->irqstat0), writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), /* reset, and enable pci */ tmp = readl(&dev->regs->devinit) | BIT(PCI_ENABLE) | BIT(FIFO_SOFT_RESET) | BIT(USB_SOFT_RESET) | BIT(M8051_RESET); writel(tmp, &dev->regs->devinit); /* standard fifo and endpoint allocations */ set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado17080.19%333.33%
David Brownell2310.85%111.11%
Jeff Garzik73.30%111.11%
Greg Kroah-Hartman62.83%111.11%
Felipe Balbi31.42%111.11%
Alan Stern20.94%111.11%
Sebastian Andrzej Siewior10.47%111.11%
Total212100.00%9100.00%


static void usb_reset_338x(struct net2280 *dev) { u32 tmp; dev->gadget.speed = USB_SPEED_UNKNOWN; (void)readl(&dev->usb->usbctl); net2280_led_init(dev); if (dev->bug7734_patched) { /* disable automatic responses, and irqs */ writel(0, &dev->usb->stdrsp); writel(0, &dev->regs->pciirqenb0); writel(0, &dev->regs->pciirqenb1); } /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { struct net2280_ep *ep = &dev->ep[tmp + 1]; struct net2280_dma_regs __iomem *dma; if (ep->dma) { abort_dma(ep); } else { dma = &dev->dma[tmp]; writel(BIT(DMA_ABORT), &dma->dmastat); writel(0, &dma->dmactl); } } writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); if (dev->bug7734_patched) { /* reset, and enable pci */ tmp = readl(&dev->regs->devinit) | BIT(PCI_ENABLE) | BIT(FIFO_SOFT_RESET) | BIT(USB_SOFT_RESET) | BIT(M8051_RESET); writel(tmp, &dev->regs->devinit); } /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */ INIT_LIST_HEAD(&dev->gadget.ep_list); for (tmp = 1; tmp < dev->n_ep; tmp++) list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado19364.77%444.44%
Mian Yousaf Kaukab4414.77%111.11%
Greg Kroah-Hartman258.39%111.11%
David Brownell217.05%111.11%
Felipe Balbi113.69%111.11%
Alan Stern41.34%111.11%
Total298100.00%9100.00%


static void usb_reset(struct net2280 *dev) { if (dev->quirks & PLX_LEGACY) return usb_reset_228x(dev); return usb_reset_338x(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado31100.00%3100.00%
Total31100.00%3100.00%


static void usb_reinit_228x(struct net2280 *dev) { u32 tmp; /* basic endpoint init */ for (tmp = 0; tmp < 7; tmp++) { struct net2280_ep *ep = &dev->ep[tmp]; ep->ep.name = ep_info_dft[tmp].name; ep->ep.caps = ep_info_dft[tmp].caps; ep->dev = dev; ep->num = tmp; if (tmp > 0 && tmp <= 4) { ep->fifo_size = 1024; ep->dma = &dev->dma[tmp - 1]; } else ep->fifo_size = 64; ep->regs = &dev->epregs[tmp]; ep->cfg = &dev->epregs[tmp]; ep_reset_228x(dev->regs, ep); } usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64); usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64); dev->gadget.ep0 = &dev->ep[0].ep; dev->ep[0].stopped = 0; INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); /* we want to prevent lowlevel/insecure access from the USB host, * but erratum 0119 means this enable bit is ignored */ for (tmp = 0; tmp < 5; tmp++) writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado18168.05%233.33%
Greg Kroah-Hartman4717.67%116.67%
David Brownell228.27%233.33%
Robert Baldyga166.02%116.67%
Total266100.00%6100.00%


static void usb_reinit_338x(struct net2280 *dev) { int i; u32 tmp, val; static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 }; static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0xC0 }; /* basic endpoint init */ for (i = 0; i < dev->n_ep; i++) { struct net2280_ep *ep = &dev->ep[i]; ep->ep.name = dev->enhanced_mode ? ep_info_adv[i].name : ep_info_dft[i].name; ep->ep.caps = dev->enhanced_mode ? ep_info_adv[i].caps : ep_info_dft[i].caps; ep->dev = dev; ep->num = i; if (i > 0 && i <= 4) ep->dma = &dev->dma[i - 1]; if (dev->enhanced_mode) { ep->cfg = &dev->epregs[ne[i]]; /* * Set USB endpoint number, hardware allows same number * in both directions. */ if (i > 0 && i < 5) writel(ne[i], &ep->cfg->ep_cfg); ep->regs = (struct net2280_ep_regs __iomem *) (((void __iomem *)&dev->epregs[ne[i]]) + ep_reg_addr[i]); } else { ep->cfg = &dev->epregs[i]; ep->regs = &dev->epregs[i]; } ep->fifo_size = (i != 0) ? 2048 : 512; ep_reset_338x(dev->regs, ep); } usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512); dev->gadget.ep0 = &dev->ep[0].ep; dev->ep[0].stopped = 0; /* Link layer set up */ if (dev->bug7734_patched) { tmp = readl(&dev->usb_ext->usbctl2) & ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE)); writel(tmp, &dev->usb_ext->usbctl2); } /* Hardware Defect and Workaround */ val = readl(&dev->ll_lfps_regs->ll_lfps_5); val &= ~(0xf << TIMER_LFPS_6US); val |= 0x5 << TIMER_LFPS_6US; writel(val, &dev->ll_lfps_regs->ll_lfps_5); val = readl(&dev->ll_lfps_regs->ll_lfps_6); val &= ~(0xffff << TIMER_LFPS_80US); val |= 0x0100 << TIMER_LFPS_80US; writel(val, &dev->ll_lfps_regs->ll_lfps_6); /* * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB * Hot Reset Exit Handshake may Fail in Specific Case using * Default Register Settings. Workaround for Enumeration test. */ val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2); val &= ~(0x1f << HOT_TX_NORESET_TS2); val |= 0x10 << HOT_TX_NORESET_TS2; writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2); val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3); val &= ~(0x1f << HOT_RX_RESET_TS2); val |= 0x3 << HOT_RX_RESET_TS2; writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3); /* * Set Recovery Idle to Recover bit: * - On SS connections, setting Recovery Idle to Recover Fmw improves * link robustness with various hosts and hubs. * - It is safe to set for all connection speeds; all chip revisions. * - R-M-W to leave other bits undisturbed. * - Reference PLX TT-7372 */ val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit); val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW); writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit); INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); /* disable dedicated endpoints */ writel(0x0D, &dev->dep[0].dep_cfg); writel(0x0D, &dev->dep[1].dep_cfg); writel(0x0E, &dev->dep[2].dep_cfg); writel(0x0E, &dev->dep[3].dep_cfg); writel(0x0F, &dev->dep[4].dep_cfg); writel(0x0C, &dev->dep[5].dep_cfg); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado64590.85%342.86%
Mian Yousaf Kaukab344.79%228.57%
Robert Baldyga304.23%114.29%
Felipe Balbi10.14%114.29%
Total710100.00%7100.00%


static void usb_reinit(struct net2280 *dev) { if (dev->quirks & PLX_LEGACY) return usb_reinit_228x(dev); return usb_reinit_338x(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado31100.00%2100.00%
Total31100.00%2100.00%


static void ep0_start_228x(struct net2280 *dev) { writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) | BIT(CLEAR_NAK_OUT_PACKETS) | BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &dev->epregs[0].ep_rsp); /* * hardware optionally handles a bunch of standard requests * that the API hides from drivers anyway. have it do so. * endpoint status/features are handled in software, to * help pass tests for some dubious behavior. */ writel(BIT(SET_TEST_MODE) | BIT(SET_ADDRESS) | BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) | BIT(GET_DEVICE_STATUS) | BIT(GET_INTERFACE_STATUS), &dev->usb->stdrsp); writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | BIT(SELF_POWERED_USB_DEVICE) | BIT(REMOTE_WAKEUP_SUPPORT) | (dev->softconnect << USB_DETECT_ENABLE) | BIT(SELF_POWERED_STATUS), &dev->usb->usbctl); /* enable irqs so we can see ep0 and general operation */ writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | BIT(ENDPOINT_0_INTERRUPT_ENABLE), &dev->regs->pciirqenb0); writel(BIT(PCI_INTERRUPT_ENABLE) | BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) | BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) | BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) | BIT(VBUS_INTERRUPT_ENABLE) | BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE), &dev->regs->pciirqenb1); /* don't leave any writes posted */ (void) readl(&dev->usb->usbctl); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado193100.00%2100.00%
Total193100.00%2100.00%


static void ep0_start_338x(struct net2280 *dev) { if (dev->bug7734_patched) writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) | BIT(SET_EP_HIDE_STATUS_PHASE), &dev->epregs[0].ep_rsp); /* * hardware optionally handles a bunch of standard requests * that the API hides from drivers anyway. have it do so. * endpoint status/features are handled in software, to * help pass tests for some dubious behavior. */ writel(BIT(SET_ISOCHRONOUS_DELAY) | BIT(SET_SEL) | BIT(SET_TEST_MODE) | BIT(SET_ADDRESS) | BIT(GET_INTERFACE_STATUS) | BIT(GET_DEVICE_STATUS), &dev->usb->stdrsp); dev->wakeup_enable = 1; writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | (dev->softconnect << USB_DETECT_ENABLE) | BIT(DEVICE_REMOTE_WAKEUP_ENABLE), &dev->usb->usbctl); /* enable irqs so we can see ep0 and general operation */ writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | BIT(ENDPOINT_0_INTERRUPT_ENABLE), &dev->regs->pciirqenb0); writel(BIT(PCI_INTERRUPT_ENABLE) | BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) | BIT(VBUS_INTERRUPT_ENABLE), &dev->regs->pciirqenb1); /* don't leave any writes posted */ (void)readl(&dev->usb->usbctl); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado180100.00%3100.00%
Total180100.00%3100.00%


static void ep0_start(struct net2280 *dev) { if (dev->quirks & PLX_LEGACY) return ep0_start_228x(dev); return ep0_start_338x(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado31100.00%2100.00%
Total31100.00%2100.00%

/* when a driver is successfully registered, it will receive * control requests including set_configuration(), which enables * non-control requests. then usb traffic follows until a * disconnect is reported. then a host may connect again, or * the driver might get unbound. */
static int net2280_start(struct usb_gadget *_gadget, struct usb_gadget_driver *driver) { struct net2280 *dev; int retval; unsigned i; /* insist on high speed support from the driver, since * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) * "must not be used in normal operation" */ if (!driver || driver->max_speed < USB_SPEED_HIGH || !driver->setup) return -EINVAL; dev = container_of(_gadget, struct net2280, gadget); for (i = 0; i < dev->n_ep; i++) dev->ep[i].irqs = 0; /* hook up the driver ... */ driver->driver.bus = NULL; dev->driver = driver; retval = device_create_file(&dev->pdev->dev, &dev_attr_function); if (retval) goto err_unbind; retval = device_create_file(&dev->pdev->dev, &dev_attr_queues); if (retval) goto err_func; /* enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. */ net2280_led_active(dev, 1); if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched) defect7374_enable_data_eps_zero(dev); ep0_start(dev); /* pci writes may still be posted */ return 0; err_func: device_remove_file(&dev->pdev->dev, &dev_attr_function); err_unbind: dev->driver = NULL; return retval; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado20799.52%480.00%
Tim Harvey10.48%120.00%
Total208100.00%5100.00%


static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver) { int i; /* don't disconnect if it's not connected */ if (dev->gadget.speed == USB_SPEED_UNKNOWN) driver = NULL; /* stop hardware; prevent new request submissions; * and kill any outstanding requests. */ usb_reset(dev); for (i = 0; i < dev->n_ep; i++) nuke(&dev->ep[i]); /* report disconnect; the driver is already quiesced */ if (driver) { spin_unlock(&dev->lock); driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } usb_reinit(dev); }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado104100.00%1100.00%
Total104100.00%1100.00%


static int net2280_stop(struct usb_gadget *_gadget) { struct net2280 *dev; unsigned long flags; dev = container_of(_gadget, struct net2280, gadget); spin_lock_irqsave(&dev->lock, flags); stop_activity(dev, NULL); spin_unlock_irqrestore(&dev->lock, flags); net2280_led_active(dev, 0); device_remove_file(&dev->pdev->dev, &dev_attr_function); device_remove_file(&dev->pdev->dev, &dev_attr_queues); dev->driver = NULL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado9493.07%150.00%
Felipe Balbi76.93%150.00%
Total101100.00%2100.00%

/*-------------------------------------------------------------------------*/ /* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. * also works for dma-capable endpoints, in pio mode or just * to manually advance the queue after short OUT transfers. */
static void handle_ep_small(struct net2280_ep *ep) { struct net2280_request *req; u32 t; /* 0 error, 1 mid-data, 2 done */ int mode = 1; if (!list_empty(&ep->queue)) req = list_entry(ep->queue.next, struct net2280_request, queue); else req = NULL; /* ack all, and handle what we care about */ t = readl(&ep->regs->ep_stat); ep->irqs++; ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n", ep->ep.name, t, req ? &req->req : NULL); if (!ep->is_in || (ep->dev->quirks & PLX_2280)) writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat); else /* Added for 2282 */ writel(t, &ep->regs->ep_stat); /* for ep0, monitor token irqs to catch data stage length errors * and to synchronize on status. * * also, to defer reporting of protocol stalls ... here's where * data or status first appears, handling stalls here should never * cause trouble on the host side.. * * control requests could be slightly faster without token synch for * status, but status can jam up that way. */ if (unlikely(ep->num == 0)) { if (ep->is_in) { /* status; stop NAKing */ if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) { if (ep->dev->protocol_stall) { ep->stopped = 1; set_halt(ep); } if (!req) allow_status(ep); mode = 2; /* reply to extra IN data tokens with a zlp */ } else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { if (ep->dev->protocol_stall) { ep->stopped = 1; set_halt(ep); mode = 2; } else if (ep->responded && !req && !ep->stopped) write_fifo(ep, NULL); } } else { /* status; stop NAKing */ if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { if (ep->dev->protocol_stall) { ep->stopped = 1; set_halt(ep); } mode = 2; /* an extra OUT token is an error */ } else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) && req && req->req.actual == req->req.length) || (ep->responded && !req)) { ep->dev->protocol_stall = 1; set_halt(ep); ep->stopped = 1; if (req) done(ep, req, -EOVERFLOW); req = NULL; } } } if (unlikely(!req)) return; /* manual DMA queue advance after short OUT */ if (likely(ep->dma)) { if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { struct net2280_request *stuck_req = NULL; int stopped = ep->stopped; int num_completed; int stuck = 0; u32 count; /* TRANSFERRED works around OUT_DONE erratum 0112. * we expect (N <= maxpacket) bytes; host wrote M. * iff (M < N) we won't ever see a DMA interrupt. */ ep->stopped = 1; for (count = 0; ; t = readl(&ep->regs->ep_stat)) { /* any preceding dma transfers must finish. * dma handles (M >= N), may empty the queue */ num_completed = scan_dma_completions(ep); if (unlikely(list_empty(&ep->queue) || ep->out_overflow)) { req = NULL; break; } req = list_entry(ep->queue.next, struct net2280_request, queue); /* here either (M < N), a "real" short rx; * or (M == N) and the queue didn't empty */ if (likely(t & BIT(FIFO_EMPTY))) { count = readl(&ep->dma->dmacount); count &= DMA_BYTE_COUNT_MASK; if (readl(&ep->dma->dmadesc) != req->td_dma) req = NULL; break; } /* Escape loop if no dma transfers completed * after few retries. */ if (num_completed == 0) { if (stuck_req == req && readl(&ep->dma->dmadesc) != req->td_dma && stuck++ > 5) { count = readl( &ep->dma->dmacount); count &= DMA_BYTE_COUNT_MASK; req = NULL; ep_dbg(ep->dev, "%s escape stuck %d, count %u\n", ep->ep.name, stuck, count); break; } else if (stuck_req != req) { stuck_req = req; stuck = 0; } } else { stuck_req = NULL; stuck = 0; } udelay(1); } /* stop DMA, leave ep NAKing */ writel(BIT(DMA_ABORT), &ep->dma->dmastat); spin_stop_dma(ep->dma); if (likely(req)) { req->td->dmacount = 0; t = readl(&ep->regs->ep_avail); dma_done(ep, req, count, (ep->out_overflow || t) ? -EOVERFLOW : 0); } /* also flush to prevent erratum 0106 trouble */ if (unlikely(ep->out_overflow || (ep->dev->chiprev == 0x0100 && ep->dev->gadget.speed == USB_SPEED_FULL))) { out_flush(ep); ep->out_overflow = 0; } /* (re)start dma if needed, stop NAKing */ ep->stopped = stopped; if (!list_empty(&ep->queue)) restart_dma(ep); } else ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n", ep->ep.name, t); return; /* data packet(s) received (in the fifo, OUT) */ } else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) { if (read_fifo(ep, req) && ep->num != 0) mode = 2; /* data packet(s) transmitted (IN) */ } else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) { unsigned len; len = req->req.length - req->req.actual; if (len > ep->ep.maxpacket) len = ep->ep.maxpacket; req->req.actual += len; /* if we wrote it all, we're usually done */ /* send zlps until the status stage */ if ((req->req.actual == req->req.length) && (!req->req.zero || len != ep->ep.maxpacket) && ep->num) mode = 2; /* there was nothing to do ... */ } else if (mode == 1) return; /* done */ if (mode == 2) { /* stream endpoints often resubmit/unlink in completion */ done(ep, req, 0); /* maybe advance queue to next request */ if (ep->num == 0) { /* NOTE: net2280 could let gadget driver start the * status stage later. since not all controllers let * them control that, the api doesn't (yet) allow it. */ if (!ep->stopped) allow_status(ep); req = NULL; } else { if (!list_empty(&ep->queue) && !ep->stopped) req = list_entry(ep->queue.next, struct net2280_request, queue); else req = NULL; if (req && !ep->is_in) stop_out_naking(ep); } } /* is there a buffer for the next packet? * for best streaming performance, make sure there is one. */ if (req && !ep->stopped) { /* load IN fifo with next packet (may be zlp) */ if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) write_fifo(ep, &req->req); } }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado99989.04%787.50%
Jussi Kivilinna12310.96%112.50%
Total1122100.00%8100.00%


static struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex) { struct net2280_ep *ep; if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) return &dev->ep[0]; list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { u8 bEndpointAddress; if (!ep->desc) continue; bEndpointAddress = ep->desc->bEndpointAddress; if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) continue; if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) return ep; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado100100.00%1100.00%
Total100100.00%1100.00%


static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) { u32 scratch, fsmvalue; u32 ack_wait_timeout, state; /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */ scratch = get_idx_reg(dev->regs, SCRATCH); fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); scratch &= ~(0xf << DEFECT7374_FSM_FIELD); if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) && (r.bRequestType & USB_DIR_IN))) return; /* This is the first Control Read for this connection: */ if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) { /* * Connection is NOT SS: * - Connection must be FS or HS. * - This FSM state should allow workaround software to * run after the next USB connection. */ scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ; dev->bug7734_patched = 1; goto restore_data_eps; } /* Connection is SS: */ for (ack_wait_timeout = 0; ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS; ack_wait_timeout++) { state = readl(&dev->plregs->pl_ep_status_1) & (0xff << STATE); if ((state >= (ACK_GOOD_NORMAL << STATE)) && (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) { scratch |= DEFECT7374_FSM_SS_CONTROL_READ; dev->bug7734_patched = 1; break; } /* * We have not yet received host's Data Phase ACK * - Wait and try again. */ udelay(DEFECT_7374_PROCESSOR_WAIT_TIME); continue; } if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) { ep_err(dev, "FAIL: Defect 7374 workaround waited but failed " "to detect SS host's data phase ACK."); ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16" "got 0x%2.2x.\n", state >> STATE); } else { ep_warn(dev, "INFO: Defect 7374 workaround waited about\n" "%duSec for Control Read Data Phase ACK\n", DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout); } restore_data_eps: /* * Restore data EPs to their pre-workaround settings (disabled, * initialized, and other details). */ defect7374_disable_data_eps(dev); set_idx_reg(dev->regs, SCRATCH, scratch); return; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado252100.00%4100.00%
Total252100.00%4100.00%


static void ep_clear_seqnum(struct net2280_ep *ep) { struct net2280 *dev = ep->dev; u32 val; static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 }; val = readl(&dev->plregs->pl_ep_ctrl) & ~0x1f; val |= ep_pl[ep->num]; writel(val, &dev->plregs->pl_ep_ctrl); val |= BIT(SEQUENCE_NUMBER_RESET); writel(val, &dev->plregs->pl_ep_ctrl); return; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado107100.00%3100.00%
Total107100.00%3100.00%


static void handle_stat0_irqs_superspeed(struct net2280 *dev, struct net2280_ep *ep, struct usb_ctrlrequest r) { int tmp = 0; #define w_value le16_to_cpu(r.wValue) #define w_index le16_to_cpu(r.wIndex) #define w_length le16_to_cpu(r.wLength) switch (r.bRequest) { struct net2280_ep *e; u16 status; case USB_REQ_SET_CONFIGURATION: dev->addressed_state = !w_value; goto usb3_delegate; case USB_REQ_GET_STATUS: switch (r.bRequestType) { case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE): status = dev->wakeup_enable ? 0x02 : 0x00; if (dev->gadget.is_selfpowered) status |= BIT(0); status |= (dev->u1_enable << 2 | dev->u2_enable << 3 | dev->ltm_enable << 4); writel(0, &dev->epregs[0].ep_irqenb); set_fifo_bytecount(ep, sizeof(status)); writel((__force u32) status, &dev->epregs[0].ep_data); allow_status_338x(ep); break; case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): e = get_ep_by_addr(dev, w_index); if (!e) goto do_stall3; status = readl(&e->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT); writel(0, &dev->epregs[0].ep_irqenb); set_fifo_bytecount(ep, sizeof(status)); writel((__force u32) status, &dev->epregs[0].ep_data); allow_status_338x(ep); break; default: goto usb3_delegate; } break; case USB_REQ_CLEAR_FEATURE: switch (r.bRequestType) { case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): if (!dev->addressed_state) { switch (w_value) { case USB_DEVICE_U1_ENABLE: dev->u1_enable = 0; writel(readl(&dev->usb_ext->usbctl2) & ~BIT(U1_ENABLE), &dev->usb_ext->usbctl2); allow_status_338x(ep); goto next_endpoints3; case USB_DEVICE_U2_ENABLE: dev->u2_enable = 0; writel(readl(&dev->usb_ext->usbctl2) & ~BIT(U2_ENABLE), &dev->usb_ext->usbctl2); allow_status_338x(ep); goto next_endpoints3; case USB_DEVICE_LTM_ENABLE: dev->ltm_enable = 0; writel(readl(&dev->usb_ext->usbctl2) & ~BIT(LTM_ENABLE), &dev->usb_ext->usbctl2); allow_status_338x(ep); goto next_endpoints3; default: break; } } if (w_value == USB_DEVICE_REMOTE_WAKEUP) { dev->wakeup_enable = 0; writel(readl(&dev->usb->usbctl) & ~BIT(DEVICE_REMOTE_WAKEUP_ENABLE), &dev->usb->usbctl); allow_status_338x(ep); break; } goto usb3_delegate; case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): e = get_ep_by_addr(dev, w_index); if (!e) goto do_stall3; if (w_value != USB_ENDPOINT_HALT) goto do_stall3; ep_vdbg(dev, "%s clear halt\n", e->ep.name); /* * Workaround for SS SeqNum not cleared via * Endpoint Halt (Clear) bit. select endpoint */ ep_clear_seqnum(e); clear_halt(e); if (!list_empty(&e->queue) && e->td_dma) restart_dma(e); allow_status(ep); ep->stopped = 1; break; default: goto usb3_delegate; } break; case USB_REQ_SET_FEATURE: switch (r.bRequestType) { case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): if (!dev->addressed_state) { switch (w_value) { case USB_DEVICE_U1_ENABLE: dev->u1_enable = 1; writel(readl(&dev->usb_ext->usbctl2) | BIT(U1_ENABLE), &dev->usb_ext->usbctl2); allow_status_338x(ep); goto next_endpoints3; case USB_DEVICE_U2_ENABLE: dev->u2_enable = 1; writel(readl(&dev->usb_ext->usbctl2) | BIT(U2_ENABLE), &dev->usb_ext->usbctl2); allow_status_338x(ep); goto next_endpoints3; case USB_DEVICE_LTM_ENABLE: dev->ltm_enable = 1; writel(readl(&dev->usb_ext->usbctl2) | BIT(LTM_ENABLE), &dev->usb_ext->usbctl2); allow_status_338x(ep); goto next_endpoints3; default: break; } } if (w_value == USB_DEVICE_REMOTE_WAKEUP) { dev->wakeup_enable = 1; writel(readl(&dev->usb->usbctl) | BIT(DEVICE_REMOTE_WAKEUP_ENABLE), &dev->usb->usbctl); allow_status_338x(ep); break; } goto usb3_delegate; case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): e = get_ep_by_addr(dev, w_index); if (!e || (w_value != USB_ENDPOINT_HALT)) goto do_stall3; ep->stopped = 1; if (ep->num == 0) ep->dev->protocol_stall = 1; else { if (ep->dma) abort_dma(ep); set_halt(ep); } allow_status_338x(ep); break; default: goto usb3_delegate; } break; default: usb3_delegate: ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n", r.bRequestType, r.bRequest, w_value, w_index, w_length, readl(&ep->cfg->ep_cfg)); ep->responded = 0; spin_unlock(&dev->lock); tmp = dev->driver->setup(&dev->gadget, &r); spin_lock(&dev->lock); } do_stall3: if (tmp < 0) { ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", r.bRequestType, r.bRequest, tmp); dev->protocol_stall = 1; /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */ set_halt(ep); } next_endpoints3: #undef w_value #undef w_index #undef w_length return; }

Contributors

PersonTokensPropCommitsCommitProp
Ricardo Ribalda Delgado85784.77%763.64%
Greg Kroah-Hartman989.69%19.09%
David Brownell535.24%218.18%
Peter Chen30.30%19.09%
Total1011100.00%11100.00%


static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0) { u32 index; u32 bit; for (index = 0; index < ARRAY_SIZE(ep_bit); index++) { bit = BIT(ep_bit[index]); if (!stat0) break; if (!(stat0 & bit)) continue; stat0 &= ~bit; handle_ep_small(&dev->ep[index]); } }

Contributors

PersonTokensPropCommitsCommitProp
Mian Yousaf Kaukab80100.00%1100.00%
Total80100.00%1100.00%


static void handle_stat0_irqs(struct net2280 *dev, u32 stat) { struct net2280_ep *ep; u32 num, scratch; /* most of these don't need individual acks */ stat &= ~BIT(INTA_ASSERTED); if (!stat) return; /* ep_dbg(dev, "irqstat0 %04x\n", stat); */ /* starting a control request? */ if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) { union { u32 raw[2]; struct usb_ctrlrequest r; } u; int tmp; struct net2280_request *req; if (dev->gadget.speed == USB_SPEED_UNKNOWN) { u32 val = readl(&dev->usb->usbstat); if (val & BIT(SUPER_SPEED)) { dev->gadget.speed = USB_SPEED_SUPER; usb_ep_set_maxpacket_limit(&dev->ep[0].ep, EP0_SS_MAX_PACKET_SIZE); } else if (val & BIT(HIGH_SPEED)) { dev->gadget.speed = USB_SPEED_HIGH; usb_ep_set_maxpacket_limit(&dev->ep[0].ep, EP0_HS_MAX_PACKET_SIZE); } else { dev->gadget.speed = USB_SPEED_FULL; usb_ep_set_maxpacket_limit(&dev->ep[0].ep, EP0_HS_MAX_PACKET_SIZE); } net2280_led_speed(dev, dev->gadget.speed); ep_dbg(dev, "%s\n", usb_speed_string(dev->gadget.speed)); } ep = &dev->ep[0]; ep->irqs++; /* make sure any leftover request state is cleared */ stat &= ~BIT(ENDPOINT_0_INTERRUPT); while (!list_empty(&ep->queue)) { req = list_entry(ep->queue.next, struct net2280_request, queue); done(ep, req, (req->req.actual == req->req.length) ? 0 : -EPROTO); } ep->stopped = 0; dev->protocol_stall = 0; if (!(dev->quirks & PLX_PCIE)) { if (ep->dev->quirks & PLX_2280) tmp = BIT(FIFO_OVERFLOW) | BIT(FIFO_UNDERFLOW); else tmp = 0; writel(tmp | BIT(TIMEOUT) | BIT(USB_STALL_SENT) | BIT(USB_IN_NAK_SENT) | BIT(USB_IN_ACK_RCVD) | BIT(USB_OUT_PING_NAK_SENT) | BIT(USB_OUT_ACK_SENT) | BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | BIT(DATA_PACKET_RECEIVED_INTERRUPT) | BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); } u.raw[0] = readl(&dev->usb->setup0123); u.raw[1] = readl(&dev->usb->setup4567); cpu_to_le32s(&u.raw[0]); cpu_to_le32s(&u.raw[1]); if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched) defect7374_workaround(dev, u.r); tmp = 0; #define w_value le16_to_cpu(u.r.wValue) #define w_index le16_to_cpu(u.r.wIndex) #define w_length le16_to_cpu(u.r.wLength) /* ack the irq */ writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0); stat ^= BIT(SETUP_PACKET_INTERRUPT); /* watch control traffic at the token level, and force * synchronization before letting the status stage happen. * FIXME ignore tokens we'll NAK, until driver responds. * that'll mean a lot less irqs for some drivers. */ ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; if (ep->is_in) { scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT); stop_out_naking(ep); } else scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT); writel(scratch, &dev->epregs[0].ep_irqenb); /* we made the hardware handle most lowlevel requests; * everything else goes uplevel to the gadget code. */ ep->responded = 1; if (dev->gadget.speed == USB_SPEED_SUPER) { handle_stat0_irqs_superspeed(dev, ep, u.r); goto next_endpoints; } switch (u.r.bRequest) { case USB_REQ_GET_STATUS: { struct net2280_ep *e; __le32 status; /* hw handles device and interface status */ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) goto delegate; e = get_ep_by_addr(dev, w_index); if (!e || w_length > 2) goto do_stall; if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT)) status = cpu_to_le32(1); else status = cpu_to_le32(0); /* don't bother with a request object! */ writel(0, &dev->epregs[0].ep_irqenb); set_fifo_bytecount(ep, w_length); writel((__force u32)status, &dev->epregs[0].ep_data); allow_status(ep); ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status); goto next_endpoints; } break; case USB_REQ_CLEAR_FEATURE: { struct net2280_ep *e; /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; if (w_value != USB_ENDPOINT_HALT || w_length != 0) goto do_stall; e = get_ep_by_addr(dev, w_index); if (!e) goto do_stall; if (e->wedged) { ep_vdbg(dev, "%s wedged, halt not cleared\n", ep->ep.name); } else { ep_vdbg(dev, "%s clear halt\n", e->ep.name); clear_halt(e); if ((ep->dev->quirks & PLX_PCIE) && !list_empty(&e->queue) && e->td_dma) restart_dma(e); } allow_status(ep); goto next_endpoints; } break; case USB_REQ_SET_FEATURE: { struct net2280_ep *e; /* hw handles device features */ if (u.r.bRequestType != USB_RECIP_ENDPOINT) goto delegate; if (w_value != USB_ENDPOINT_HALT || w_length != 0) goto do_stall; e = get_ep_by_addr(dev, w_index); if (!e) goto do_stall; if (e->ep.name == ep0name) goto do_stall; set_halt(e); if ((dev->quirks & PLX_PCIE) && e->dma) abort_dma(e); allow_status(ep); ep_vdbg(dev, "%s set halt\n", ep->ep.name); goto next_endpoints; } break; default: delegate: ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x " "ep_cfg %08x\n", u.r.bRequestType, u.r.bRequest, w_value, w_index, w_length, readl(&ep->cfg->ep_cfg)); ep->responded = 0; spin_unlock(&dev->lock); tmp = dev->driver->setup(&dev->gadget, &u.r); spin_lock(&dev->lock); } /* stall ep0 on error */ if (tmp < 0) { do_stall: ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", u.r.bRequestType, u.r.bRequest, tmp); dev->protocol_stall = 1; } /* some in/out token irq should follow; maybe stall then. * driver must queue a request (even zlp) or halt ep0 * before the host times out. */ } #undef w_value #undef w_index #undef w_length next_endpoints: if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) { u32 mask = (BIT(ENDPOINT_0_INTERRUPT) | USB3380_IRQSTAT0_EP_INTR_MASK_IN | USB3380_IRQSTAT0_EP_INTR_MASK_OUT); if (stat & mask) { usb338x_handle_ep_intr(dev, stat & mask); stat &= ~mask; } } else { /* endpoint data irq ? */ scratch = stat & 0x7f; stat &= ~0x7f; for (num = 0; scratch; num++) { u32 t; /* do this endpoint's FIFO and queue need tending? */ t = BIT(num); if ((scratch & t) == 0) continue; scratch ^= t; ep = &dev->ep[num]; handle_ep_small(ep); } } if (stat) ep_dbg(dev, "unhandled irqstat0 %08x\n", stat); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell88066.07%832.00%
Ricardo Ribalda Delgado30222.67%832.00%
Mian Yousaf Kaukab544.05%14.00%
Alan Stern513.83%28.00%
Guennadi Liakhovetski302.25%14.00%
Tim Harvey50.38%14.00%
Michal Nazarewicz40.30%14.00%
Greg Kroah-Hartman30.23%14.00%
Harvey Harrison20.15%14.00%
Joe Perches10.08%14.00%
Total1332100.00%25100.00%

#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \ BIT(DMA_C_INTERRUPT) | \ BIT(DMA_B_INTERRUPT) | \ BIT(DMA_A_INTERRUPT)) #define PCI_ERROR_INTERRUPTS ( \ BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \ BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \ BIT(PCI_RETRY_ABORT_INTERRUPT))
static void handle_stat1_irqs(struct net2280 *dev, u32 stat) __releases(dev->lock) __acquires(dev->lock) { struct net2280_ep *ep; u32 tmp, num, mask, scratch; /* after disconnect there's nothing else to do! */ tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT); mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED); /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT * only indicates a change in the reset state). */ if (stat & tmp) { bool reset = false; bool disconnect = false; /* * Ignore disconnects and resets if the speed hasn't been set. * VBUS can bounce and there's always an initial reset. */ writel(tmp, &dev->regs->irqstat1); if (dev->gadget.speed != USB_SPEED_UNKNOWN) { if ((stat & BIT(VBUS_INTERRUPT)) && (readl(&dev->usb->usbctl) & BIT(VBUS_PIN)) == 0) { disconnect = true; ep_dbg(dev, "disconnect %s\n", dev->driver->driver.name); } else if ((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) && (readl(&dev->usb->usbstat) & mask) == 0) { reset = true; ep_dbg(dev, "reset %s\n", dev->driver->driver.name); } if (disconnect || reset) { stop_activity(dev, dev->driver); ep0_start(dev); spin_unlock(&dev->lock); if (reset) usb_gadget_udc_reset (&dev->gadget, dev->driver); else (dev->driver->disconnect) (&dev->gadget); spin_lock(&dev->lock); return; } } stat &= ~tmp; /* vBUS can bounce ... one of many reasons to ignore the * notion of hotplug events on bus connect/disconnect! */ if (!stat) return; } /* NOTE: chip stays in PCI D0 state for now, but it could * enter D1 to save more power */ tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT); if (stat & tmp) { writel(tmp, &dev->regs->irqstat1); if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) { if (dev->driver->suspend) dev->driver->suspend(&dev->gadget); if (!enable_suspend) stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT); } else { if (dev->driver->resume) dev->driver->resume(&dev->gadget); /* at high speed, note erratum 0133 */ } stat &= ~tmp; } /* clear any other status/irqs */ if (stat) writel(stat, &dev->regs->irqstat1); /* some status we can just ignore */ if (dev->quirks & PLX_2280) stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | BIT(SUSPEND_REQUEST_INTERRUPT) | BIT(RESUME_INTERRUPT) | BIT(SOF_INTERRUPT)); else stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | BIT(RESUME_INTERRUPT) | BIT(SOF_DOWN_INTERRUPT) | BIT(SOF_INTERRUPT)); if (!stat) return; /* ep_dbg(dev, "irqstat1 %08x\n", stat);*/ /* DMA status, for ep-{a,b,c,d} */ scratch = stat & DMA_INTERRUPTS; stat &= ~DMA_INTERRUPTS; scratch >>= 9; for (num = 0; scratch; num++) { struct net2280_dma_regs __iomem *dma; tmp = BIT(num); if ((tmp & scratch) == 0) continue; scratch ^= tmp; ep = &dev->ep[num + 1]; dma = ep->dma; if (!dma) continue; /* clear ep's dma status */ tmp = readl(&dma->dmastat); writel(tmp, &dma->dmastat); /* dma sync*/ if (dev->quirks & PLX_PCIE) { u32 r_dmacount = readl(&dma->dmacount); if (!ep->is_in && (r_dmacount & 0x00FFFFFF) && (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) continue; } if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) { ep_dbg(ep->dev, "%s no xact done? %08x\n", ep->ep.name, tmp); continue; } stop_dma(ep->dma); /* OUT transfers terminate when the data from the * host is in our memory. Process whatever's done. * On this path, we know transfer's last packet wasn't * less than req->length. NAK_OUT_PACKETS may be set, * or the FIFO may already be holding new packets. * * IN transfers can linger in the FIFO for a very * long time ... we ignore that for now, accounting * precisely (like PIO does) needs per-packet irqs */ scan_dma_completions(ep); /* disable dma on inactive queues; else maybe restart */ if (!list_empty(&ep->queue)) { tmp = readl(&dma->dmactl); restart_dma(ep); } ep->irqs++; } /* NOTE: there are other PCI errors we might usefully notice. * if they appear very often, here's where to try recovering. */ if (stat & PCI_ERROR_INTERRUPTS) { ep_err(dev, "pci dma error; stat %08x\n", stat); stat &= ~PCI_ERROR_INTERRUPTS; /* these are fatal errors, but "maybe" they won't * happen again ... */ stop_activity(dev, dev->driver); ep0_start(dev); stat = 0; } if (stat) ep_dbg(dev, "unhandled irqstat1 %08x\n", stat); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell48763.91%529.41%
Alan Stern12316.14%15.88%
Ricardo Ribalda Delgado11815.49%635.29%
Guennadi Liakhovetski192.49%15.88%
Felipe Balbi121.57%15.88%
Vitaliy Ivanov10.13%15.88%
Tim Harvey10.13%15.88%
Al Viro10.13%15.88%
Total762100.00%17100.00%


static irqreturn_t net2280_irq(int irq, void *_dev) { struct net2280 *dev = _dev; /* shared interrupt, not ours */ if ((dev->quirks & PLX_LEGACY) && (!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED)))) return IRQ_NONE; spin_lock(&dev->lock); /* handle disconnect, dma, and more */ handle_stat1_irqs(dev, readl(&dev->regs->irqstat1)); /* control requests and PIO */ handle_stat0_irqs(dev, readl(&dev->regs->irqstat0)); if (dev->quirks & PLX_PCIE) { /* re-enable interrupt to trigger any possible new interrupt */ u32 pciirqenb1 = readl(&dev->regs->pciirqenb1); writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1); writel(pciirqenb1, &dev->regs->pciirqenb1); } spin_unlock(&dev->lock); return IRQ_HANDLED; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell7145.81%116.67%
Ricardo Ribalda Delgado6240.00%350.00%
Alan Stern2113.55%116.67%
Tim Harvey10.65%116.67%
Total155100.00%6100.00%

/*-------------------------------------------------------------------------*/
static void gadget_release(struct device *_dev) { struct net2280 *dev = dev_get_drvdata(_dev); kfree(dev); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell26100.00%1100.00%
Total26100.00%1100.00%

/* tear down the binding between this driver and the pci device */
static void net2280_remove(struct pci_dev *pdev) { struct net2280 *dev = pci_get_drvdata(pdev); usb_del_gadget_udc(&dev->gadget); BUG_ON(dev->driver); /* then clean up the resources we allocated during probe() */ net2280_led_shutdown(dev); if (dev->requests) { int i; for (i = 1; i < 5; i++) { if (!dev->ep[i].dummy) continue; pci_pool_free(dev->requests, dev->ep[i].dummy, dev->ep[i].td_dma); } pci_pool_destroy(dev->requests); } if (dev->got_irq) free_irq(pdev->irq, dev); if (dev->quirks & PLX_PCIE) pci_disable_msi(pdev); if (dev->regs) iounmap(dev->regs); if (dev->region) release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); if (dev->enabled) pci_disable_device(pdev); device_remove_file(&pdev->dev, &dev_attr_registers); ep_info(dev, "unbind\n"); }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell18489.32%337.50%
Ricardo Ribalda Delgado136.31%337.50%
Sebastian Andrzej Siewior83.88%112.50%
Tim Harvey10.49%112.50%
Total206100.00%8100.00%

/* wrap this driver around the specified device, but * don't respond over USB until a gadget driver binds to us. */
static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct net2280 *dev; unsigned long resource, len; void __iomem *base = NULL; int retval, i; /* alloc, and start init */ dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { retval = -ENOMEM; goto done; } pci_set_drvdata(pdev, dev); spin_lock_init(&dev->lock); dev->quirks = id->driver_data; dev->pdev = pdev; dev->gadget.ops = &net2280_ops; dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ? USB_SPEED_SUPER : USB_SPEED_HIGH; /* the "gadget" abstracts/virtualizes the controller */ dev->gadget.name = driver_name; /* now all the pci goodies ... */ if (pci_enable_device(pdev) < 0) { retval = -ENODEV; goto done; } dev->enabled = 1; /* BAR 0 holds all the registers * BAR 1 is 8051 memory; unused here (note erratum 0103) * BAR 2 is fifo memory; unused here */ resource = pci_resource_start(pdev, 0); len = pci_resource_len(pdev, 0); if (!request_mem_region(resource, len, driver_name)) { ep_dbg(dev, "controller already in use\n"); retval = -EBUSY; goto done; } 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); if (base == NULL) { ep_dbg(dev, "can't map memory\n"); retval = -EFAULT; goto done; } dev->regs = (struct net2280_regs __iomem *) base; dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); if (dev->quirks & PLX_PCIE) { u32 fsmvalue; u32 usbstat; dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) (base + 0x00b4); dev->llregs = (struct usb338x_ll_regs __iomem *) (base + 0x0700); dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *) (base + 0x0748); dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *) (base + 0x077c); dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *) (base + 0x079c); dev->plregs = (struct usb338x_pl_regs __iomem *) (base + 0x0800); usbstat = readl(&dev->usb->usbstat); dev->enhanced_mode = !!(usbstat & BIT(11)); dev->n_ep = (dev->enhanced_mode) ? 9 : 5; /* put into initial config, link up all endpoints */ fsmvalue = get_idx_reg(dev->regs, SCRATCH) & (0xf << DEFECT7374_FSM_FIELD); /* See if firmware needs to set up for workaround: */ if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { dev->bug7734_patched = 1; writel(0, &dev->usb->usbctl); } else dev->bug7734_patched = 0; } else { dev->enhanced_mode = 0; dev->n_ep = 7; /* put into initial config, link up all endpoints */ writel(0, &dev->usb->usbctl); } usb_reset(dev); usb_reinit(dev); /* irq setup after old hardware is cleaned up */ if (!pdev->irq) { ep_err(dev, "No IRQ. Check PCI setup!\n"); retval = -ENODEV; goto done; } if (dev->quirks & PLX_PCIE) if (pci_enable_msi(pdev)) ep_err(dev, "Failed to enable MSI mode\n"); if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev)) { ep_err(dev, "request interrupt %d failed\n", pdev->irq); retval = -EBUSY; goto done; } dev->got_irq = 1; /* DMA setup */ /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ dev->requests = pci_pool_create("requests", pdev, sizeof(struct net2280_dma), 0 /* no alignment requirements */, 0 /* or page-crossing issues */); if (!dev->requests) { ep_dbg(dev, "can't get request pool\n"); retval = -ENOMEM; goto done; } for (i = 1; i < 5; i++) { struct net2280_dma *td; td = pci_pool_alloc(dev->requests, GFP_KERNEL, &dev->ep[i].td_dma); if (!td) { ep_dbg(dev, "can't get dummy %d\n", i); retval = -ENOMEM; goto done; } td->dmacount = 0; /* not VALID */ td->dmadesc = td->dmaaddr; dev->ep[i].dummy = td; } /* enable lower-overhead pci memory bursts during DMA */ if (dev->quirks & PLX_LEGACY) writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) | /* * 256 write retries may not be enough... BIT(PCI_RETRY_ABORT_ENABLE) | */ BIT(DMA_READ_MULTIPLE_ENABLE) | BIT(DMA_READ_LINE_ENABLE), &dev->pci->pcimstctl); /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ pci_set_master(pdev); pci_try_set_mwi(pdev); /* ... also flushes any posted pci writes */ dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff; /* done */ ep_info(dev, "%s\n", driver_desc); ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n", pdev->irq, base, dev->chiprev); ep_info(dev, "version: " DRIVER_VERSION "; %s\n", dev->enhanced_mode ? "enhanced mode" : "legacy mode"); retval = device_create_file(&pdev->dev, &dev_attr_registers); if (retval) goto done; retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget, gadget_release); if (retval) goto done; return 0; done: if (dev) net2280_remove(pdev); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
David Brownell58759.84%726.92%
Ricardo Ribalda Delgado33634.25%726.92%
Sebastian Andrzej Siewior212.14%13.85%
Jeff Garzik90.92%13.85%
David S. Miller80.82%13.85%
Al Viro70.71%27.69%
Alan Stern30.31%13.85%
Felipe Balbi30.31%13.85%
Tim Harvey20.20%13.85%
Michal Nazarewicz20.20%13.85%
Christoph Lameter10.10%13.85%
Randy Dunlap10.10%13.85%
Thomas Gleixner10.10%13.85%
Total981100.00%26100.00%

/* make sure the board is quiescent; otherwise it will continue * generating IRQs across the upcoming reboot. */
static void net2280_shutdown(struct pci_dev *pdev) { struct net2280 *dev = pci_get_drvdata(pdev); /* disable IRQs */ writel(0, &dev->regs->pciirqenb0); writel(0, &dev->regs->pciirqenb1); /* disable the pullup so the host will think we're gone */ writel(0, &dev->usb->usbctl); }

Contributors

PersonTokensPropCommitsCommitProp
Alan Stern59100.00%1100.00%
Total59100.00%1100.00%

/*-------------------------------------------------------------------------*/ static const struct pci_device_id pci_ids[] = { { .class = PCI_CLASS_SERIAL_USB_DEVICE, .class_mask = ~0, .vendor = PCI_VENDOR_ID_PLX_LEGACY, .device = 0x2280, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = PLX_LEGACY | PLX_2280, }, { .class = PCI_CLASS_SERIAL_USB_DEVICE, .class_mask = ~0, .vendor = PCI_VENDOR_ID_PLX_LEGACY, .device = 0x2282, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = PLX_LEGACY, }, { .class = PCI_CLASS_SERIAL_USB_DEVICE, .class_mask = ~0, .vendor = PCI_VENDOR_ID_PLX, .device = 0x2380, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = PLX_PCIE, }, { .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), .class_mask = ~0, .vendor = PCI_VENDOR_ID_PLX, .device = 0x3380, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = PLX_PCIE | PLX_SUPERSPEED, }, { .class = PCI_CLASS_SERIAL_USB_DEVICE, .class_mask = ~0, .vendor = PCI_VENDOR_ID_PLX, .device = 0x3382, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = PLX_PCIE | PLX_SUPERSPEED, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(pci, pci_ids); /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver net2280_pci_driver = { .name = (char *) driver_name, .id_table = pci_ids, .probe = net2280_probe, .remove = net2280_remove, .shutdown = net2280_shutdown, /* FIXME add power management support */ }; module_pci_driver(net2280_pci_driver); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("David Brownell"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
David Brownell942048.09%2723.08%
Ricardo Ribalda Delgado720236.77%2521.37%
Mian Yousaf Kaukab9244.72%119.40%
Jussi Kivilinna4242.16%21.71%
Robert Baldyga4222.15%32.56%
Alan Stern3751.91%75.98%
Greg Kroah-Hartman2931.50%21.71%
Guennadi Liakhovetski1760.90%10.85%
Tim Harvey690.35%10.85%
Felipe Balbi600.31%75.98%
Sebastian Andrzej Siewior560.29%10.85%
Andrew Morton270.14%43.42%
Al Viro260.13%32.56%
Raz Manor240.12%10.85%
Jeff Garzik160.08%10.85%
Yani Ioannou150.08%10.85%
Mario Schuknecht90.05%10.85%
David S. Miller80.04%10.85%
Linus Torvalds70.04%10.85%
Peter Chen70.04%10.85%
Michal Nazarewicz60.03%21.71%
Heikki Krogerus40.02%10.85%
Harvey Harrison40.02%21.71%
Bryan Wu30.02%10.85%
Christoph Lameter10.01%10.85%
Lucas De Marchi10.01%10.85%
Michal Sojka10.01%10.85%
Thomas Gleixner10.01%10.85%
Vitaliy Ivanov10.01%10.85%
Joe Perches10.01%10.85%
Rusty Russell10.01%10.85%
Colin Ian King10.01%10.85%
Kuninori Morimoto10.01%10.85%
Randy Dunlap10.01%10.85%
Total19587100.00%117100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.