162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the PLX NET2280 USB device controller. 462306a36Sopenharmony_ci * Specs and errata are available from <http://www.plxtech.com>. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * PLX Technology Inc. (formerly NetChip Technology) supported the 762306a36Sopenharmony_ci * development of this driver. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * CODE STATUS HIGHLIGHTS 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This driver should work well with most "gadget" drivers, including 1362306a36Sopenharmony_ci * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers 1462306a36Sopenharmony_ci * as well as Gadget Zero and Gadgetfs. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * DMA is enabled by default. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * MSI is enabled by default. The legacy IRQ is used if MSI couldn't 1962306a36Sopenharmony_ci * be enabled. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Note that almost all the errata workarounds here are only needed for 2262306a36Sopenharmony_ci * rev1 chips. Rev1a silicon (0110) fixes almost all of them. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Copyright (C) 2003 David Brownell 2762306a36Sopenharmony_ci * Copyright (C) 2003-2005 PLX Technology, Inc. 2862306a36Sopenharmony_ci * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility 3162306a36Sopenharmony_ci * with 2282 chip 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Modified Ricardo Ribalda Qtechnology AS to provide compatibility 3462306a36Sopenharmony_ci * with usb 338x chip. Based on PLX driver 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <linux/module.h> 3862306a36Sopenharmony_ci#include <linux/pci.h> 3962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 4062306a36Sopenharmony_ci#include <linux/kernel.h> 4162306a36Sopenharmony_ci#include <linux/delay.h> 4262306a36Sopenharmony_ci#include <linux/ioport.h> 4362306a36Sopenharmony_ci#include <linux/slab.h> 4462306a36Sopenharmony_ci#include <linux/errno.h> 4562306a36Sopenharmony_ci#include <linux/init.h> 4662306a36Sopenharmony_ci#include <linux/timer.h> 4762306a36Sopenharmony_ci#include <linux/list.h> 4862306a36Sopenharmony_ci#include <linux/interrupt.h> 4962306a36Sopenharmony_ci#include <linux/moduleparam.h> 5062306a36Sopenharmony_ci#include <linux/device.h> 5162306a36Sopenharmony_ci#include <linux/usb/ch9.h> 5262306a36Sopenharmony_ci#include <linux/usb/gadget.h> 5362306a36Sopenharmony_ci#include <linux/prefetch.h> 5462306a36Sopenharmony_ci#include <linux/io.h> 5562306a36Sopenharmony_ci#include <linux/iopoll.h> 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#include <asm/byteorder.h> 5862306a36Sopenharmony_ci#include <asm/irq.h> 5962306a36Sopenharmony_ci#include <asm/unaligned.h> 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller" 6262306a36Sopenharmony_ci#define DRIVER_VERSION "2005 Sept 27/v3.0" 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define EP_DONTUSE 13 /* nonzero */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define USE_RDK_LEDS /* GPIO pins control three LEDs */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic const char driver_name[] = "net2280"; 7062306a36Sopenharmony_cistatic const char driver_desc[] = DRIVER_DESC; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 }; 7362306a36Sopenharmony_cistatic const char ep0name[] = "ep0"; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define EP_INFO(_name, _caps) \ 7662306a36Sopenharmony_ci { \ 7762306a36Sopenharmony_ci .name = _name, \ 7862306a36Sopenharmony_ci .caps = _caps, \ 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic const struct { 8262306a36Sopenharmony_ci const char *name; 8362306a36Sopenharmony_ci const struct usb_ep_caps caps; 8462306a36Sopenharmony_ci} ep_info_dft[] = { /* Default endpoint configuration */ 8562306a36Sopenharmony_ci EP_INFO(ep0name, 8662306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)), 8762306a36Sopenharmony_ci EP_INFO("ep-a", 8862306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 8962306a36Sopenharmony_ci EP_INFO("ep-b", 9062306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 9162306a36Sopenharmony_ci EP_INFO("ep-c", 9262306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 9362306a36Sopenharmony_ci EP_INFO("ep-d", 9462306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 9562306a36Sopenharmony_ci EP_INFO("ep-e", 9662306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 9762306a36Sopenharmony_ci EP_INFO("ep-f", 9862306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 9962306a36Sopenharmony_ci EP_INFO("ep-g", 10062306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 10162306a36Sopenharmony_ci EP_INFO("ep-h", 10262306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_ALL)), 10362306a36Sopenharmony_ci}, ep_info_adv[] = { /* Endpoints for usb3380 advance mode */ 10462306a36Sopenharmony_ci EP_INFO(ep0name, 10562306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)), 10662306a36Sopenharmony_ci EP_INFO("ep1in", 10762306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), 10862306a36Sopenharmony_ci EP_INFO("ep2out", 10962306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), 11062306a36Sopenharmony_ci EP_INFO("ep3in", 11162306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), 11262306a36Sopenharmony_ci EP_INFO("ep4out", 11362306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), 11462306a36Sopenharmony_ci EP_INFO("ep1out", 11562306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), 11662306a36Sopenharmony_ci EP_INFO("ep2in", 11762306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), 11862306a36Sopenharmony_ci EP_INFO("ep3out", 11962306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_OUT)), 12062306a36Sopenharmony_ci EP_INFO("ep4in", 12162306a36Sopenharmony_ci USB_EP_CAPS(USB_EP_CAPS_TYPE_ALL, USB_EP_CAPS_DIR_IN)), 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#undef EP_INFO 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* mode 0 == ep-{a,b,c,d} 1K fifo each 12762306a36Sopenharmony_ci * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable 12862306a36Sopenharmony_ci * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_cistatic ushort fifo_mode; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* "modprobe net2280 fifo_mode=1" etc */ 13362306a36Sopenharmony_cimodule_param(fifo_mode, ushort, 0644); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* enable_suspend -- When enabled, the driver will respond to 13662306a36Sopenharmony_ci * USB suspend requests by powering down the NET2280. Otherwise, 13762306a36Sopenharmony_ci * USB suspend requests will be ignored. This is acceptable for 13862306a36Sopenharmony_ci * self-powered devices 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic bool enable_suspend; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* "modprobe net2280 enable_suspend=1" etc */ 14362306a36Sopenharmony_cimodule_param(enable_suspend, bool, 0444); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic char *type_string(u8 bmAttributes) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { 15062306a36Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: return "bulk"; 15162306a36Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: return "iso"; 15262306a36Sopenharmony_ci case USB_ENDPOINT_XFER_INT: return "intr"; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci return "control"; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#include "net2280.h" 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define valid_bit cpu_to_le32(BIT(VALID_BIT)) 16062306a36Sopenharmony_ci#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void ep_clear_seqnum(struct net2280_ep *ep); 16362306a36Sopenharmony_cistatic void stop_activity(struct net2280 *dev, 16462306a36Sopenharmony_ci struct usb_gadget_driver *driver); 16562306a36Sopenharmony_cistatic void ep0_start(struct net2280 *dev); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 16862306a36Sopenharmony_cistatic inline void enable_pciirqenb(struct net2280_ep *ep) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci u32 tmp = readl(&ep->dev->regs->pciirqenb0); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (ep->dev->quirks & PLX_LEGACY) 17362306a36Sopenharmony_ci tmp |= BIT(ep->num); 17462306a36Sopenharmony_ci else 17562306a36Sopenharmony_ci tmp |= BIT(ep_bit[ep->num]); 17662306a36Sopenharmony_ci writel(tmp, &ep->dev->regs->pciirqenb0); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int 18262306a36Sopenharmony_cinet2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct net2280 *dev; 18562306a36Sopenharmony_ci struct net2280_ep *ep; 18662306a36Sopenharmony_ci u32 max; 18762306a36Sopenharmony_ci u32 tmp = 0; 18862306a36Sopenharmony_ci u32 type; 18962306a36Sopenharmony_ci unsigned long flags; 19062306a36Sopenharmony_ci static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; 19162306a36Sopenharmony_ci int ret = 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 19462306a36Sopenharmony_ci if (!_ep || !desc || ep->desc || _ep->name == ep0name || 19562306a36Sopenharmony_ci desc->bDescriptorType != USB_DT_ENDPOINT) { 19662306a36Sopenharmony_ci pr_err("%s: failed at line=%d\n", __func__, __LINE__); 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci dev = ep->dev; 20062306a36Sopenharmony_ci if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { 20162306a36Sopenharmony_ci ret = -ESHUTDOWN; 20262306a36Sopenharmony_ci goto print_err; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* erratum 0119 workaround ties up an endpoint number */ 20662306a36Sopenharmony_ci if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) { 20762306a36Sopenharmony_ci ret = -EDOM; 20862306a36Sopenharmony_ci goto print_err; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (dev->quirks & PLX_PCIE) { 21262306a36Sopenharmony_ci if ((desc->bEndpointAddress & 0x0f) >= 0x0c) { 21362306a36Sopenharmony_ci ret = -EDOM; 21462306a36Sopenharmony_ci goto print_err; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci ep->is_in = !!usb_endpoint_dir_in(desc); 21762306a36Sopenharmony_ci if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) { 21862306a36Sopenharmony_ci ret = -EINVAL; 21962306a36Sopenharmony_ci goto print_err; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* sanity check ep-e/ep-f since their fifos are small */ 22462306a36Sopenharmony_ci max = usb_endpoint_maxp(desc); 22562306a36Sopenharmony_ci if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) { 22662306a36Sopenharmony_ci ret = -ERANGE; 22762306a36Sopenharmony_ci goto print_err; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 23162306a36Sopenharmony_ci _ep->maxpacket = max; 23262306a36Sopenharmony_ci ep->desc = desc; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* ep_reset() has already been called */ 23562306a36Sopenharmony_ci ep->stopped = 0; 23662306a36Sopenharmony_ci ep->wedged = 0; 23762306a36Sopenharmony_ci ep->out_overflow = 0; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* set speed-dependent max packet; may kick in high bandwidth */ 24062306a36Sopenharmony_ci set_max_speed(ep, max); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* set type, direction, address; reset fifo counters */ 24362306a36Sopenharmony_ci writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) { 24662306a36Sopenharmony_ci tmp = readl(&ep->cfg->ep_cfg); 24762306a36Sopenharmony_ci /* If USB ep number doesn't match hardware ep number */ 24862306a36Sopenharmony_ci if ((tmp & 0xf) != usb_endpoint_num(desc)) { 24962306a36Sopenharmony_ci ret = -EINVAL; 25062306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 25162306a36Sopenharmony_ci goto print_err; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci if (ep->is_in) 25462306a36Sopenharmony_ci tmp &= ~USB3380_EP_CFG_MASK_IN; 25562306a36Sopenharmony_ci else 25662306a36Sopenharmony_ci tmp &= ~USB3380_EP_CFG_MASK_OUT; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); 25962306a36Sopenharmony_ci if (type == USB_ENDPOINT_XFER_INT) { 26062306a36Sopenharmony_ci /* erratum 0105 workaround prevents hs NYET */ 26162306a36Sopenharmony_ci if (dev->chiprev == 0100 && 26262306a36Sopenharmony_ci dev->gadget.speed == USB_SPEED_HIGH && 26362306a36Sopenharmony_ci !(desc->bEndpointAddress & USB_DIR_IN)) 26462306a36Sopenharmony_ci writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), 26562306a36Sopenharmony_ci &ep->regs->ep_rsp); 26662306a36Sopenharmony_ci } else if (type == USB_ENDPOINT_XFER_BULK) { 26762306a36Sopenharmony_ci /* catch some particularly blatant driver bugs */ 26862306a36Sopenharmony_ci if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || 26962306a36Sopenharmony_ci (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || 27062306a36Sopenharmony_ci (dev->gadget.speed == USB_SPEED_FULL && max > 64)) { 27162306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 27262306a36Sopenharmony_ci ret = -ERANGE; 27362306a36Sopenharmony_ci goto print_err; 27462306a36Sopenharmony_ci } 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC); 27762306a36Sopenharmony_ci /* Enable this endpoint */ 27862306a36Sopenharmony_ci if (dev->quirks & PLX_LEGACY) { 27962306a36Sopenharmony_ci tmp |= type << ENDPOINT_TYPE; 28062306a36Sopenharmony_ci tmp |= desc->bEndpointAddress; 28162306a36Sopenharmony_ci /* default full fifo lines */ 28262306a36Sopenharmony_ci tmp |= (4 << ENDPOINT_BYTE_COUNT); 28362306a36Sopenharmony_ci tmp |= BIT(ENDPOINT_ENABLE); 28462306a36Sopenharmony_ci ep->is_in = (tmp & USB_DIR_IN) != 0; 28562306a36Sopenharmony_ci } else { 28662306a36Sopenharmony_ci /* In Legacy mode, only OUT endpoints are used */ 28762306a36Sopenharmony_ci if (dev->enhanced_mode && ep->is_in) { 28862306a36Sopenharmony_ci tmp |= type << IN_ENDPOINT_TYPE; 28962306a36Sopenharmony_ci tmp |= BIT(IN_ENDPOINT_ENABLE); 29062306a36Sopenharmony_ci } else { 29162306a36Sopenharmony_ci tmp |= type << OUT_ENDPOINT_TYPE; 29262306a36Sopenharmony_ci tmp |= BIT(OUT_ENDPOINT_ENABLE); 29362306a36Sopenharmony_ci tmp |= (ep->is_in << ENDPOINT_DIRECTION); 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci tmp |= (4 << ENDPOINT_BYTE_COUNT); 29762306a36Sopenharmony_ci if (!dev->enhanced_mode) 29862306a36Sopenharmony_ci tmp |= usb_endpoint_num(desc); 29962306a36Sopenharmony_ci tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Make sure all the registers are written before ep_rsp*/ 30362306a36Sopenharmony_ci wmb(); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* for OUT transfers, block the rx fifo until a read is posted */ 30662306a36Sopenharmony_ci if (!ep->is_in) 30762306a36Sopenharmony_ci writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); 30862306a36Sopenharmony_ci else if (!(dev->quirks & PLX_2280)) { 30962306a36Sopenharmony_ci /* Added for 2282, Don't use nak packets on an in endpoint, 31062306a36Sopenharmony_ci * this was ignored on 2280 31162306a36Sopenharmony_ci */ 31262306a36Sopenharmony_ci writel(BIT(CLEAR_NAK_OUT_PACKETS) | 31362306a36Sopenharmony_ci BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (dev->quirks & PLX_PCIE) 31762306a36Sopenharmony_ci ep_clear_seqnum(ep); 31862306a36Sopenharmony_ci writel(tmp, &ep->cfg->ep_cfg); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* enable irqs */ 32162306a36Sopenharmony_ci if (!ep->dma) { /* pio, per-packet */ 32262306a36Sopenharmony_ci enable_pciirqenb(ep); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) | 32562306a36Sopenharmony_ci BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE); 32662306a36Sopenharmony_ci if (dev->quirks & PLX_2280) 32762306a36Sopenharmony_ci tmp |= readl(&ep->regs->ep_irqenb); 32862306a36Sopenharmony_ci writel(tmp, &ep->regs->ep_irqenb); 32962306a36Sopenharmony_ci } else { /* dma, per-request */ 33062306a36Sopenharmony_ci tmp = BIT((8 + ep->num)); /* completion */ 33162306a36Sopenharmony_ci tmp |= readl(&dev->regs->pciirqenb1); 33262306a36Sopenharmony_ci writel(tmp, &dev->regs->pciirqenb1); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* for short OUT transfers, dma completions can't 33562306a36Sopenharmony_ci * advance the queue; do it pio-style, by hand. 33662306a36Sopenharmony_ci * NOTE erratum 0112 workaround #2 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { 33962306a36Sopenharmony_ci tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); 34062306a36Sopenharmony_ci writel(tmp, &ep->regs->ep_irqenb); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci enable_pciirqenb(ep); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci tmp = desc->bEndpointAddress; 34762306a36Sopenharmony_ci ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n", 34862306a36Sopenharmony_ci _ep->name, tmp & 0x0f, DIR_STRING(tmp), 34962306a36Sopenharmony_ci type_string(desc->bmAttributes), 35062306a36Sopenharmony_ci ep->dma ? "dma" : "pio", max); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* pci writes may still be posted */ 35362306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciprint_err: 35762306a36Sopenharmony_ci dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci u32 result; 36462306a36Sopenharmony_ci int ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(ptr, result, 36762306a36Sopenharmony_ci ((result & mask) == done || 36862306a36Sopenharmony_ci result == U32_MAX), 36962306a36Sopenharmony_ci 1, usec); 37062306a36Sopenharmony_ci if (result == U32_MAX) /* device unplugged */ 37162306a36Sopenharmony_ci return -ENODEV; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic const struct usb_ep_ops net2280_ep_ops; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void ep_reset_228x(struct net2280_regs __iomem *regs, 37962306a36Sopenharmony_ci struct net2280_ep *ep) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci u32 tmp; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ep->desc = NULL; 38462306a36Sopenharmony_ci INIT_LIST_HEAD(&ep->queue); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep, ~0); 38762306a36Sopenharmony_ci ep->ep.ops = &net2280_ep_ops; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* disable the dma, irqs, endpoint... */ 39062306a36Sopenharmony_ci if (ep->dma) { 39162306a36Sopenharmony_ci writel(0, &ep->dma->dmactl); 39262306a36Sopenharmony_ci writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | 39362306a36Sopenharmony_ci BIT(DMA_TRANSACTION_DONE_INTERRUPT) | 39462306a36Sopenharmony_ci BIT(DMA_ABORT), 39562306a36Sopenharmony_ci &ep->dma->dmastat); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci tmp = readl(®s->pciirqenb0); 39862306a36Sopenharmony_ci tmp &= ~BIT(ep->num); 39962306a36Sopenharmony_ci writel(tmp, ®s->pciirqenb0); 40062306a36Sopenharmony_ci } else { 40162306a36Sopenharmony_ci tmp = readl(®s->pciirqenb1); 40262306a36Sopenharmony_ci tmp &= ~BIT((8 + ep->num)); /* completion */ 40362306a36Sopenharmony_ci writel(tmp, ®s->pciirqenb1); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci writel(0, &ep->regs->ep_irqenb); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* init to our chosen defaults, notably so that we NAK OUT 40862306a36Sopenharmony_ci * packets until the driver queues a read (+note erratum 0112) 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci if (!ep->is_in || (ep->dev->quirks & PLX_2280)) { 41162306a36Sopenharmony_ci tmp = BIT(SET_NAK_OUT_PACKETS_MODE) | 41262306a36Sopenharmony_ci BIT(SET_NAK_OUT_PACKETS) | 41362306a36Sopenharmony_ci BIT(CLEAR_EP_HIDE_STATUS_PHASE) | 41462306a36Sopenharmony_ci BIT(CLEAR_INTERRUPT_MODE); 41562306a36Sopenharmony_ci } else { 41662306a36Sopenharmony_ci /* added for 2282 */ 41762306a36Sopenharmony_ci tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) | 41862306a36Sopenharmony_ci BIT(CLEAR_NAK_OUT_PACKETS) | 41962306a36Sopenharmony_ci BIT(CLEAR_EP_HIDE_STATUS_PHASE) | 42062306a36Sopenharmony_ci BIT(CLEAR_INTERRUPT_MODE); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (ep->num != 0) { 42462306a36Sopenharmony_ci tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) | 42562306a36Sopenharmony_ci BIT(CLEAR_ENDPOINT_HALT); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci writel(tmp, &ep->regs->ep_rsp); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* scrub most status bits, and flush any fifo state */ 43062306a36Sopenharmony_ci if (ep->dev->quirks & PLX_2280) 43162306a36Sopenharmony_ci tmp = BIT(FIFO_OVERFLOW) | 43262306a36Sopenharmony_ci BIT(FIFO_UNDERFLOW); 43362306a36Sopenharmony_ci else 43462306a36Sopenharmony_ci tmp = 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci writel(tmp | BIT(TIMEOUT) | 43762306a36Sopenharmony_ci BIT(USB_STALL_SENT) | 43862306a36Sopenharmony_ci BIT(USB_IN_NAK_SENT) | 43962306a36Sopenharmony_ci BIT(USB_IN_ACK_RCVD) | 44062306a36Sopenharmony_ci BIT(USB_OUT_PING_NAK_SENT) | 44162306a36Sopenharmony_ci BIT(USB_OUT_ACK_SENT) | 44262306a36Sopenharmony_ci BIT(FIFO_FLUSH) | 44362306a36Sopenharmony_ci BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | 44462306a36Sopenharmony_ci BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | 44562306a36Sopenharmony_ci BIT(DATA_PACKET_RECEIVED_INTERRUPT) | 44662306a36Sopenharmony_ci BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | 44762306a36Sopenharmony_ci BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | 44862306a36Sopenharmony_ci BIT(DATA_IN_TOKEN_INTERRUPT), 44962306a36Sopenharmony_ci &ep->regs->ep_stat); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* fifo size is handled separately */ 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic void ep_reset_338x(struct net2280_regs __iomem *regs, 45562306a36Sopenharmony_ci struct net2280_ep *ep) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci u32 tmp, dmastat; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ep->desc = NULL; 46062306a36Sopenharmony_ci INIT_LIST_HEAD(&ep->queue); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep, ~0); 46362306a36Sopenharmony_ci ep->ep.ops = &net2280_ep_ops; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* disable the dma, irqs, endpoint... */ 46662306a36Sopenharmony_ci if (ep->dma) { 46762306a36Sopenharmony_ci writel(0, &ep->dma->dmactl); 46862306a36Sopenharmony_ci writel(BIT(DMA_ABORT_DONE_INTERRUPT) | 46962306a36Sopenharmony_ci BIT(DMA_PAUSE_DONE_INTERRUPT) | 47062306a36Sopenharmony_ci BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | 47162306a36Sopenharmony_ci BIT(DMA_TRANSACTION_DONE_INTERRUPT), 47262306a36Sopenharmony_ci /* | BIT(DMA_ABORT), */ 47362306a36Sopenharmony_ci &ep->dma->dmastat); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci dmastat = readl(&ep->dma->dmastat); 47662306a36Sopenharmony_ci if (dmastat == 0x5002) { 47762306a36Sopenharmony_ci ep_warn(ep->dev, "The dmastat return = %x!!\n", 47862306a36Sopenharmony_ci dmastat); 47962306a36Sopenharmony_ci writel(0x5a, &ep->dma->dmastat); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci tmp = readl(®s->pciirqenb0); 48362306a36Sopenharmony_ci tmp &= ~BIT(ep_bit[ep->num]); 48462306a36Sopenharmony_ci writel(tmp, ®s->pciirqenb0); 48562306a36Sopenharmony_ci } else { 48662306a36Sopenharmony_ci if (ep->num < 5) { 48762306a36Sopenharmony_ci tmp = readl(®s->pciirqenb1); 48862306a36Sopenharmony_ci tmp &= ~BIT((8 + ep->num)); /* completion */ 48962306a36Sopenharmony_ci writel(tmp, ®s->pciirqenb1); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci writel(0, &ep->regs->ep_irqenb); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | 49562306a36Sopenharmony_ci BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | 49662306a36Sopenharmony_ci BIT(FIFO_OVERFLOW) | 49762306a36Sopenharmony_ci BIT(DATA_PACKET_RECEIVED_INTERRUPT) | 49862306a36Sopenharmony_ci BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | 49962306a36Sopenharmony_ci BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | 50062306a36Sopenharmony_ci BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci tmp = readl(&ep->cfg->ep_cfg); 50362306a36Sopenharmony_ci if (ep->is_in) 50462306a36Sopenharmony_ci tmp &= ~USB3380_EP_CFG_MASK_IN; 50562306a36Sopenharmony_ci else 50662306a36Sopenharmony_ci tmp &= ~USB3380_EP_CFG_MASK_OUT; 50762306a36Sopenharmony_ci writel(tmp, &ep->cfg->ep_cfg); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic void nuke(struct net2280_ep *); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic int net2280_disable(struct usb_ep *_ep) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct net2280_ep *ep; 51562306a36Sopenharmony_ci unsigned long flags; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 51862306a36Sopenharmony_ci if (!_ep || _ep->name == ep0name) { 51962306a36Sopenharmony_ci pr_err("%s: Invalid ep=%p\n", __func__, _ep); 52062306a36Sopenharmony_ci return -EINVAL; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci spin_lock_irqsave(&ep->dev->lock, flags); 52362306a36Sopenharmony_ci nuke(ep); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (ep->dev->quirks & PLX_PCIE) 52662306a36Sopenharmony_ci ep_reset_338x(ep->dev->regs, ep); 52762306a36Sopenharmony_ci else 52862306a36Sopenharmony_ci ep_reset_228x(ep->dev->regs, ep); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ep_vdbg(ep->dev, "disabled %s %s\n", 53162306a36Sopenharmony_ci ep->dma ? "dma" : "pio", _ep->name); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* synch memory views with the device */ 53462306a36Sopenharmony_ci (void)readl(&ep->cfg->ep_cfg); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (!ep->dma && ep->num >= 1 && ep->num <= 4) 53762306a36Sopenharmony_ci ep->dma = &ep->dev->dma[ep->num - 1]; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->dev->lock, flags); 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic struct usb_request 54662306a36Sopenharmony_ci*net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct net2280_ep *ep; 54962306a36Sopenharmony_ci struct net2280_request *req; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (!_ep) { 55262306a36Sopenharmony_ci pr_err("%s: Invalid ep\n", __func__); 55362306a36Sopenharmony_ci return NULL; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci req = kzalloc(sizeof(*req), gfp_flags); 55862306a36Sopenharmony_ci if (!req) 55962306a36Sopenharmony_ci return NULL; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci INIT_LIST_HEAD(&req->queue); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci /* this dma descriptor may be swapped with the previous dummy */ 56462306a36Sopenharmony_ci if (ep->dma) { 56562306a36Sopenharmony_ci struct net2280_dma *td; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci td = dma_pool_alloc(ep->dev->requests, gfp_flags, 56862306a36Sopenharmony_ci &req->td_dma); 56962306a36Sopenharmony_ci if (!td) { 57062306a36Sopenharmony_ci kfree(req); 57162306a36Sopenharmony_ci return NULL; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci td->dmacount = 0; /* not VALID */ 57462306a36Sopenharmony_ci td->dmadesc = td->dmaaddr; 57562306a36Sopenharmony_ci req->td = td; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci return &req->req; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci struct net2280_ep *ep; 58362306a36Sopenharmony_ci struct net2280_request *req; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 58662306a36Sopenharmony_ci if (!_ep || !_req) { 58762306a36Sopenharmony_ci dev_err(&ep->dev->pdev->dev, "%s: Invalid ep=%p or req=%p\n", 58862306a36Sopenharmony_ci __func__, _ep, _req); 58962306a36Sopenharmony_ci return; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci req = container_of(_req, struct net2280_request, req); 59362306a36Sopenharmony_ci WARN_ON(!list_empty(&req->queue)); 59462306a36Sopenharmony_ci if (req->td) 59562306a36Sopenharmony_ci dma_pool_free(ep->dev->requests, req->td, req->td_dma); 59662306a36Sopenharmony_ci kfree(req); 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* load a packet into the fifo we use for usb IN transfers. 60262306a36Sopenharmony_ci * works for all endpoints. 60362306a36Sopenharmony_ci * 60462306a36Sopenharmony_ci * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo 60562306a36Sopenharmony_ci * at a time, but this code is simpler because it knows it only writes 60662306a36Sopenharmony_ci * one packet. ep-a..ep-d should use dma instead. 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_cistatic void write_fifo(struct net2280_ep *ep, struct usb_request *req) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct net2280_ep_regs __iomem *regs = ep->regs; 61162306a36Sopenharmony_ci u8 *buf; 61262306a36Sopenharmony_ci u32 tmp; 61362306a36Sopenharmony_ci unsigned count, total; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* INVARIANT: fifo is currently empty. (testable) */ 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (req) { 61862306a36Sopenharmony_ci buf = req->buf + req->actual; 61962306a36Sopenharmony_ci prefetch(buf); 62062306a36Sopenharmony_ci total = req->length - req->actual; 62162306a36Sopenharmony_ci } else { 62262306a36Sopenharmony_ci total = 0; 62362306a36Sopenharmony_ci buf = NULL; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* write just one packet at a time */ 62762306a36Sopenharmony_ci count = ep->ep.maxpacket; 62862306a36Sopenharmony_ci if (count > total) /* min() cannot be used on a bitfield */ 62962306a36Sopenharmony_ci count = total; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", 63262306a36Sopenharmony_ci ep->ep.name, count, 63362306a36Sopenharmony_ci (count != ep->ep.maxpacket) ? " (short)" : "", 63462306a36Sopenharmony_ci req); 63562306a36Sopenharmony_ci while (count >= 4) { 63662306a36Sopenharmony_ci /* NOTE be careful if you try to align these. fifo lines 63762306a36Sopenharmony_ci * should normally be full (4 bytes) and successive partial 63862306a36Sopenharmony_ci * lines are ok only in certain cases. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci tmp = get_unaligned((u32 *)buf); 64162306a36Sopenharmony_ci cpu_to_le32s(&tmp); 64262306a36Sopenharmony_ci writel(tmp, ®s->ep_data); 64362306a36Sopenharmony_ci buf += 4; 64462306a36Sopenharmony_ci count -= 4; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* last fifo entry is "short" unless we wrote a full packet. 64862306a36Sopenharmony_ci * also explicitly validate last word in (periodic) transfers 64962306a36Sopenharmony_ci * when maxpacket is not a multiple of 4 bytes. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci if (count || total < ep->ep.maxpacket) { 65262306a36Sopenharmony_ci tmp = count ? get_unaligned((u32 *)buf) : count; 65362306a36Sopenharmony_ci cpu_to_le32s(&tmp); 65462306a36Sopenharmony_ci set_fifo_bytecount(ep, count & 0x03); 65562306a36Sopenharmony_ci writel(tmp, ®s->ep_data); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci /* pci writes may still be posted */ 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* work around erratum 0106: PCI and USB race over the OUT fifo. 66262306a36Sopenharmony_ci * caller guarantees chiprev 0100, out endpoint is NAKing, and 66362306a36Sopenharmony_ci * there's no real data in the fifo. 66462306a36Sopenharmony_ci * 66562306a36Sopenharmony_ci * NOTE: also used in cases where that erratum doesn't apply: 66662306a36Sopenharmony_ci * where the host wrote "too much" data to us. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_cistatic void out_flush(struct net2280_ep *ep) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci u32 __iomem *statp; 67162306a36Sopenharmony_ci u32 tmp; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci statp = &ep->regs->ep_stat; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci tmp = readl(statp); 67662306a36Sopenharmony_ci if (tmp & BIT(NAK_OUT_PACKETS)) { 67762306a36Sopenharmony_ci ep_dbg(ep->dev, "%s %s %08x !NAK\n", 67862306a36Sopenharmony_ci ep->ep.name, __func__, tmp); 67962306a36Sopenharmony_ci writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | 68362306a36Sopenharmony_ci BIT(DATA_PACKET_RECEIVED_INTERRUPT), 68462306a36Sopenharmony_ci statp); 68562306a36Sopenharmony_ci writel(BIT(FIFO_FLUSH), statp); 68662306a36Sopenharmony_ci /* Make sure that stap is written */ 68762306a36Sopenharmony_ci mb(); 68862306a36Sopenharmony_ci tmp = readl(statp); 68962306a36Sopenharmony_ci if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) && 69062306a36Sopenharmony_ci /* high speed did bulk NYET; fifo isn't filling */ 69162306a36Sopenharmony_ci ep->dev->gadget.speed == USB_SPEED_FULL) { 69262306a36Sopenharmony_ci unsigned usec; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci usec = 50; /* 64 byte bulk/interrupt */ 69562306a36Sopenharmony_ci handshake(statp, BIT(USB_OUT_PING_NAK_SENT), 69662306a36Sopenharmony_ci BIT(USB_OUT_PING_NAK_SENT), usec); 69762306a36Sopenharmony_ci /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci/* unload packet(s) from the fifo we use for usb OUT transfers. 70262306a36Sopenharmony_ci * returns true iff the request completed, because of short packet 70362306a36Sopenharmony_ci * or the request buffer having filled with full packets. 70462306a36Sopenharmony_ci * 70562306a36Sopenharmony_ci * for ep-a..ep-d this will read multiple packets out when they 70662306a36Sopenharmony_ci * have been accepted. 70762306a36Sopenharmony_ci */ 70862306a36Sopenharmony_cistatic int read_fifo(struct net2280_ep *ep, struct net2280_request *req) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct net2280_ep_regs __iomem *regs = ep->regs; 71162306a36Sopenharmony_ci u8 *buf = req->req.buf + req->req.actual; 71262306a36Sopenharmony_ci unsigned count, tmp, is_short; 71362306a36Sopenharmony_ci unsigned cleanup = 0, prevent = 0; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* erratum 0106 ... packets coming in during fifo reads might 71662306a36Sopenharmony_ci * be incompletely rejected. not all cases have workarounds. 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci if (ep->dev->chiprev == 0x0100 && 71962306a36Sopenharmony_ci ep->dev->gadget.speed == USB_SPEED_FULL) { 72062306a36Sopenharmony_ci udelay(1); 72162306a36Sopenharmony_ci tmp = readl(&ep->regs->ep_stat); 72262306a36Sopenharmony_ci if ((tmp & BIT(NAK_OUT_PACKETS))) 72362306a36Sopenharmony_ci cleanup = 1; 72462306a36Sopenharmony_ci else if ((tmp & BIT(FIFO_FULL))) { 72562306a36Sopenharmony_ci start_out_naking(ep); 72662306a36Sopenharmony_ci prevent = 1; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci /* else: hope we don't see the problem */ 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* never overflow the rx buffer. the fifo reads packets until 73262306a36Sopenharmony_ci * it sees a short one; we might not be ready for them all. 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci prefetchw(buf); 73562306a36Sopenharmony_ci count = readl(®s->ep_avail); 73662306a36Sopenharmony_ci if (unlikely(count == 0)) { 73762306a36Sopenharmony_ci udelay(1); 73862306a36Sopenharmony_ci tmp = readl(&ep->regs->ep_stat); 73962306a36Sopenharmony_ci count = readl(®s->ep_avail); 74062306a36Sopenharmony_ci /* handled that data already? */ 74162306a36Sopenharmony_ci if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0) 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci tmp = req->req.length - req->req.actual; 74662306a36Sopenharmony_ci if (count > tmp) { 74762306a36Sopenharmony_ci /* as with DMA, data overflow gets flushed */ 74862306a36Sopenharmony_ci if ((tmp % ep->ep.maxpacket) != 0) { 74962306a36Sopenharmony_ci ep_err(ep->dev, 75062306a36Sopenharmony_ci "%s out fifo %d bytes, expected %d\n", 75162306a36Sopenharmony_ci ep->ep.name, count, tmp); 75262306a36Sopenharmony_ci req->req.status = -EOVERFLOW; 75362306a36Sopenharmony_ci cleanup = 1; 75462306a36Sopenharmony_ci /* NAK_OUT_PACKETS will be set, so flushing is safe; 75562306a36Sopenharmony_ci * the next read will start with the next packet 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci } /* else it's a ZLP, no worries */ 75862306a36Sopenharmony_ci count = tmp; 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci req->req.actual += count; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", 76562306a36Sopenharmony_ci ep->ep.name, count, is_short ? " (short)" : "", 76662306a36Sopenharmony_ci cleanup ? " flush" : "", prevent ? " nak" : "", 76762306a36Sopenharmony_ci req, req->req.actual, req->req.length); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci while (count >= 4) { 77062306a36Sopenharmony_ci tmp = readl(®s->ep_data); 77162306a36Sopenharmony_ci cpu_to_le32s(&tmp); 77262306a36Sopenharmony_ci put_unaligned(tmp, (u32 *)buf); 77362306a36Sopenharmony_ci buf += 4; 77462306a36Sopenharmony_ci count -= 4; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci if (count) { 77762306a36Sopenharmony_ci tmp = readl(®s->ep_data); 77862306a36Sopenharmony_ci /* LE conversion is implicit here: */ 77962306a36Sopenharmony_ci do { 78062306a36Sopenharmony_ci *buf++ = (u8) tmp; 78162306a36Sopenharmony_ci tmp >>= 8; 78262306a36Sopenharmony_ci } while (--count); 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci if (cleanup) 78562306a36Sopenharmony_ci out_flush(ep); 78662306a36Sopenharmony_ci if (prevent) { 78762306a36Sopenharmony_ci writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); 78862306a36Sopenharmony_ci (void) readl(&ep->regs->ep_rsp); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci return is_short || req->req.actual == req->req.length; 79262306a36Sopenharmony_ci} 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci/* fill out dma descriptor to match a given request */ 79562306a36Sopenharmony_cistatic void fill_dma_desc(struct net2280_ep *ep, 79662306a36Sopenharmony_ci struct net2280_request *req, int valid) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct net2280_dma *td = req->td; 79962306a36Sopenharmony_ci u32 dmacount = req->req.length; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* don't let DMA continue after a short OUT packet, 80262306a36Sopenharmony_ci * so overruns can't affect the next transfer. 80362306a36Sopenharmony_ci * in case of overruns on max-size packets, we can't 80462306a36Sopenharmony_ci * stop the fifo from filling but we can flush it. 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ci if (ep->is_in) 80762306a36Sopenharmony_ci dmacount |= BIT(DMA_DIRECTION); 80862306a36Sopenharmony_ci if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || 80962306a36Sopenharmony_ci !(ep->dev->quirks & PLX_2280)) 81062306a36Sopenharmony_ci dmacount |= BIT(END_OF_CHAIN); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci req->valid = valid; 81362306a36Sopenharmony_ci if (valid) 81462306a36Sopenharmony_ci dmacount |= BIT(VALID_BIT); 81562306a36Sopenharmony_ci dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* td->dmadesc = previously set by caller */ 81862306a36Sopenharmony_ci td->dmaaddr = cpu_to_le32 (req->req.dma); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ 82162306a36Sopenharmony_ci wmb(); 82262306a36Sopenharmony_ci td->dmacount = cpu_to_le32(dmacount); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic const u32 dmactl_default = 82662306a36Sopenharmony_ci BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) | 82762306a36Sopenharmony_ci BIT(DMA_CLEAR_COUNT_ENABLE) | 82862306a36Sopenharmony_ci /* erratum 0116 workaround part 1 (use POLLING) */ 82962306a36Sopenharmony_ci (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) | 83062306a36Sopenharmony_ci BIT(DMA_VALID_BIT_POLLING_ENABLE) | 83162306a36Sopenharmony_ci BIT(DMA_VALID_BIT_ENABLE) | 83262306a36Sopenharmony_ci BIT(DMA_SCATTER_GATHER_ENABLE) | 83362306a36Sopenharmony_ci /* erratum 0116 workaround part 2 (no AUTOSTART) */ 83462306a36Sopenharmony_ci BIT(DMA_ENABLE); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50); 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic inline void stop_dma(struct net2280_dma_regs __iomem *dma) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl); 84462306a36Sopenharmony_ci spin_stop_dma(dma); 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci struct net2280_dma_regs __iomem *dma = ep->dma; 85062306a36Sopenharmony_ci unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (!(ep->dev->quirks & PLX_2280)) 85362306a36Sopenharmony_ci tmp |= BIT(END_OF_CHAIN); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci writel(tmp, &dma->dmacount); 85662306a36Sopenharmony_ci writel(readl(&dma->dmastat), &dma->dmastat); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci writel(td_dma, &dma->dmadesc); 85962306a36Sopenharmony_ci if (ep->dev->quirks & PLX_PCIE) 86062306a36Sopenharmony_ci dmactl |= BIT(DMA_REQUEST_OUTSTANDING); 86162306a36Sopenharmony_ci writel(dmactl, &dma->dmactl); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ 86462306a36Sopenharmony_ci (void) readl(&ep->dev->pci->pcimstctl); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci writel(BIT(DMA_START), &dma->dmastat); 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic void start_dma(struct net2280_ep *ep, struct net2280_request *req) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci u32 tmp; 87262306a36Sopenharmony_ci struct net2280_dma_regs __iomem *dma = ep->dma; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* FIXME can't use DMA for ZLPs */ 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* on this path we "know" there's no dma active (yet) */ 87762306a36Sopenharmony_ci WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE)); 87862306a36Sopenharmony_ci writel(0, &ep->dma->dmactl); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci /* previous OUT packet might have been short */ 88162306a36Sopenharmony_ci if (!ep->is_in && (readl(&ep->regs->ep_stat) & 88262306a36Sopenharmony_ci BIT(NAK_OUT_PACKETS))) { 88362306a36Sopenharmony_ci writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT), 88462306a36Sopenharmony_ci &ep->regs->ep_stat); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci tmp = readl(&ep->regs->ep_avail); 88762306a36Sopenharmony_ci if (tmp) { 88862306a36Sopenharmony_ci writel(readl(&dma->dmastat), &dma->dmastat); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* transfer all/some fifo data */ 89162306a36Sopenharmony_ci writel(req->req.dma, &dma->dmaaddr); 89262306a36Sopenharmony_ci tmp = min(tmp, req->req.length); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* dma irq, faking scatterlist status */ 89562306a36Sopenharmony_ci req->td->dmacount = cpu_to_le32(req->req.length - tmp); 89662306a36Sopenharmony_ci writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp, 89762306a36Sopenharmony_ci &dma->dmacount); 89862306a36Sopenharmony_ci req->td->dmadesc = 0; 89962306a36Sopenharmony_ci req->valid = 1; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci writel(BIT(DMA_ENABLE), &dma->dmactl); 90262306a36Sopenharmony_ci writel(BIT(DMA_START), &dma->dmastat); 90362306a36Sopenharmony_ci return; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci stop_out_naking(ep); 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci tmp = dmactl_default; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* force packet boundaries between dma requests, but prevent the 91162306a36Sopenharmony_ci * controller from automagically writing a last "short" packet 91262306a36Sopenharmony_ci * (zero length) unless the driver explicitly said to do that. 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_ci if (ep->is_in) { 91562306a36Sopenharmony_ci if (likely((req->req.length % ep->ep.maxpacket) || 91662306a36Sopenharmony_ci req->req.zero)){ 91762306a36Sopenharmony_ci tmp |= BIT(DMA_FIFO_VALIDATE); 91862306a36Sopenharmony_ci ep->in_fifo_validate = 1; 91962306a36Sopenharmony_ci } else 92062306a36Sopenharmony_ci ep->in_fifo_validate = 0; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* init req->td, pointing to the current dummy */ 92462306a36Sopenharmony_ci req->td->dmadesc = cpu_to_le32 (ep->td_dma); 92562306a36Sopenharmony_ci fill_dma_desc(ep, req, 1); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN)); 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci start_queue(ep, tmp, req->td_dma); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic inline void 93362306a36Sopenharmony_ciqueue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci /* swap new dummy for old, link; fill and maybe activate */ 93662306a36Sopenharmony_ci swap(ep->dummy, req->td); 93762306a36Sopenharmony_ci swap(ep->td_dma, req->td_dma); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci req->td->dmadesc = cpu_to_le32 (ep->td_dma); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci fill_dma_desc(ep, req, valid); 94262306a36Sopenharmony_ci} 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic void 94562306a36Sopenharmony_cidone(struct net2280_ep *ep, struct net2280_request *req, int status) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci struct net2280 *dev; 94862306a36Sopenharmony_ci unsigned stopped = ep->stopped; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci list_del_init(&req->queue); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (req->req.status == -EINPROGRESS) 95362306a36Sopenharmony_ci req->req.status = status; 95462306a36Sopenharmony_ci else 95562306a36Sopenharmony_ci status = req->req.status; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci dev = ep->dev; 95862306a36Sopenharmony_ci if (ep->dma) 95962306a36Sopenharmony_ci usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (status && status != -ESHUTDOWN) 96262306a36Sopenharmony_ci ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n", 96362306a36Sopenharmony_ci ep->ep.name, &req->req, status, 96462306a36Sopenharmony_ci req->req.actual, req->req.length); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* don't modify queue heads during completion callback */ 96762306a36Sopenharmony_ci ep->stopped = 1; 96862306a36Sopenharmony_ci spin_unlock(&dev->lock); 96962306a36Sopenharmony_ci usb_gadget_giveback_request(&ep->ep, &req->req); 97062306a36Sopenharmony_ci spin_lock(&dev->lock); 97162306a36Sopenharmony_ci ep->stopped = stopped; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic int 97762306a36Sopenharmony_cinet2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct net2280_request *req; 98062306a36Sopenharmony_ci struct net2280_ep *ep; 98162306a36Sopenharmony_ci struct net2280 *dev; 98262306a36Sopenharmony_ci unsigned long flags; 98362306a36Sopenharmony_ci int ret = 0; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* we always require a cpu-view buffer, so that we can 98662306a36Sopenharmony_ci * always use pio (as fallback or whatever). 98762306a36Sopenharmony_ci */ 98862306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 98962306a36Sopenharmony_ci if (!_ep || (!ep->desc && ep->num != 0)) { 99062306a36Sopenharmony_ci pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); 99162306a36Sopenharmony_ci return -EINVAL; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci req = container_of(_req, struct net2280_request, req); 99462306a36Sopenharmony_ci if (!_req || !_req->complete || !_req->buf || 99562306a36Sopenharmony_ci !list_empty(&req->queue)) { 99662306a36Sopenharmony_ci ret = -EINVAL; 99762306a36Sopenharmony_ci goto print_err; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) { 100062306a36Sopenharmony_ci ret = -EDOM; 100162306a36Sopenharmony_ci goto print_err; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci dev = ep->dev; 100462306a36Sopenharmony_ci if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { 100562306a36Sopenharmony_ci ret = -ESHUTDOWN; 100662306a36Sopenharmony_ci goto print_err; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* FIXME implement PIO fallback for ZLPs with DMA */ 101062306a36Sopenharmony_ci if (ep->dma && _req->length == 0) { 101162306a36Sopenharmony_ci ret = -EOPNOTSUPP; 101262306a36Sopenharmony_ci goto print_err; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* set up dma mapping in case the caller didn't */ 101662306a36Sopenharmony_ci if (ep->dma) { 101762306a36Sopenharmony_ci ret = usb_gadget_map_request(&dev->gadget, _req, 101862306a36Sopenharmony_ci ep->is_in); 101962306a36Sopenharmony_ci if (ret) 102062306a36Sopenharmony_ci goto print_err; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci ep_vdbg(dev, "%s queue req %p, len %d buf %p\n", 102462306a36Sopenharmony_ci _ep->name, _req, _req->length, _req->buf); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci _req->status = -EINPROGRESS; 102962306a36Sopenharmony_ci _req->actual = 0; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci /* kickstart this i/o queue? */ 103262306a36Sopenharmony_ci if (list_empty(&ep->queue) && !ep->stopped && 103362306a36Sopenharmony_ci !((dev->quirks & PLX_PCIE) && ep->dma && 103462306a36Sopenharmony_ci (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) { 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* use DMA if the endpoint supports it, else pio */ 103762306a36Sopenharmony_ci if (ep->dma) 103862306a36Sopenharmony_ci start_dma(ep, req); 103962306a36Sopenharmony_ci else { 104062306a36Sopenharmony_ci /* maybe there's no control data, just status ack */ 104162306a36Sopenharmony_ci if (ep->num == 0 && _req->length == 0) { 104262306a36Sopenharmony_ci allow_status(ep); 104362306a36Sopenharmony_ci done(ep, req, 0); 104462306a36Sopenharmony_ci ep_vdbg(dev, "%s status ack\n", ep->ep.name); 104562306a36Sopenharmony_ci goto done; 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* PIO ... stuff the fifo, or unblock it. */ 104962306a36Sopenharmony_ci if (ep->is_in) 105062306a36Sopenharmony_ci write_fifo(ep, _req); 105162306a36Sopenharmony_ci else { 105262306a36Sopenharmony_ci u32 s; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci /* OUT FIFO might have packet(s) buffered */ 105562306a36Sopenharmony_ci s = readl(&ep->regs->ep_stat); 105662306a36Sopenharmony_ci if ((s & BIT(FIFO_EMPTY)) == 0) { 105762306a36Sopenharmony_ci /* note: _req->short_not_ok is 105862306a36Sopenharmony_ci * ignored here since PIO _always_ 105962306a36Sopenharmony_ci * stops queue advance here, and 106062306a36Sopenharmony_ci * _req->status doesn't change for 106162306a36Sopenharmony_ci * short reads (only _req->actual) 106262306a36Sopenharmony_ci */ 106362306a36Sopenharmony_ci if (read_fifo(ep, req) && 106462306a36Sopenharmony_ci ep->num == 0) { 106562306a36Sopenharmony_ci done(ep, req, 0); 106662306a36Sopenharmony_ci allow_status(ep); 106762306a36Sopenharmony_ci /* don't queue it */ 106862306a36Sopenharmony_ci req = NULL; 106962306a36Sopenharmony_ci } else if (read_fifo(ep, req) && 107062306a36Sopenharmony_ci ep->num != 0) { 107162306a36Sopenharmony_ci done(ep, req, 0); 107262306a36Sopenharmony_ci req = NULL; 107362306a36Sopenharmony_ci } else 107462306a36Sopenharmony_ci s = readl(&ep->regs->ep_stat); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* don't NAK, let the fifo fill */ 107862306a36Sopenharmony_ci if (req && (s & BIT(NAK_OUT_PACKETS))) 107962306a36Sopenharmony_ci writel(BIT(CLEAR_NAK_OUT_PACKETS), 108062306a36Sopenharmony_ci &ep->regs->ep_rsp); 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci } else if (ep->dma) { 108562306a36Sopenharmony_ci int valid = 1; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci if (ep->is_in) { 108862306a36Sopenharmony_ci int expect; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* preventing magic zlps is per-engine state, not 109162306a36Sopenharmony_ci * per-transfer; irq logic must recover hiccups. 109262306a36Sopenharmony_ci */ 109362306a36Sopenharmony_ci expect = likely(req->req.zero || 109462306a36Sopenharmony_ci (req->req.length % ep->ep.maxpacket)); 109562306a36Sopenharmony_ci if (expect != ep->in_fifo_validate) 109662306a36Sopenharmony_ci valid = 0; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci queue_dma(ep, req, valid); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci } /* else the irq handler advances the queue. */ 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci ep->responded = 1; 110362306a36Sopenharmony_ci if (req) 110462306a36Sopenharmony_ci list_add_tail(&req->queue, &ep->queue); 110562306a36Sopenharmony_cidone: 110662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci /* pci writes may still be posted */ 110962306a36Sopenharmony_ci return ret; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ciprint_err: 111262306a36Sopenharmony_ci dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret); 111362306a36Sopenharmony_ci return ret; 111462306a36Sopenharmony_ci} 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_cistatic inline void 111762306a36Sopenharmony_cidma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount, 111862306a36Sopenharmony_ci int status) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); 112162306a36Sopenharmony_ci done(ep, req, status); 112262306a36Sopenharmony_ci} 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cistatic int scan_dma_completions(struct net2280_ep *ep) 112562306a36Sopenharmony_ci{ 112662306a36Sopenharmony_ci int num_completed = 0; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci /* only look at descriptors that were "naturally" retired, 112962306a36Sopenharmony_ci * so fifo and list head state won't matter 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_ci while (!list_empty(&ep->queue)) { 113262306a36Sopenharmony_ci struct net2280_request *req; 113362306a36Sopenharmony_ci u32 req_dma_count; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci req = list_entry(ep->queue.next, 113662306a36Sopenharmony_ci struct net2280_request, queue); 113762306a36Sopenharmony_ci if (!req->valid) 113862306a36Sopenharmony_ci break; 113962306a36Sopenharmony_ci rmb(); 114062306a36Sopenharmony_ci req_dma_count = le32_to_cpup(&req->td->dmacount); 114162306a36Sopenharmony_ci if ((req_dma_count & BIT(VALID_BIT)) != 0) 114262306a36Sopenharmony_ci break; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" 114562306a36Sopenharmony_ci * cases where DMA must be aborted; this code handles 114662306a36Sopenharmony_ci * all non-abort DMA completions. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci if (unlikely(req->td->dmadesc == 0)) { 114962306a36Sopenharmony_ci /* paranoia */ 115062306a36Sopenharmony_ci u32 const ep_dmacount = readl(&ep->dma->dmacount); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (ep_dmacount & DMA_BYTE_COUNT_MASK) 115362306a36Sopenharmony_ci break; 115462306a36Sopenharmony_ci /* single transfer mode */ 115562306a36Sopenharmony_ci dma_done(ep, req, req_dma_count, 0); 115662306a36Sopenharmony_ci num_completed++; 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci } else if (!ep->is_in && 115962306a36Sopenharmony_ci (req->req.length % ep->ep.maxpacket) && 116062306a36Sopenharmony_ci !(ep->dev->quirks & PLX_PCIE)) { 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci u32 const ep_stat = readl(&ep->regs->ep_stat); 116362306a36Sopenharmony_ci /* AVOID TROUBLE HERE by not issuing short reads from 116462306a36Sopenharmony_ci * your gadget driver. That helps avoids errata 0121, 116562306a36Sopenharmony_ci * 0122, and 0124; not all cases trigger the warning. 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ci if ((ep_stat & BIT(NAK_OUT_PACKETS)) == 0) { 116862306a36Sopenharmony_ci ep_warn(ep->dev, "%s lost packet sync!\n", 116962306a36Sopenharmony_ci ep->ep.name); 117062306a36Sopenharmony_ci req->req.status = -EOVERFLOW; 117162306a36Sopenharmony_ci } else { 117262306a36Sopenharmony_ci u32 const ep_avail = readl(&ep->regs->ep_avail); 117362306a36Sopenharmony_ci if (ep_avail) { 117462306a36Sopenharmony_ci /* fifo gets flushed later */ 117562306a36Sopenharmony_ci ep->out_overflow = 1; 117662306a36Sopenharmony_ci ep_dbg(ep->dev, 117762306a36Sopenharmony_ci "%s dma, discard %d len %d\n", 117862306a36Sopenharmony_ci ep->ep.name, ep_avail, 117962306a36Sopenharmony_ci req->req.length); 118062306a36Sopenharmony_ci req->req.status = -EOVERFLOW; 118162306a36Sopenharmony_ci } 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci dma_done(ep, req, req_dma_count, 0); 118562306a36Sopenharmony_ci num_completed++; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci return num_completed; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_cistatic void restart_dma(struct net2280_ep *ep) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci struct net2280_request *req; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (ep->stopped) 119662306a36Sopenharmony_ci return; 119762306a36Sopenharmony_ci req = list_entry(ep->queue.next, struct net2280_request, queue); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci start_dma(ep, req); 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic void abort_dma(struct net2280_ep *ep) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci /* abort the current transfer */ 120562306a36Sopenharmony_ci if (likely(!list_empty(&ep->queue))) { 120662306a36Sopenharmony_ci /* FIXME work around errata 0121, 0122, 0124 */ 120762306a36Sopenharmony_ci writel(BIT(DMA_ABORT), &ep->dma->dmastat); 120862306a36Sopenharmony_ci spin_stop_dma(ep->dma); 120962306a36Sopenharmony_ci } else 121062306a36Sopenharmony_ci stop_dma(ep->dma); 121162306a36Sopenharmony_ci scan_dma_completions(ep); 121262306a36Sopenharmony_ci} 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci/* dequeue ALL requests */ 121562306a36Sopenharmony_cistatic void nuke(struct net2280_ep *ep) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci struct net2280_request *req; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* called with spinlock held */ 122062306a36Sopenharmony_ci ep->stopped = 1; 122162306a36Sopenharmony_ci if (ep->dma) 122262306a36Sopenharmony_ci abort_dma(ep); 122362306a36Sopenharmony_ci while (!list_empty(&ep->queue)) { 122462306a36Sopenharmony_ci req = list_entry(ep->queue.next, 122562306a36Sopenharmony_ci struct net2280_request, 122662306a36Sopenharmony_ci queue); 122762306a36Sopenharmony_ci done(ep, req, -ESHUTDOWN); 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci} 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci/* dequeue JUST ONE request */ 123262306a36Sopenharmony_cistatic int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct net2280_ep *ep; 123562306a36Sopenharmony_ci struct net2280_request *req = NULL; 123662306a36Sopenharmony_ci struct net2280_request *iter; 123762306a36Sopenharmony_ci unsigned long flags; 123862306a36Sopenharmony_ci u32 dmactl; 123962306a36Sopenharmony_ci int stopped; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 124262306a36Sopenharmony_ci if (!_ep || (!ep->desc && ep->num != 0) || !_req) { 124362306a36Sopenharmony_ci pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n", 124462306a36Sopenharmony_ci __func__, _ep, _req); 124562306a36Sopenharmony_ci return -EINVAL; 124662306a36Sopenharmony_ci } 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci spin_lock_irqsave(&ep->dev->lock, flags); 124962306a36Sopenharmony_ci stopped = ep->stopped; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* quiesce dma while we patch the queue */ 125262306a36Sopenharmony_ci dmactl = 0; 125362306a36Sopenharmony_ci ep->stopped = 1; 125462306a36Sopenharmony_ci if (ep->dma) { 125562306a36Sopenharmony_ci dmactl = readl(&ep->dma->dmactl); 125662306a36Sopenharmony_ci /* WARNING erratum 0127 may kick in ... */ 125762306a36Sopenharmony_ci stop_dma(ep->dma); 125862306a36Sopenharmony_ci scan_dma_completions(ep); 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* make sure it's still queued on this endpoint */ 126262306a36Sopenharmony_ci list_for_each_entry(iter, &ep->queue, queue) { 126362306a36Sopenharmony_ci if (&iter->req != _req) 126462306a36Sopenharmony_ci continue; 126562306a36Sopenharmony_ci req = iter; 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci if (!req) { 126962306a36Sopenharmony_ci ep->stopped = stopped; 127062306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->dev->lock, flags); 127162306a36Sopenharmony_ci ep_dbg(ep->dev, "%s: Request mismatch\n", __func__); 127262306a36Sopenharmony_ci return -EINVAL; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci /* queue head may be partially complete. */ 127662306a36Sopenharmony_ci if (ep->queue.next == &req->queue) { 127762306a36Sopenharmony_ci if (ep->dma) { 127862306a36Sopenharmony_ci ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name); 127962306a36Sopenharmony_ci _req->status = -ECONNRESET; 128062306a36Sopenharmony_ci abort_dma(ep); 128162306a36Sopenharmony_ci if (likely(ep->queue.next == &req->queue)) { 128262306a36Sopenharmony_ci /* NOTE: misreports single-transfer mode*/ 128362306a36Sopenharmony_ci req->td->dmacount = 0; /* invalidate */ 128462306a36Sopenharmony_ci dma_done(ep, req, 128562306a36Sopenharmony_ci readl(&ep->dma->dmacount), 128662306a36Sopenharmony_ci -ECONNRESET); 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci } else { 128962306a36Sopenharmony_ci ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name); 129062306a36Sopenharmony_ci done(ep, req, -ECONNRESET); 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci req = NULL; 129362306a36Sopenharmony_ci } 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci if (req) 129662306a36Sopenharmony_ci done(ep, req, -ECONNRESET); 129762306a36Sopenharmony_ci ep->stopped = stopped; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (ep->dma) { 130062306a36Sopenharmony_ci /* turn off dma on inactive queues */ 130162306a36Sopenharmony_ci if (list_empty(&ep->queue)) 130262306a36Sopenharmony_ci stop_dma(ep->dma); 130362306a36Sopenharmony_ci else if (!ep->stopped) { 130462306a36Sopenharmony_ci /* resume current request, or start new one */ 130562306a36Sopenharmony_ci if (req) 130662306a36Sopenharmony_ci writel(dmactl, &ep->dma->dmactl); 130762306a36Sopenharmony_ci else 130862306a36Sopenharmony_ci start_dma(ep, list_entry(ep->queue.next, 130962306a36Sopenharmony_ci struct net2280_request, queue)); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->dev->lock, flags); 131462306a36Sopenharmony_ci return 0; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic int net2280_fifo_status(struct usb_ep *_ep); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic int 132262306a36Sopenharmony_cinet2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci struct net2280_ep *ep; 132562306a36Sopenharmony_ci unsigned long flags; 132662306a36Sopenharmony_ci int retval = 0; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 132962306a36Sopenharmony_ci if (!_ep || (!ep->desc && ep->num != 0)) { 133062306a36Sopenharmony_ci pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); 133162306a36Sopenharmony_ci return -EINVAL; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { 133462306a36Sopenharmony_ci retval = -ESHUTDOWN; 133562306a36Sopenharmony_ci goto print_err; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) 133862306a36Sopenharmony_ci == USB_ENDPOINT_XFER_ISOC) { 133962306a36Sopenharmony_ci retval = -EINVAL; 134062306a36Sopenharmony_ci goto print_err; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci spin_lock_irqsave(&ep->dev->lock, flags); 134462306a36Sopenharmony_ci if (!list_empty(&ep->queue)) { 134562306a36Sopenharmony_ci retval = -EAGAIN; 134662306a36Sopenharmony_ci goto print_unlock; 134762306a36Sopenharmony_ci } else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) { 134862306a36Sopenharmony_ci retval = -EAGAIN; 134962306a36Sopenharmony_ci goto print_unlock; 135062306a36Sopenharmony_ci } else { 135162306a36Sopenharmony_ci ep_vdbg(ep->dev, "%s %s %s\n", _ep->name, 135262306a36Sopenharmony_ci value ? "set" : "clear", 135362306a36Sopenharmony_ci wedged ? "wedge" : "halt"); 135462306a36Sopenharmony_ci /* set/clear, then synch memory views with the device */ 135562306a36Sopenharmony_ci if (value) { 135662306a36Sopenharmony_ci if (ep->num == 0) 135762306a36Sopenharmony_ci ep->dev->protocol_stall = 1; 135862306a36Sopenharmony_ci else 135962306a36Sopenharmony_ci set_halt(ep); 136062306a36Sopenharmony_ci if (wedged) 136162306a36Sopenharmony_ci ep->wedged = 1; 136262306a36Sopenharmony_ci } else { 136362306a36Sopenharmony_ci clear_halt(ep); 136462306a36Sopenharmony_ci if (ep->dev->quirks & PLX_PCIE && 136562306a36Sopenharmony_ci !list_empty(&ep->queue) && ep->td_dma) 136662306a36Sopenharmony_ci restart_dma(ep); 136762306a36Sopenharmony_ci ep->wedged = 0; 136862306a36Sopenharmony_ci } 136962306a36Sopenharmony_ci (void) readl(&ep->regs->ep_rsp); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->dev->lock, flags); 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci return retval; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ciprint_unlock: 137662306a36Sopenharmony_ci spin_unlock_irqrestore(&ep->dev->lock, flags); 137762306a36Sopenharmony_ciprint_err: 137862306a36Sopenharmony_ci dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval); 137962306a36Sopenharmony_ci return retval; 138062306a36Sopenharmony_ci} 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_cistatic int net2280_set_halt(struct usb_ep *_ep, int value) 138362306a36Sopenharmony_ci{ 138462306a36Sopenharmony_ci return net2280_set_halt_and_wedge(_ep, value, 0); 138562306a36Sopenharmony_ci} 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_cistatic int net2280_set_wedge(struct usb_ep *_ep) 138862306a36Sopenharmony_ci{ 138962306a36Sopenharmony_ci if (!_ep || _ep->name == ep0name) { 139062306a36Sopenharmony_ci pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep); 139162306a36Sopenharmony_ci return -EINVAL; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci return net2280_set_halt_and_wedge(_ep, 1, 1); 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_cistatic int net2280_fifo_status(struct usb_ep *_ep) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct net2280_ep *ep; 139962306a36Sopenharmony_ci u32 avail; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 140262306a36Sopenharmony_ci if (!_ep || (!ep->desc && ep->num != 0)) { 140362306a36Sopenharmony_ci pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); 140462306a36Sopenharmony_ci return -ENODEV; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { 140762306a36Sopenharmony_ci dev_err(&ep->dev->pdev->dev, 140862306a36Sopenharmony_ci "%s: Invalid driver=%p or speed=%d\n", 140962306a36Sopenharmony_ci __func__, ep->dev->driver, ep->dev->gadget.speed); 141062306a36Sopenharmony_ci return -ESHUTDOWN; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1); 141462306a36Sopenharmony_ci if (avail > ep->fifo_size) { 141562306a36Sopenharmony_ci dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__); 141662306a36Sopenharmony_ci return -EOVERFLOW; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci if (ep->is_in) 141962306a36Sopenharmony_ci avail = ep->fifo_size - avail; 142062306a36Sopenharmony_ci return avail; 142162306a36Sopenharmony_ci} 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_cistatic void net2280_fifo_flush(struct usb_ep *_ep) 142462306a36Sopenharmony_ci{ 142562306a36Sopenharmony_ci struct net2280_ep *ep; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci ep = container_of(_ep, struct net2280_ep, ep); 142862306a36Sopenharmony_ci if (!_ep || (!ep->desc && ep->num != 0)) { 142962306a36Sopenharmony_ci pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep); 143062306a36Sopenharmony_ci return; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) { 143362306a36Sopenharmony_ci dev_err(&ep->dev->pdev->dev, 143462306a36Sopenharmony_ci "%s: Invalid driver=%p or speed=%d\n", 143562306a36Sopenharmony_ci __func__, ep->dev->driver, ep->dev->gadget.speed); 143662306a36Sopenharmony_ci return; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); 144062306a36Sopenharmony_ci (void) readl(&ep->regs->ep_rsp); 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic const struct usb_ep_ops net2280_ep_ops = { 144462306a36Sopenharmony_ci .enable = net2280_enable, 144562306a36Sopenharmony_ci .disable = net2280_disable, 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci .alloc_request = net2280_alloc_request, 144862306a36Sopenharmony_ci .free_request = net2280_free_request, 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci .queue = net2280_queue, 145162306a36Sopenharmony_ci .dequeue = net2280_dequeue, 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci .set_halt = net2280_set_halt, 145462306a36Sopenharmony_ci .set_wedge = net2280_set_wedge, 145562306a36Sopenharmony_ci .fifo_status = net2280_fifo_status, 145662306a36Sopenharmony_ci .fifo_flush = net2280_fifo_flush, 145762306a36Sopenharmony_ci}; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_cistatic int net2280_get_frame(struct usb_gadget *_gadget) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci struct net2280 *dev; 146462306a36Sopenharmony_ci unsigned long flags; 146562306a36Sopenharmony_ci u16 retval; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci if (!_gadget) 146862306a36Sopenharmony_ci return -ENODEV; 146962306a36Sopenharmony_ci dev = container_of(_gadget, struct net2280, gadget); 147062306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 147162306a36Sopenharmony_ci retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff; 147262306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 147362306a36Sopenharmony_ci return retval; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic int net2280_wakeup(struct usb_gadget *_gadget) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci struct net2280 *dev; 147962306a36Sopenharmony_ci u32 tmp; 148062306a36Sopenharmony_ci unsigned long flags; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci if (!_gadget) 148362306a36Sopenharmony_ci return 0; 148462306a36Sopenharmony_ci dev = container_of(_gadget, struct net2280, gadget); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 148762306a36Sopenharmony_ci tmp = readl(&dev->usb->usbctl); 148862306a36Sopenharmony_ci if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE)) 148962306a36Sopenharmony_ci writel(BIT(GENERATE_RESUME), &dev->usb->usbstat); 149062306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* pci writes may still be posted */ 149362306a36Sopenharmony_ci return 0; 149462306a36Sopenharmony_ci} 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic int net2280_set_selfpowered(struct usb_gadget *_gadget, int value) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct net2280 *dev; 149962306a36Sopenharmony_ci u32 tmp; 150062306a36Sopenharmony_ci unsigned long flags; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (!_gadget) 150362306a36Sopenharmony_ci return 0; 150462306a36Sopenharmony_ci dev = container_of(_gadget, struct net2280, gadget); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 150762306a36Sopenharmony_ci tmp = readl(&dev->usb->usbctl); 150862306a36Sopenharmony_ci if (value) { 150962306a36Sopenharmony_ci tmp |= BIT(SELF_POWERED_STATUS); 151062306a36Sopenharmony_ci _gadget->is_selfpowered = 1; 151162306a36Sopenharmony_ci } else { 151262306a36Sopenharmony_ci tmp &= ~BIT(SELF_POWERED_STATUS); 151362306a36Sopenharmony_ci _gadget->is_selfpowered = 0; 151462306a36Sopenharmony_ci } 151562306a36Sopenharmony_ci writel(tmp, &dev->usb->usbctl); 151662306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci return 0; 151962306a36Sopenharmony_ci} 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_cistatic int net2280_pullup(struct usb_gadget *_gadget, int is_on) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci struct net2280 *dev; 152462306a36Sopenharmony_ci u32 tmp; 152562306a36Sopenharmony_ci unsigned long flags; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (!_gadget) 152862306a36Sopenharmony_ci return -ENODEV; 152962306a36Sopenharmony_ci dev = container_of(_gadget, struct net2280, gadget); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 153262306a36Sopenharmony_ci tmp = readl(&dev->usb->usbctl); 153362306a36Sopenharmony_ci dev->softconnect = (is_on != 0); 153462306a36Sopenharmony_ci if (is_on) { 153562306a36Sopenharmony_ci ep0_start(dev); 153662306a36Sopenharmony_ci writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); 153762306a36Sopenharmony_ci } else { 153862306a36Sopenharmony_ci writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); 153962306a36Sopenharmony_ci stop_activity(dev, NULL); 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci return 0; 154562306a36Sopenharmony_ci} 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_cistatic struct usb_ep *net2280_match_ep(struct usb_gadget *_gadget, 154862306a36Sopenharmony_ci struct usb_endpoint_descriptor *desc, 154962306a36Sopenharmony_ci struct usb_ss_ep_comp_descriptor *ep_comp) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci char name[8]; 155262306a36Sopenharmony_ci struct usb_ep *ep; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT) { 155562306a36Sopenharmony_ci /* ep-e, ep-f are PIO with only 64 byte fifos */ 155662306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep-e"); 155762306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 155862306a36Sopenharmony_ci return ep; 155962306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep-f"); 156062306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 156162306a36Sopenharmony_ci return ep; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci /* USB3380: Only first four endpoints have DMA channels. Allocate 156562306a36Sopenharmony_ci * slower interrupt endpoints from PIO hw endpoints, to allow bulk/isoc 156662306a36Sopenharmony_ci * endpoints use DMA hw endpoints. 156762306a36Sopenharmony_ci */ 156862306a36Sopenharmony_ci if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && 156962306a36Sopenharmony_ci usb_endpoint_dir_in(desc)) { 157062306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep2in"); 157162306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 157262306a36Sopenharmony_ci return ep; 157362306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep4in"); 157462306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 157562306a36Sopenharmony_ci return ep; 157662306a36Sopenharmony_ci } else if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_INT && 157762306a36Sopenharmony_ci !usb_endpoint_dir_in(desc)) { 157862306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep1out"); 157962306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 158062306a36Sopenharmony_ci return ep; 158162306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep3out"); 158262306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 158362306a36Sopenharmony_ci return ep; 158462306a36Sopenharmony_ci } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && 158562306a36Sopenharmony_ci usb_endpoint_dir_in(desc)) { 158662306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep1in"); 158762306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 158862306a36Sopenharmony_ci return ep; 158962306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep3in"); 159062306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 159162306a36Sopenharmony_ci return ep; 159262306a36Sopenharmony_ci } else if (usb_endpoint_type(desc) != USB_ENDPOINT_XFER_BULK && 159362306a36Sopenharmony_ci !usb_endpoint_dir_in(desc)) { 159462306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep2out"); 159562306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 159662306a36Sopenharmony_ci return ep; 159762306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, "ep4out"); 159862306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 159962306a36Sopenharmony_ci return ep; 160062306a36Sopenharmony_ci } 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci /* USB3380: use same address for usb and hardware endpoints */ 160362306a36Sopenharmony_ci snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), 160462306a36Sopenharmony_ci usb_endpoint_dir_in(desc) ? "in" : "out"); 160562306a36Sopenharmony_ci ep = gadget_find_ep_by_name(_gadget, name); 160662306a36Sopenharmony_ci if (ep && usb_gadget_ep_match_desc(_gadget, ep, desc, ep_comp)) 160762306a36Sopenharmony_ci return ep; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci return NULL; 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic int net2280_start(struct usb_gadget *_gadget, 161362306a36Sopenharmony_ci struct usb_gadget_driver *driver); 161462306a36Sopenharmony_cistatic int net2280_stop(struct usb_gadget *_gadget); 161562306a36Sopenharmony_cistatic void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_cistatic const struct usb_gadget_ops net2280_ops = { 161862306a36Sopenharmony_ci .get_frame = net2280_get_frame, 161962306a36Sopenharmony_ci .wakeup = net2280_wakeup, 162062306a36Sopenharmony_ci .set_selfpowered = net2280_set_selfpowered, 162162306a36Sopenharmony_ci .pullup = net2280_pullup, 162262306a36Sopenharmony_ci .udc_start = net2280_start, 162362306a36Sopenharmony_ci .udc_stop = net2280_stop, 162462306a36Sopenharmony_ci .udc_async_callbacks = net2280_async_callbacks, 162562306a36Sopenharmony_ci .match_ep = net2280_match_ep, 162662306a36Sopenharmony_ci}; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FILES 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci/* FIXME move these into procfs, and use seq_file. 163362306a36Sopenharmony_ci * Sysfs _still_ doesn't behave for arbitrarily sized files, 163462306a36Sopenharmony_ci * and also doesn't help products using this with 2.4 kernels. 163562306a36Sopenharmony_ci */ 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci/* "function" sysfs attribute */ 163862306a36Sopenharmony_cistatic ssize_t function_show(struct device *_dev, struct device_attribute *attr, 163962306a36Sopenharmony_ci char *buf) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct net2280 *dev = dev_get_drvdata(_dev); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (!dev->driver || !dev->driver->function || 164462306a36Sopenharmony_ci strlen(dev->driver->function) > PAGE_SIZE) 164562306a36Sopenharmony_ci return 0; 164662306a36Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); 164762306a36Sopenharmony_ci} 164862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(function); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_cistatic ssize_t registers_show(struct device *_dev, 165162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 165262306a36Sopenharmony_ci{ 165362306a36Sopenharmony_ci struct net2280 *dev; 165462306a36Sopenharmony_ci char *next; 165562306a36Sopenharmony_ci unsigned size, t; 165662306a36Sopenharmony_ci unsigned long flags; 165762306a36Sopenharmony_ci int i; 165862306a36Sopenharmony_ci u32 t1, t2; 165962306a36Sopenharmony_ci const char *s; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci dev = dev_get_drvdata(_dev); 166262306a36Sopenharmony_ci next = buf; 166362306a36Sopenharmony_ci size = PAGE_SIZE; 166462306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci if (dev->driver) 166762306a36Sopenharmony_ci s = dev->driver->driver.name; 166862306a36Sopenharmony_ci else 166962306a36Sopenharmony_ci s = "(none)"; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci /* Main Control Registers */ 167262306a36Sopenharmony_ci t = scnprintf(next, size, "%s version " DRIVER_VERSION 167362306a36Sopenharmony_ci ", chiprev %04x\n\n" 167462306a36Sopenharmony_ci "devinit %03x fifoctl %08x gadget '%s'\n" 167562306a36Sopenharmony_ci "pci irqenb0 %02x irqenb1 %08x " 167662306a36Sopenharmony_ci "irqstat0 %04x irqstat1 %08x\n", 167762306a36Sopenharmony_ci driver_name, dev->chiprev, 167862306a36Sopenharmony_ci readl(&dev->regs->devinit), 167962306a36Sopenharmony_ci readl(&dev->regs->fifoctl), 168062306a36Sopenharmony_ci s, 168162306a36Sopenharmony_ci readl(&dev->regs->pciirqenb0), 168262306a36Sopenharmony_ci readl(&dev->regs->pciirqenb1), 168362306a36Sopenharmony_ci readl(&dev->regs->irqstat0), 168462306a36Sopenharmony_ci readl(&dev->regs->irqstat1)); 168562306a36Sopenharmony_ci size -= t; 168662306a36Sopenharmony_ci next += t; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci /* USB Control Registers */ 168962306a36Sopenharmony_ci t1 = readl(&dev->usb->usbctl); 169062306a36Sopenharmony_ci t2 = readl(&dev->usb->usbstat); 169162306a36Sopenharmony_ci if (t1 & BIT(VBUS_PIN)) { 169262306a36Sopenharmony_ci if (t2 & BIT(HIGH_SPEED)) 169362306a36Sopenharmony_ci s = "high speed"; 169462306a36Sopenharmony_ci else if (dev->gadget.speed == USB_SPEED_UNKNOWN) 169562306a36Sopenharmony_ci s = "powered"; 169662306a36Sopenharmony_ci else 169762306a36Sopenharmony_ci s = "full speed"; 169862306a36Sopenharmony_ci /* full speed bit (6) not working?? */ 169962306a36Sopenharmony_ci } else 170062306a36Sopenharmony_ci s = "not attached"; 170162306a36Sopenharmony_ci t = scnprintf(next, size, 170262306a36Sopenharmony_ci "stdrsp %08x usbctl %08x usbstat %08x " 170362306a36Sopenharmony_ci "addr 0x%02x (%s)\n", 170462306a36Sopenharmony_ci readl(&dev->usb->stdrsp), t1, t2, 170562306a36Sopenharmony_ci readl(&dev->usb->ouraddr), s); 170662306a36Sopenharmony_ci size -= t; 170762306a36Sopenharmony_ci next += t; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci /* PCI Master Control Registers */ 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci /* DMA Control Registers */ 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci /* Configurable EP Control Registers */ 171462306a36Sopenharmony_ci for (i = 0; i < dev->n_ep; i++) { 171562306a36Sopenharmony_ci struct net2280_ep *ep; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci ep = &dev->ep[i]; 171862306a36Sopenharmony_ci if (i && !ep->desc) 171962306a36Sopenharmony_ci continue; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci t1 = readl(&ep->cfg->ep_cfg); 172262306a36Sopenharmony_ci t2 = readl(&ep->regs->ep_rsp) & 0xff; 172362306a36Sopenharmony_ci t = scnprintf(next, size, 172462306a36Sopenharmony_ci "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" 172562306a36Sopenharmony_ci "irqenb %02x\n", 172662306a36Sopenharmony_ci ep->ep.name, t1, t2, 172762306a36Sopenharmony_ci (t2 & BIT(CLEAR_NAK_OUT_PACKETS)) 172862306a36Sopenharmony_ci ? "NAK " : "", 172962306a36Sopenharmony_ci (t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE)) 173062306a36Sopenharmony_ci ? "hide " : "", 173162306a36Sopenharmony_ci (t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR)) 173262306a36Sopenharmony_ci ? "CRC " : "", 173362306a36Sopenharmony_ci (t2 & BIT(CLEAR_INTERRUPT_MODE)) 173462306a36Sopenharmony_ci ? "interrupt " : "", 173562306a36Sopenharmony_ci (t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) 173662306a36Sopenharmony_ci ? "status " : "", 173762306a36Sopenharmony_ci (t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE)) 173862306a36Sopenharmony_ci ? "NAKmode " : "", 173962306a36Sopenharmony_ci (t2 & BIT(CLEAR_ENDPOINT_TOGGLE)) 174062306a36Sopenharmony_ci ? "DATA1 " : "DATA0 ", 174162306a36Sopenharmony_ci (t2 & BIT(CLEAR_ENDPOINT_HALT)) 174262306a36Sopenharmony_ci ? "HALT " : "", 174362306a36Sopenharmony_ci readl(&ep->regs->ep_irqenb)); 174462306a36Sopenharmony_ci size -= t; 174562306a36Sopenharmony_ci next += t; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci t = scnprintf(next, size, 174862306a36Sopenharmony_ci "\tstat %08x avail %04x " 174962306a36Sopenharmony_ci "(ep%d%s-%s)%s\n", 175062306a36Sopenharmony_ci readl(&ep->regs->ep_stat), 175162306a36Sopenharmony_ci readl(&ep->regs->ep_avail), 175262306a36Sopenharmony_ci t1 & 0x0f, DIR_STRING(t1), 175362306a36Sopenharmony_ci type_string(t1 >> 8), 175462306a36Sopenharmony_ci ep->stopped ? "*" : ""); 175562306a36Sopenharmony_ci size -= t; 175662306a36Sopenharmony_ci next += t; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (!ep->dma) 175962306a36Sopenharmony_ci continue; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci t = scnprintf(next, size, 176262306a36Sopenharmony_ci " dma\tctl %08x stat %08x count %08x\n" 176362306a36Sopenharmony_ci "\taddr %08x desc %08x\n", 176462306a36Sopenharmony_ci readl(&ep->dma->dmactl), 176562306a36Sopenharmony_ci readl(&ep->dma->dmastat), 176662306a36Sopenharmony_ci readl(&ep->dma->dmacount), 176762306a36Sopenharmony_ci readl(&ep->dma->dmaaddr), 176862306a36Sopenharmony_ci readl(&ep->dma->dmadesc)); 176962306a36Sopenharmony_ci size -= t; 177062306a36Sopenharmony_ci next += t; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci /* Indexed Registers (none yet) */ 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci /* Statistics */ 177762306a36Sopenharmony_ci t = scnprintf(next, size, "\nirqs: "); 177862306a36Sopenharmony_ci size -= t; 177962306a36Sopenharmony_ci next += t; 178062306a36Sopenharmony_ci for (i = 0; i < dev->n_ep; i++) { 178162306a36Sopenharmony_ci struct net2280_ep *ep; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci ep = &dev->ep[i]; 178462306a36Sopenharmony_ci if (i && !ep->irqs) 178562306a36Sopenharmony_ci continue; 178662306a36Sopenharmony_ci t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs); 178762306a36Sopenharmony_ci size -= t; 178862306a36Sopenharmony_ci next += t; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci } 179162306a36Sopenharmony_ci t = scnprintf(next, size, "\n"); 179262306a36Sopenharmony_ci size -= t; 179362306a36Sopenharmony_ci next += t; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci return PAGE_SIZE - size; 179862306a36Sopenharmony_ci} 179962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(registers); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_cistatic ssize_t queues_show(struct device *_dev, struct device_attribute *attr, 180262306a36Sopenharmony_ci char *buf) 180362306a36Sopenharmony_ci{ 180462306a36Sopenharmony_ci struct net2280 *dev; 180562306a36Sopenharmony_ci char *next; 180662306a36Sopenharmony_ci unsigned size; 180762306a36Sopenharmony_ci unsigned long flags; 180862306a36Sopenharmony_ci int i; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci dev = dev_get_drvdata(_dev); 181162306a36Sopenharmony_ci next = buf; 181262306a36Sopenharmony_ci size = PAGE_SIZE; 181362306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci for (i = 0; i < dev->n_ep; i++) { 181662306a36Sopenharmony_ci struct net2280_ep *ep = &dev->ep[i]; 181762306a36Sopenharmony_ci struct net2280_request *req; 181862306a36Sopenharmony_ci int t; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci if (i != 0) { 182162306a36Sopenharmony_ci const struct usb_endpoint_descriptor *d; 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci d = ep->desc; 182462306a36Sopenharmony_ci if (!d) 182562306a36Sopenharmony_ci continue; 182662306a36Sopenharmony_ci t = d->bEndpointAddress; 182762306a36Sopenharmony_ci t = scnprintf(next, size, 182862306a36Sopenharmony_ci "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", 182962306a36Sopenharmony_ci ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, 183062306a36Sopenharmony_ci (t & USB_DIR_IN) ? "in" : "out", 183162306a36Sopenharmony_ci type_string(d->bmAttributes), 183262306a36Sopenharmony_ci usb_endpoint_maxp(d), 183362306a36Sopenharmony_ci ep->dma ? "dma" : "pio", ep->fifo_size 183462306a36Sopenharmony_ci ); 183562306a36Sopenharmony_ci } else /* ep0 should only have one transfer queued */ 183662306a36Sopenharmony_ci t = scnprintf(next, size, "ep0 max 64 pio %s\n", 183762306a36Sopenharmony_ci ep->is_in ? "in" : "out"); 183862306a36Sopenharmony_ci if (t <= 0 || t > size) 183962306a36Sopenharmony_ci goto done; 184062306a36Sopenharmony_ci size -= t; 184162306a36Sopenharmony_ci next += t; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci if (list_empty(&ep->queue)) { 184462306a36Sopenharmony_ci t = scnprintf(next, size, "\t(nothing queued)\n"); 184562306a36Sopenharmony_ci if (t <= 0 || t > size) 184662306a36Sopenharmony_ci goto done; 184762306a36Sopenharmony_ci size -= t; 184862306a36Sopenharmony_ci next += t; 184962306a36Sopenharmony_ci continue; 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci list_for_each_entry(req, &ep->queue, queue) { 185262306a36Sopenharmony_ci if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc)) 185362306a36Sopenharmony_ci t = scnprintf(next, size, 185462306a36Sopenharmony_ci "\treq %p len %d/%d " 185562306a36Sopenharmony_ci "buf %p (dmacount %08x)\n", 185662306a36Sopenharmony_ci &req->req, req->req.actual, 185762306a36Sopenharmony_ci req->req.length, req->req.buf, 185862306a36Sopenharmony_ci readl(&ep->dma->dmacount)); 185962306a36Sopenharmony_ci else 186062306a36Sopenharmony_ci t = scnprintf(next, size, 186162306a36Sopenharmony_ci "\treq %p len %d/%d buf %p\n", 186262306a36Sopenharmony_ci &req->req, req->req.actual, 186362306a36Sopenharmony_ci req->req.length, req->req.buf); 186462306a36Sopenharmony_ci if (t <= 0 || t > size) 186562306a36Sopenharmony_ci goto done; 186662306a36Sopenharmony_ci size -= t; 186762306a36Sopenharmony_ci next += t; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (ep->dma) { 187062306a36Sopenharmony_ci struct net2280_dma *td; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci td = req->td; 187362306a36Sopenharmony_ci t = scnprintf(next, size, "\t td %08x " 187462306a36Sopenharmony_ci " count %08x buf %08x desc %08x\n", 187562306a36Sopenharmony_ci (u32) req->td_dma, 187662306a36Sopenharmony_ci le32_to_cpu(td->dmacount), 187762306a36Sopenharmony_ci le32_to_cpu(td->dmaaddr), 187862306a36Sopenharmony_ci le32_to_cpu(td->dmadesc)); 187962306a36Sopenharmony_ci if (t <= 0 || t > size) 188062306a36Sopenharmony_ci goto done; 188162306a36Sopenharmony_ci size -= t; 188262306a36Sopenharmony_ci next += t; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci } 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cidone: 188862306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 188962306a36Sopenharmony_ci return PAGE_SIZE - size; 189062306a36Sopenharmony_ci} 189162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(queues); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci#else 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci#define device_create_file(a, b) (0) 189762306a36Sopenharmony_ci#define device_remove_file(a, b) do { } while (0) 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci#endif 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci/* another driver-specific mode might be a request type doing dma 190462306a36Sopenharmony_ci * to/from another device fifo instead of to/from memory. 190562306a36Sopenharmony_ci */ 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_cistatic void set_fifo_mode(struct net2280 *dev, int mode) 190862306a36Sopenharmony_ci{ 190962306a36Sopenharmony_ci /* keeping high bits preserves BAR2 */ 191062306a36Sopenharmony_ci writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ 191362306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->gadget.ep_list); 191462306a36Sopenharmony_ci list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list); 191562306a36Sopenharmony_ci list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list); 191662306a36Sopenharmony_ci switch (mode) { 191762306a36Sopenharmony_ci case 0: 191862306a36Sopenharmony_ci list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); 191962306a36Sopenharmony_ci list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list); 192062306a36Sopenharmony_ci dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024; 192162306a36Sopenharmony_ci break; 192262306a36Sopenharmony_ci case 1: 192362306a36Sopenharmony_ci dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048; 192462306a36Sopenharmony_ci break; 192562306a36Sopenharmony_ci case 2: 192662306a36Sopenharmony_ci list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list); 192762306a36Sopenharmony_ci dev->ep[1].fifo_size = 2048; 192862306a36Sopenharmony_ci dev->ep[2].fifo_size = 1024; 192962306a36Sopenharmony_ci break; 193062306a36Sopenharmony_ci } 193162306a36Sopenharmony_ci /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ 193262306a36Sopenharmony_ci list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list); 193362306a36Sopenharmony_ci list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list); 193462306a36Sopenharmony_ci} 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_cistatic void defect7374_disable_data_eps(struct net2280 *dev) 193762306a36Sopenharmony_ci{ 193862306a36Sopenharmony_ci /* 193962306a36Sopenharmony_ci * For Defect 7374, disable data EPs (and more): 194062306a36Sopenharmony_ci * - This phase undoes the earlier phase of the Defect 7374 workaround, 194162306a36Sopenharmony_ci * returing ep regs back to normal. 194262306a36Sopenharmony_ci */ 194362306a36Sopenharmony_ci struct net2280_ep *ep; 194462306a36Sopenharmony_ci int i; 194562306a36Sopenharmony_ci unsigned char ep_sel; 194662306a36Sopenharmony_ci u32 tmp_reg; 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci for (i = 1; i < 5; i++) { 194962306a36Sopenharmony_ci ep = &dev->ep[i]; 195062306a36Sopenharmony_ci writel(i, &ep->cfg->ep_cfg); 195162306a36Sopenharmony_ci } 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */ 195462306a36Sopenharmony_ci for (i = 0; i < 6; i++) 195562306a36Sopenharmony_ci writel(0, &dev->dep[i].dep_cfg); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci for (ep_sel = 0; ep_sel <= 21; ep_sel++) { 195862306a36Sopenharmony_ci /* Select an endpoint for subsequent operations: */ 195962306a36Sopenharmony_ci tmp_reg = readl(&dev->plregs->pl_ep_ctrl); 196062306a36Sopenharmony_ci writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) || 196362306a36Sopenharmony_ci ep_sel == 18 || ep_sel == 20) 196462306a36Sopenharmony_ci continue; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* Change settings on some selected endpoints */ 196762306a36Sopenharmony_ci tmp_reg = readl(&dev->plregs->pl_ep_cfg_4); 196862306a36Sopenharmony_ci tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR); 196962306a36Sopenharmony_ci writel(tmp_reg, &dev->plregs->pl_ep_cfg_4); 197062306a36Sopenharmony_ci tmp_reg = readl(&dev->plregs->pl_ep_ctrl); 197162306a36Sopenharmony_ci tmp_reg |= BIT(EP_INITIALIZED); 197262306a36Sopenharmony_ci writel(tmp_reg, &dev->plregs->pl_ep_ctrl); 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci} 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_cistatic void defect7374_enable_data_eps_zero(struct net2280 *dev) 197762306a36Sopenharmony_ci{ 197862306a36Sopenharmony_ci u32 tmp = 0, tmp_reg; 197962306a36Sopenharmony_ci u32 scratch; 198062306a36Sopenharmony_ci int i; 198162306a36Sopenharmony_ci unsigned char ep_sel; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci scratch = get_idx_reg(dev->regs, SCRATCH); 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci WARN_ON((scratch & (0xf << DEFECT7374_FSM_FIELD)) 198662306a36Sopenharmony_ci == DEFECT7374_FSM_SS_CONTROL_READ); 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci scratch &= ~(0xf << DEFECT7374_FSM_FIELD); 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci ep_warn(dev, "Operate Defect 7374 workaround soft this time"); 199162306a36Sopenharmony_ci ep_warn(dev, "It will operate on cold-reboot and SS connect"); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci /*GPEPs:*/ 199462306a36Sopenharmony_ci tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | 199562306a36Sopenharmony_ci (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | 199662306a36Sopenharmony_ci ((dev->enhanced_mode) ? 199762306a36Sopenharmony_ci BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) : 199862306a36Sopenharmony_ci BIT(ENDPOINT_ENABLE))); 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci for (i = 1; i < 5; i++) 200162306a36Sopenharmony_ci writel(tmp, &dev->ep[i].cfg->ep_cfg); 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci /* CSRIN, PCIIN, STATIN, RCIN*/ 200462306a36Sopenharmony_ci tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE)); 200562306a36Sopenharmony_ci writel(tmp, &dev->dep[1].dep_cfg); 200662306a36Sopenharmony_ci writel(tmp, &dev->dep[3].dep_cfg); 200762306a36Sopenharmony_ci writel(tmp, &dev->dep[4].dep_cfg); 200862306a36Sopenharmony_ci writel(tmp, &dev->dep[5].dep_cfg); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci /*Implemented for development and debug. 201162306a36Sopenharmony_ci * Can be refined/tuned later.*/ 201262306a36Sopenharmony_ci for (ep_sel = 0; ep_sel <= 21; ep_sel++) { 201362306a36Sopenharmony_ci /* Select an endpoint for subsequent operations: */ 201462306a36Sopenharmony_ci tmp_reg = readl(&dev->plregs->pl_ep_ctrl); 201562306a36Sopenharmony_ci writel(((tmp_reg & ~0x1f) | ep_sel), 201662306a36Sopenharmony_ci &dev->plregs->pl_ep_ctrl); 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci if (ep_sel == 1) { 201962306a36Sopenharmony_ci tmp = 202062306a36Sopenharmony_ci (readl(&dev->plregs->pl_ep_ctrl) | 202162306a36Sopenharmony_ci BIT(CLEAR_ACK_ERROR_CODE) | 0); 202262306a36Sopenharmony_ci writel(tmp, &dev->plregs->pl_ep_ctrl); 202362306a36Sopenharmony_ci continue; 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) || 202762306a36Sopenharmony_ci ep_sel == 18 || ep_sel == 20) 202862306a36Sopenharmony_ci continue; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci tmp = (readl(&dev->plregs->pl_ep_cfg_4) | 203162306a36Sopenharmony_ci BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0); 203262306a36Sopenharmony_ci writel(tmp, &dev->plregs->pl_ep_cfg_4); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci tmp = readl(&dev->plregs->pl_ep_ctrl) & 203562306a36Sopenharmony_ci ~BIT(EP_INITIALIZED); 203662306a36Sopenharmony_ci writel(tmp, &dev->plregs->pl_ep_ctrl); 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci /* Set FSM to focus on the first Control Read: 204162306a36Sopenharmony_ci * - Tip: Connection speed is known upon the first 204262306a36Sopenharmony_ci * setup request.*/ 204362306a36Sopenharmony_ci scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ; 204462306a36Sopenharmony_ci set_idx_reg(dev->regs, SCRATCH, scratch); 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci} 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci/* keeping it simple: 204962306a36Sopenharmony_ci * - one bus driver, initted first; 205062306a36Sopenharmony_ci * - one function driver, initted second 205162306a36Sopenharmony_ci * 205262306a36Sopenharmony_ci * most of the work to support multiple net2280 controllers would 205362306a36Sopenharmony_ci * be to associate this gadget driver (yes?) with all of them, or 205462306a36Sopenharmony_ci * perhaps to bind specific drivers to specific devices. 205562306a36Sopenharmony_ci */ 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_cistatic void usb_reset_228x(struct net2280 *dev) 205862306a36Sopenharmony_ci{ 205962306a36Sopenharmony_ci u32 tmp; 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci dev->gadget.speed = USB_SPEED_UNKNOWN; 206262306a36Sopenharmony_ci (void) readl(&dev->usb->usbctl); 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci net2280_led_init(dev); 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci /* disable automatic responses, and irqs */ 206762306a36Sopenharmony_ci writel(0, &dev->usb->stdrsp); 206862306a36Sopenharmony_ci writel(0, &dev->regs->pciirqenb0); 206962306a36Sopenharmony_ci writel(0, &dev->regs->pciirqenb1); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci /* clear old dma and irq state */ 207262306a36Sopenharmony_ci for (tmp = 0; tmp < 4; tmp++) { 207362306a36Sopenharmony_ci struct net2280_ep *ep = &dev->ep[tmp + 1]; 207462306a36Sopenharmony_ci if (ep->dma) 207562306a36Sopenharmony_ci abort_dma(ep); 207662306a36Sopenharmony_ci } 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci writel(~0, &dev->regs->irqstat0), 207962306a36Sopenharmony_ci writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci /* reset, and enable pci */ 208262306a36Sopenharmony_ci tmp = readl(&dev->regs->devinit) | 208362306a36Sopenharmony_ci BIT(PCI_ENABLE) | 208462306a36Sopenharmony_ci BIT(FIFO_SOFT_RESET) | 208562306a36Sopenharmony_ci BIT(USB_SOFT_RESET) | 208662306a36Sopenharmony_ci BIT(M8051_RESET); 208762306a36Sopenharmony_ci writel(tmp, &dev->regs->devinit); 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci /* standard fifo and endpoint allocations */ 209062306a36Sopenharmony_ci set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0); 209162306a36Sopenharmony_ci} 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_cistatic void usb_reset_338x(struct net2280 *dev) 209462306a36Sopenharmony_ci{ 209562306a36Sopenharmony_ci u32 tmp; 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci dev->gadget.speed = USB_SPEED_UNKNOWN; 209862306a36Sopenharmony_ci (void)readl(&dev->usb->usbctl); 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci net2280_led_init(dev); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci if (dev->bug7734_patched) { 210362306a36Sopenharmony_ci /* disable automatic responses, and irqs */ 210462306a36Sopenharmony_ci writel(0, &dev->usb->stdrsp); 210562306a36Sopenharmony_ci writel(0, &dev->regs->pciirqenb0); 210662306a36Sopenharmony_ci writel(0, &dev->regs->pciirqenb1); 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci /* clear old dma and irq state */ 211062306a36Sopenharmony_ci for (tmp = 0; tmp < 4; tmp++) { 211162306a36Sopenharmony_ci struct net2280_ep *ep = &dev->ep[tmp + 1]; 211262306a36Sopenharmony_ci struct net2280_dma_regs __iomem *dma; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci if (ep->dma) { 211562306a36Sopenharmony_ci abort_dma(ep); 211662306a36Sopenharmony_ci } else { 211762306a36Sopenharmony_ci dma = &dev->dma[tmp]; 211862306a36Sopenharmony_ci writel(BIT(DMA_ABORT), &dma->dmastat); 211962306a36Sopenharmony_ci writel(0, &dma->dmactl); 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci } 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci if (dev->bug7734_patched) { 212662306a36Sopenharmony_ci /* reset, and enable pci */ 212762306a36Sopenharmony_ci tmp = readl(&dev->regs->devinit) | 212862306a36Sopenharmony_ci BIT(PCI_ENABLE) | 212962306a36Sopenharmony_ci BIT(FIFO_SOFT_RESET) | 213062306a36Sopenharmony_ci BIT(USB_SOFT_RESET) | 213162306a36Sopenharmony_ci BIT(M8051_RESET); 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci writel(tmp, &dev->regs->devinit); 213462306a36Sopenharmony_ci } 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */ 213762306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->gadget.ep_list); 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci for (tmp = 1; tmp < dev->n_ep; tmp++) 214062306a36Sopenharmony_ci list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list); 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci} 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_cistatic void usb_reset(struct net2280 *dev) 214562306a36Sopenharmony_ci{ 214662306a36Sopenharmony_ci if (dev->quirks & PLX_LEGACY) 214762306a36Sopenharmony_ci return usb_reset_228x(dev); 214862306a36Sopenharmony_ci return usb_reset_338x(dev); 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_cistatic void usb_reinit_228x(struct net2280 *dev) 215262306a36Sopenharmony_ci{ 215362306a36Sopenharmony_ci u32 tmp; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci /* basic endpoint init */ 215662306a36Sopenharmony_ci for (tmp = 0; tmp < 7; tmp++) { 215762306a36Sopenharmony_ci struct net2280_ep *ep = &dev->ep[tmp]; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci ep->ep.name = ep_info_dft[tmp].name; 216062306a36Sopenharmony_ci ep->ep.caps = ep_info_dft[tmp].caps; 216162306a36Sopenharmony_ci ep->dev = dev; 216262306a36Sopenharmony_ci ep->num = tmp; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci if (tmp > 0 && tmp <= 4) { 216562306a36Sopenharmony_ci ep->fifo_size = 1024; 216662306a36Sopenharmony_ci ep->dma = &dev->dma[tmp - 1]; 216762306a36Sopenharmony_ci } else 216862306a36Sopenharmony_ci ep->fifo_size = 64; 216962306a36Sopenharmony_ci ep->regs = &dev->epregs[tmp]; 217062306a36Sopenharmony_ci ep->cfg = &dev->epregs[tmp]; 217162306a36Sopenharmony_ci ep_reset_228x(dev->regs, ep); 217262306a36Sopenharmony_ci } 217362306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64); 217462306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64); 217562306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci dev->gadget.ep0 = &dev->ep[0].ep; 217862306a36Sopenharmony_ci dev->ep[0].stopped = 0; 217962306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci /* we want to prevent lowlevel/insecure access from the USB host, 218262306a36Sopenharmony_ci * but erratum 0119 means this enable bit is ignored 218362306a36Sopenharmony_ci */ 218462306a36Sopenharmony_ci for (tmp = 0; tmp < 5; tmp++) 218562306a36Sopenharmony_ci writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg); 218662306a36Sopenharmony_ci} 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_cistatic void usb_reinit_338x(struct net2280 *dev) 218962306a36Sopenharmony_ci{ 219062306a36Sopenharmony_ci int i; 219162306a36Sopenharmony_ci u32 tmp, val; 219262306a36Sopenharmony_ci static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 }; 219362306a36Sopenharmony_ci static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00, 219462306a36Sopenharmony_ci 0x00, 0xC0, 0x00, 0xC0 }; 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci /* basic endpoint init */ 219762306a36Sopenharmony_ci for (i = 0; i < dev->n_ep; i++) { 219862306a36Sopenharmony_ci struct net2280_ep *ep = &dev->ep[i]; 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci ep->ep.name = dev->enhanced_mode ? ep_info_adv[i].name : 220162306a36Sopenharmony_ci ep_info_dft[i].name; 220262306a36Sopenharmony_ci ep->ep.caps = dev->enhanced_mode ? ep_info_adv[i].caps : 220362306a36Sopenharmony_ci ep_info_dft[i].caps; 220462306a36Sopenharmony_ci ep->dev = dev; 220562306a36Sopenharmony_ci ep->num = i; 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci if (i > 0 && i <= 4) 220862306a36Sopenharmony_ci ep->dma = &dev->dma[i - 1]; 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci if (dev->enhanced_mode) { 221162306a36Sopenharmony_ci ep->cfg = &dev->epregs[ne[i]]; 221262306a36Sopenharmony_ci /* 221362306a36Sopenharmony_ci * Set USB endpoint number, hardware allows same number 221462306a36Sopenharmony_ci * in both directions. 221562306a36Sopenharmony_ci */ 221662306a36Sopenharmony_ci if (i > 0 && i < 5) 221762306a36Sopenharmony_ci writel(ne[i], &ep->cfg->ep_cfg); 221862306a36Sopenharmony_ci ep->regs = (struct net2280_ep_regs __iomem *) 221962306a36Sopenharmony_ci (((void __iomem *)&dev->epregs[ne[i]]) + 222062306a36Sopenharmony_ci ep_reg_addr[i]); 222162306a36Sopenharmony_ci } else { 222262306a36Sopenharmony_ci ep->cfg = &dev->epregs[i]; 222362306a36Sopenharmony_ci ep->regs = &dev->epregs[i]; 222462306a36Sopenharmony_ci } 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci ep->fifo_size = (i != 0) ? 2048 : 512; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci ep_reset_338x(dev->regs, ep); 222962306a36Sopenharmony_ci } 223062306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512); 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci dev->gadget.ep0 = &dev->ep[0].ep; 223362306a36Sopenharmony_ci dev->ep[0].stopped = 0; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci /* Link layer set up */ 223662306a36Sopenharmony_ci if (dev->bug7734_patched) { 223762306a36Sopenharmony_ci tmp = readl(&dev->usb_ext->usbctl2) & 223862306a36Sopenharmony_ci ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE)); 223962306a36Sopenharmony_ci writel(tmp, &dev->usb_ext->usbctl2); 224062306a36Sopenharmony_ci } 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci /* Hardware Defect and Workaround */ 224362306a36Sopenharmony_ci val = readl(&dev->llregs->ll_lfps_5); 224462306a36Sopenharmony_ci val &= ~(0xf << TIMER_LFPS_6US); 224562306a36Sopenharmony_ci val |= 0x5 << TIMER_LFPS_6US; 224662306a36Sopenharmony_ci writel(val, &dev->llregs->ll_lfps_5); 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci val = readl(&dev->llregs->ll_lfps_6); 224962306a36Sopenharmony_ci val &= ~(0xffff << TIMER_LFPS_80US); 225062306a36Sopenharmony_ci val |= 0x0100 << TIMER_LFPS_80US; 225162306a36Sopenharmony_ci writel(val, &dev->llregs->ll_lfps_6); 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci /* 225462306a36Sopenharmony_ci * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB 225562306a36Sopenharmony_ci * Hot Reset Exit Handshake may Fail in Specific Case using 225662306a36Sopenharmony_ci * Default Register Settings. Workaround for Enumeration test. 225762306a36Sopenharmony_ci */ 225862306a36Sopenharmony_ci val = readl(&dev->llregs->ll_tsn_counters_2); 225962306a36Sopenharmony_ci val &= ~(0x1f << HOT_TX_NORESET_TS2); 226062306a36Sopenharmony_ci val |= 0x10 << HOT_TX_NORESET_TS2; 226162306a36Sopenharmony_ci writel(val, &dev->llregs->ll_tsn_counters_2); 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci val = readl(&dev->llregs->ll_tsn_counters_3); 226462306a36Sopenharmony_ci val &= ~(0x1f << HOT_RX_RESET_TS2); 226562306a36Sopenharmony_ci val |= 0x3 << HOT_RX_RESET_TS2; 226662306a36Sopenharmony_ci writel(val, &dev->llregs->ll_tsn_counters_3); 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci /* 226962306a36Sopenharmony_ci * AB errata. Errata 11. Workaround for Default Duration of LFPS 227062306a36Sopenharmony_ci * Handshake Signaling for Device-Initiated U1 Exit is too short. 227162306a36Sopenharmony_ci * Without this, various enumeration failures observed with 227262306a36Sopenharmony_ci * modern superspeed hosts. 227362306a36Sopenharmony_ci */ 227462306a36Sopenharmony_ci val = readl(&dev->llregs->ll_lfps_timers_2); 227562306a36Sopenharmony_ci writel((val & 0xffff0000) | LFPS_TIMERS_2_WORKAROUND_VALUE, 227662306a36Sopenharmony_ci &dev->llregs->ll_lfps_timers_2); 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci /* 227962306a36Sopenharmony_ci * Set Recovery Idle to Recover bit: 228062306a36Sopenharmony_ci * - On SS connections, setting Recovery Idle to Recover Fmw improves 228162306a36Sopenharmony_ci * link robustness with various hosts and hubs. 228262306a36Sopenharmony_ci * - It is safe to set for all connection speeds; all chip revisions. 228362306a36Sopenharmony_ci * - R-M-W to leave other bits undisturbed. 228462306a36Sopenharmony_ci * - Reference PLX TT-7372 228562306a36Sopenharmony_ci */ 228662306a36Sopenharmony_ci val = readl(&dev->llregs->ll_tsn_chicken_bit); 228762306a36Sopenharmony_ci val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW); 228862306a36Sopenharmony_ci writel(val, &dev->llregs->ll_tsn_chicken_bit); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci /* disable dedicated endpoints */ 229362306a36Sopenharmony_ci writel(0x0D, &dev->dep[0].dep_cfg); 229462306a36Sopenharmony_ci writel(0x0D, &dev->dep[1].dep_cfg); 229562306a36Sopenharmony_ci writel(0x0E, &dev->dep[2].dep_cfg); 229662306a36Sopenharmony_ci writel(0x0E, &dev->dep[3].dep_cfg); 229762306a36Sopenharmony_ci writel(0x0F, &dev->dep[4].dep_cfg); 229862306a36Sopenharmony_ci writel(0x0C, &dev->dep[5].dep_cfg); 229962306a36Sopenharmony_ci} 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cistatic void usb_reinit(struct net2280 *dev) 230262306a36Sopenharmony_ci{ 230362306a36Sopenharmony_ci if (dev->quirks & PLX_LEGACY) 230462306a36Sopenharmony_ci return usb_reinit_228x(dev); 230562306a36Sopenharmony_ci return usb_reinit_338x(dev); 230662306a36Sopenharmony_ci} 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_cistatic void ep0_start_228x(struct net2280 *dev) 230962306a36Sopenharmony_ci{ 231062306a36Sopenharmony_ci writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) | 231162306a36Sopenharmony_ci BIT(CLEAR_NAK_OUT_PACKETS) | 231262306a36Sopenharmony_ci BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), 231362306a36Sopenharmony_ci &dev->epregs[0].ep_rsp); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci /* 231662306a36Sopenharmony_ci * hardware optionally handles a bunch of standard requests 231762306a36Sopenharmony_ci * that the API hides from drivers anyway. have it do so. 231862306a36Sopenharmony_ci * endpoint status/features are handled in software, to 231962306a36Sopenharmony_ci * help pass tests for some dubious behavior. 232062306a36Sopenharmony_ci */ 232162306a36Sopenharmony_ci writel(BIT(SET_TEST_MODE) | 232262306a36Sopenharmony_ci BIT(SET_ADDRESS) | 232362306a36Sopenharmony_ci BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) | 232462306a36Sopenharmony_ci BIT(GET_DEVICE_STATUS) | 232562306a36Sopenharmony_ci BIT(GET_INTERFACE_STATUS), 232662306a36Sopenharmony_ci &dev->usb->stdrsp); 232762306a36Sopenharmony_ci writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | 232862306a36Sopenharmony_ci BIT(SELF_POWERED_USB_DEVICE) | 232962306a36Sopenharmony_ci BIT(REMOTE_WAKEUP_SUPPORT) | 233062306a36Sopenharmony_ci (dev->softconnect << USB_DETECT_ENABLE) | 233162306a36Sopenharmony_ci BIT(SELF_POWERED_STATUS), 233262306a36Sopenharmony_ci &dev->usb->usbctl); 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci /* enable irqs so we can see ep0 and general operation */ 233562306a36Sopenharmony_ci writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | 233662306a36Sopenharmony_ci BIT(ENDPOINT_0_INTERRUPT_ENABLE), 233762306a36Sopenharmony_ci &dev->regs->pciirqenb0); 233862306a36Sopenharmony_ci writel(BIT(PCI_INTERRUPT_ENABLE) | 233962306a36Sopenharmony_ci BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) | 234062306a36Sopenharmony_ci BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) | 234162306a36Sopenharmony_ci BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) | 234262306a36Sopenharmony_ci BIT(VBUS_INTERRUPT_ENABLE) | 234362306a36Sopenharmony_ci BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | 234462306a36Sopenharmony_ci BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE), 234562306a36Sopenharmony_ci &dev->regs->pciirqenb1); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci /* don't leave any writes posted */ 234862306a36Sopenharmony_ci (void) readl(&dev->usb->usbctl); 234962306a36Sopenharmony_ci} 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_cistatic void ep0_start_338x(struct net2280 *dev) 235262306a36Sopenharmony_ci{ 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci if (dev->bug7734_patched) 235562306a36Sopenharmony_ci writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) | 235662306a36Sopenharmony_ci BIT(SET_EP_HIDE_STATUS_PHASE), 235762306a36Sopenharmony_ci &dev->epregs[0].ep_rsp); 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci /* 236062306a36Sopenharmony_ci * hardware optionally handles a bunch of standard requests 236162306a36Sopenharmony_ci * that the API hides from drivers anyway. have it do so. 236262306a36Sopenharmony_ci * endpoint status/features are handled in software, to 236362306a36Sopenharmony_ci * help pass tests for some dubious behavior. 236462306a36Sopenharmony_ci */ 236562306a36Sopenharmony_ci writel(BIT(SET_ISOCHRONOUS_DELAY) | 236662306a36Sopenharmony_ci BIT(SET_SEL) | 236762306a36Sopenharmony_ci BIT(SET_TEST_MODE) | 236862306a36Sopenharmony_ci BIT(SET_ADDRESS) | 236962306a36Sopenharmony_ci BIT(GET_INTERFACE_STATUS) | 237062306a36Sopenharmony_ci BIT(GET_DEVICE_STATUS), 237162306a36Sopenharmony_ci &dev->usb->stdrsp); 237262306a36Sopenharmony_ci dev->wakeup_enable = 1; 237362306a36Sopenharmony_ci writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) | 237462306a36Sopenharmony_ci (dev->softconnect << USB_DETECT_ENABLE) | 237562306a36Sopenharmony_ci BIT(DEVICE_REMOTE_WAKEUP_ENABLE), 237662306a36Sopenharmony_ci &dev->usb->usbctl); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* enable irqs so we can see ep0 and general operation */ 237962306a36Sopenharmony_ci writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) | 238062306a36Sopenharmony_ci BIT(ENDPOINT_0_INTERRUPT_ENABLE), 238162306a36Sopenharmony_ci &dev->regs->pciirqenb0); 238262306a36Sopenharmony_ci writel(BIT(PCI_INTERRUPT_ENABLE) | 238362306a36Sopenharmony_ci BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) | 238462306a36Sopenharmony_ci BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) | 238562306a36Sopenharmony_ci BIT(VBUS_INTERRUPT_ENABLE), 238662306a36Sopenharmony_ci &dev->regs->pciirqenb1); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci /* don't leave any writes posted */ 238962306a36Sopenharmony_ci (void)readl(&dev->usb->usbctl); 239062306a36Sopenharmony_ci} 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_cistatic void ep0_start(struct net2280 *dev) 239362306a36Sopenharmony_ci{ 239462306a36Sopenharmony_ci if (dev->quirks & PLX_LEGACY) 239562306a36Sopenharmony_ci return ep0_start_228x(dev); 239662306a36Sopenharmony_ci return ep0_start_338x(dev); 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci/* when a driver is successfully registered, it will receive 240062306a36Sopenharmony_ci * control requests including set_configuration(), which enables 240162306a36Sopenharmony_ci * non-control requests. then usb traffic follows until a 240262306a36Sopenharmony_ci * disconnect is reported. then a host may connect again, or 240362306a36Sopenharmony_ci * the driver might get unbound. 240462306a36Sopenharmony_ci */ 240562306a36Sopenharmony_cistatic int net2280_start(struct usb_gadget *_gadget, 240662306a36Sopenharmony_ci struct usb_gadget_driver *driver) 240762306a36Sopenharmony_ci{ 240862306a36Sopenharmony_ci struct net2280 *dev; 240962306a36Sopenharmony_ci int retval; 241062306a36Sopenharmony_ci unsigned i; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci /* insist on high speed support from the driver, since 241362306a36Sopenharmony_ci * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) 241462306a36Sopenharmony_ci * "must not be used in normal operation" 241562306a36Sopenharmony_ci */ 241662306a36Sopenharmony_ci if (!driver || driver->max_speed < USB_SPEED_HIGH || 241762306a36Sopenharmony_ci !driver->setup) 241862306a36Sopenharmony_ci return -EINVAL; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci dev = container_of(_gadget, struct net2280, gadget); 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci for (i = 0; i < dev->n_ep; i++) 242362306a36Sopenharmony_ci dev->ep[i].irqs = 0; 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci /* hook up the driver ... */ 242662306a36Sopenharmony_ci dev->driver = driver; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci retval = device_create_file(&dev->pdev->dev, &dev_attr_function); 242962306a36Sopenharmony_ci if (retval) 243062306a36Sopenharmony_ci goto err_unbind; 243162306a36Sopenharmony_ci retval = device_create_file(&dev->pdev->dev, &dev_attr_queues); 243262306a36Sopenharmony_ci if (retval) 243362306a36Sopenharmony_ci goto err_func; 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci /* enable host detection and ep0; and we're ready 243662306a36Sopenharmony_ci * for set_configuration as well as eventual disconnect. 243762306a36Sopenharmony_ci */ 243862306a36Sopenharmony_ci net2280_led_active(dev, 1); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched) 244162306a36Sopenharmony_ci defect7374_enable_data_eps_zero(dev); 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci ep0_start(dev); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci /* pci writes may still be posted */ 244662306a36Sopenharmony_ci return 0; 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_cierr_func: 244962306a36Sopenharmony_ci device_remove_file(&dev->pdev->dev, &dev_attr_function); 245062306a36Sopenharmony_cierr_unbind: 245162306a36Sopenharmony_ci dev->driver = NULL; 245262306a36Sopenharmony_ci return retval; 245362306a36Sopenharmony_ci} 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_cistatic void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver) 245662306a36Sopenharmony_ci{ 245762306a36Sopenharmony_ci int i; 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci /* don't disconnect if it's not connected */ 246062306a36Sopenharmony_ci if (dev->gadget.speed == USB_SPEED_UNKNOWN) 246162306a36Sopenharmony_ci driver = NULL; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci /* stop hardware; prevent new request submissions; 246462306a36Sopenharmony_ci * and kill any outstanding requests. 246562306a36Sopenharmony_ci */ 246662306a36Sopenharmony_ci usb_reset(dev); 246762306a36Sopenharmony_ci for (i = 0; i < dev->n_ep; i++) 246862306a36Sopenharmony_ci nuke(&dev->ep[i]); 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci /* report disconnect; the driver is already quiesced */ 247162306a36Sopenharmony_ci if (dev->async_callbacks && driver) { 247262306a36Sopenharmony_ci spin_unlock(&dev->lock); 247362306a36Sopenharmony_ci driver->disconnect(&dev->gadget); 247462306a36Sopenharmony_ci spin_lock(&dev->lock); 247562306a36Sopenharmony_ci } 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci usb_reinit(dev); 247862306a36Sopenharmony_ci} 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_cistatic int net2280_stop(struct usb_gadget *_gadget) 248162306a36Sopenharmony_ci{ 248262306a36Sopenharmony_ci struct net2280 *dev; 248362306a36Sopenharmony_ci unsigned long flags; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci dev = container_of(_gadget, struct net2280, gadget); 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci spin_lock_irqsave(&dev->lock, flags); 248862306a36Sopenharmony_ci stop_activity(dev, NULL); 248962306a36Sopenharmony_ci spin_unlock_irqrestore(&dev->lock, flags); 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci net2280_led_active(dev, 0); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci device_remove_file(&dev->pdev->dev, &dev_attr_function); 249462306a36Sopenharmony_ci device_remove_file(&dev->pdev->dev, &dev_attr_queues); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci dev->driver = NULL; 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci return 0; 249962306a36Sopenharmony_ci} 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_cistatic void net2280_async_callbacks(struct usb_gadget *_gadget, bool enable) 250262306a36Sopenharmony_ci{ 250362306a36Sopenharmony_ci struct net2280 *dev = container_of(_gadget, struct net2280, gadget); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci spin_lock_irq(&dev->lock); 250662306a36Sopenharmony_ci dev->async_callbacks = enable; 250762306a36Sopenharmony_ci spin_unlock_irq(&dev->lock); 250862306a36Sopenharmony_ci} 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. 251362306a36Sopenharmony_ci * also works for dma-capable endpoints, in pio mode or just 251462306a36Sopenharmony_ci * to manually advance the queue after short OUT transfers. 251562306a36Sopenharmony_ci */ 251662306a36Sopenharmony_cistatic void handle_ep_small(struct net2280_ep *ep) 251762306a36Sopenharmony_ci{ 251862306a36Sopenharmony_ci struct net2280_request *req; 251962306a36Sopenharmony_ci u32 t; 252062306a36Sopenharmony_ci /* 0 error, 1 mid-data, 2 done */ 252162306a36Sopenharmony_ci int mode = 1; 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci if (!list_empty(&ep->queue)) 252462306a36Sopenharmony_ci req = list_entry(ep->queue.next, 252562306a36Sopenharmony_ci struct net2280_request, queue); 252662306a36Sopenharmony_ci else 252762306a36Sopenharmony_ci req = NULL; 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci /* ack all, and handle what we care about */ 253062306a36Sopenharmony_ci t = readl(&ep->regs->ep_stat); 253162306a36Sopenharmony_ci ep->irqs++; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n", 253462306a36Sopenharmony_ci ep->ep.name, t, req ? &req->req : NULL); 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci if (!ep->is_in || (ep->dev->quirks & PLX_2280)) 253762306a36Sopenharmony_ci writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat); 253862306a36Sopenharmony_ci else 253962306a36Sopenharmony_ci /* Added for 2282 */ 254062306a36Sopenharmony_ci writel(t, &ep->regs->ep_stat); 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci /* for ep0, monitor token irqs to catch data stage length errors 254362306a36Sopenharmony_ci * and to synchronize on status. 254462306a36Sopenharmony_ci * 254562306a36Sopenharmony_ci * also, to defer reporting of protocol stalls ... here's where 254662306a36Sopenharmony_ci * data or status first appears, handling stalls here should never 254762306a36Sopenharmony_ci * cause trouble on the host side.. 254862306a36Sopenharmony_ci * 254962306a36Sopenharmony_ci * control requests could be slightly faster without token synch for 255062306a36Sopenharmony_ci * status, but status can jam up that way. 255162306a36Sopenharmony_ci */ 255262306a36Sopenharmony_ci if (unlikely(ep->num == 0)) { 255362306a36Sopenharmony_ci if (ep->is_in) { 255462306a36Sopenharmony_ci /* status; stop NAKing */ 255562306a36Sopenharmony_ci if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) { 255662306a36Sopenharmony_ci if (ep->dev->protocol_stall) { 255762306a36Sopenharmony_ci ep->stopped = 1; 255862306a36Sopenharmony_ci set_halt(ep); 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci if (!req) 256162306a36Sopenharmony_ci allow_status(ep); 256262306a36Sopenharmony_ci mode = 2; 256362306a36Sopenharmony_ci /* reply to extra IN data tokens with a zlp */ 256462306a36Sopenharmony_ci } else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { 256562306a36Sopenharmony_ci if (ep->dev->protocol_stall) { 256662306a36Sopenharmony_ci ep->stopped = 1; 256762306a36Sopenharmony_ci set_halt(ep); 256862306a36Sopenharmony_ci mode = 2; 256962306a36Sopenharmony_ci } else if (ep->responded && 257062306a36Sopenharmony_ci !req && !ep->stopped) 257162306a36Sopenharmony_ci write_fifo(ep, NULL); 257262306a36Sopenharmony_ci } 257362306a36Sopenharmony_ci } else { 257462306a36Sopenharmony_ci /* status; stop NAKing */ 257562306a36Sopenharmony_ci if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) { 257662306a36Sopenharmony_ci if (ep->dev->protocol_stall) { 257762306a36Sopenharmony_ci ep->stopped = 1; 257862306a36Sopenharmony_ci set_halt(ep); 257962306a36Sopenharmony_ci } 258062306a36Sopenharmony_ci mode = 2; 258162306a36Sopenharmony_ci /* an extra OUT token is an error */ 258262306a36Sopenharmony_ci } else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) && 258362306a36Sopenharmony_ci req && 258462306a36Sopenharmony_ci req->req.actual == req->req.length) || 258562306a36Sopenharmony_ci (ep->responded && !req)) { 258662306a36Sopenharmony_ci ep->dev->protocol_stall = 1; 258762306a36Sopenharmony_ci set_halt(ep); 258862306a36Sopenharmony_ci ep->stopped = 1; 258962306a36Sopenharmony_ci if (req) 259062306a36Sopenharmony_ci done(ep, req, -EOVERFLOW); 259162306a36Sopenharmony_ci req = NULL; 259262306a36Sopenharmony_ci } 259362306a36Sopenharmony_ci } 259462306a36Sopenharmony_ci } 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci if (unlikely(!req)) 259762306a36Sopenharmony_ci return; 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci /* manual DMA queue advance after short OUT */ 260062306a36Sopenharmony_ci if (likely(ep->dma)) { 260162306a36Sopenharmony_ci if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) { 260262306a36Sopenharmony_ci struct net2280_request *stuck_req = NULL; 260362306a36Sopenharmony_ci int stopped = ep->stopped; 260462306a36Sopenharmony_ci int num_completed; 260562306a36Sopenharmony_ci int stuck = 0; 260662306a36Sopenharmony_ci u32 count; 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci /* TRANSFERRED works around OUT_DONE erratum 0112. 260962306a36Sopenharmony_ci * we expect (N <= maxpacket) bytes; host wrote M. 261062306a36Sopenharmony_ci * iff (M < N) we won't ever see a DMA interrupt. 261162306a36Sopenharmony_ci */ 261262306a36Sopenharmony_ci ep->stopped = 1; 261362306a36Sopenharmony_ci for (count = 0; ; t = readl(&ep->regs->ep_stat)) { 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci /* any preceding dma transfers must finish. 261662306a36Sopenharmony_ci * dma handles (M >= N), may empty the queue 261762306a36Sopenharmony_ci */ 261862306a36Sopenharmony_ci num_completed = scan_dma_completions(ep); 261962306a36Sopenharmony_ci if (unlikely(list_empty(&ep->queue) || 262062306a36Sopenharmony_ci ep->out_overflow)) { 262162306a36Sopenharmony_ci req = NULL; 262262306a36Sopenharmony_ci break; 262362306a36Sopenharmony_ci } 262462306a36Sopenharmony_ci req = list_entry(ep->queue.next, 262562306a36Sopenharmony_ci struct net2280_request, queue); 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci /* here either (M < N), a "real" short rx; 262862306a36Sopenharmony_ci * or (M == N) and the queue didn't empty 262962306a36Sopenharmony_ci */ 263062306a36Sopenharmony_ci if (likely(t & BIT(FIFO_EMPTY))) { 263162306a36Sopenharmony_ci count = readl(&ep->dma->dmacount); 263262306a36Sopenharmony_ci count &= DMA_BYTE_COUNT_MASK; 263362306a36Sopenharmony_ci if (readl(&ep->dma->dmadesc) 263462306a36Sopenharmony_ci != req->td_dma) 263562306a36Sopenharmony_ci req = NULL; 263662306a36Sopenharmony_ci break; 263762306a36Sopenharmony_ci } 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci /* Escape loop if no dma transfers completed 264062306a36Sopenharmony_ci * after few retries. 264162306a36Sopenharmony_ci */ 264262306a36Sopenharmony_ci if (num_completed == 0) { 264362306a36Sopenharmony_ci if (stuck_req == req && 264462306a36Sopenharmony_ci readl(&ep->dma->dmadesc) != 264562306a36Sopenharmony_ci req->td_dma && stuck++ > 5) { 264662306a36Sopenharmony_ci count = readl( 264762306a36Sopenharmony_ci &ep->dma->dmacount); 264862306a36Sopenharmony_ci count &= DMA_BYTE_COUNT_MASK; 264962306a36Sopenharmony_ci req = NULL; 265062306a36Sopenharmony_ci ep_dbg(ep->dev, "%s escape stuck %d, count %u\n", 265162306a36Sopenharmony_ci ep->ep.name, stuck, 265262306a36Sopenharmony_ci count); 265362306a36Sopenharmony_ci break; 265462306a36Sopenharmony_ci } else if (stuck_req != req) { 265562306a36Sopenharmony_ci stuck_req = req; 265662306a36Sopenharmony_ci stuck = 0; 265762306a36Sopenharmony_ci } 265862306a36Sopenharmony_ci } else { 265962306a36Sopenharmony_ci stuck_req = NULL; 266062306a36Sopenharmony_ci stuck = 0; 266162306a36Sopenharmony_ci } 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci udelay(1); 266462306a36Sopenharmony_ci } 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci /* stop DMA, leave ep NAKing */ 266762306a36Sopenharmony_ci writel(BIT(DMA_ABORT), &ep->dma->dmastat); 266862306a36Sopenharmony_ci spin_stop_dma(ep->dma); 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci if (likely(req)) { 267162306a36Sopenharmony_ci req->td->dmacount = 0; 267262306a36Sopenharmony_ci t = readl(&ep->regs->ep_avail); 267362306a36Sopenharmony_ci dma_done(ep, req, count, 267462306a36Sopenharmony_ci (ep->out_overflow || t) 267562306a36Sopenharmony_ci ? -EOVERFLOW : 0); 267662306a36Sopenharmony_ci } 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci /* also flush to prevent erratum 0106 trouble */ 267962306a36Sopenharmony_ci if (unlikely(ep->out_overflow || 268062306a36Sopenharmony_ci (ep->dev->chiprev == 0x0100 && 268162306a36Sopenharmony_ci ep->dev->gadget.speed 268262306a36Sopenharmony_ci == USB_SPEED_FULL))) { 268362306a36Sopenharmony_ci out_flush(ep); 268462306a36Sopenharmony_ci ep->out_overflow = 0; 268562306a36Sopenharmony_ci } 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci /* (re)start dma if needed, stop NAKing */ 268862306a36Sopenharmony_ci ep->stopped = stopped; 268962306a36Sopenharmony_ci if (!list_empty(&ep->queue)) 269062306a36Sopenharmony_ci restart_dma(ep); 269162306a36Sopenharmony_ci } else 269262306a36Sopenharmony_ci ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n", 269362306a36Sopenharmony_ci ep->ep.name, t); 269462306a36Sopenharmony_ci return; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci /* data packet(s) received (in the fifo, OUT) */ 269762306a36Sopenharmony_ci } else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) { 269862306a36Sopenharmony_ci if (read_fifo(ep, req) && ep->num != 0) 269962306a36Sopenharmony_ci mode = 2; 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ci /* data packet(s) transmitted (IN) */ 270262306a36Sopenharmony_ci } else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) { 270362306a36Sopenharmony_ci unsigned len; 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci len = req->req.length - req->req.actual; 270662306a36Sopenharmony_ci if (len > ep->ep.maxpacket) 270762306a36Sopenharmony_ci len = ep->ep.maxpacket; 270862306a36Sopenharmony_ci req->req.actual += len; 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci /* if we wrote it all, we're usually done */ 271162306a36Sopenharmony_ci /* send zlps until the status stage */ 271262306a36Sopenharmony_ci if ((req->req.actual == req->req.length) && 271362306a36Sopenharmony_ci (!req->req.zero || len != ep->ep.maxpacket) && ep->num) 271462306a36Sopenharmony_ci mode = 2; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci /* there was nothing to do ... */ 271762306a36Sopenharmony_ci } else if (mode == 1) 271862306a36Sopenharmony_ci return; 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci /* done */ 272162306a36Sopenharmony_ci if (mode == 2) { 272262306a36Sopenharmony_ci /* stream endpoints often resubmit/unlink in completion */ 272362306a36Sopenharmony_ci done(ep, req, 0); 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_ci /* maybe advance queue to next request */ 272662306a36Sopenharmony_ci if (ep->num == 0) { 272762306a36Sopenharmony_ci /* NOTE: net2280 could let gadget driver start the 272862306a36Sopenharmony_ci * status stage later. since not all controllers let 272962306a36Sopenharmony_ci * them control that, the api doesn't (yet) allow it. 273062306a36Sopenharmony_ci */ 273162306a36Sopenharmony_ci if (!ep->stopped) 273262306a36Sopenharmony_ci allow_status(ep); 273362306a36Sopenharmony_ci req = NULL; 273462306a36Sopenharmony_ci } else { 273562306a36Sopenharmony_ci if (!list_empty(&ep->queue) && !ep->stopped) 273662306a36Sopenharmony_ci req = list_entry(ep->queue.next, 273762306a36Sopenharmony_ci struct net2280_request, queue); 273862306a36Sopenharmony_ci else 273962306a36Sopenharmony_ci req = NULL; 274062306a36Sopenharmony_ci if (req && !ep->is_in) 274162306a36Sopenharmony_ci stop_out_naking(ep); 274262306a36Sopenharmony_ci } 274362306a36Sopenharmony_ci } 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci /* is there a buffer for the next packet? 274662306a36Sopenharmony_ci * for best streaming performance, make sure there is one. 274762306a36Sopenharmony_ci */ 274862306a36Sopenharmony_ci if (req && !ep->stopped) { 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci /* load IN fifo with next packet (may be zlp) */ 275162306a36Sopenharmony_ci if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) 275262306a36Sopenharmony_ci write_fifo(ep, &req->req); 275362306a36Sopenharmony_ci } 275462306a36Sopenharmony_ci} 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_cistatic struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex) 275762306a36Sopenharmony_ci{ 275862306a36Sopenharmony_ci struct net2280_ep *ep; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) 276162306a36Sopenharmony_ci return &dev->ep[0]; 276262306a36Sopenharmony_ci list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) { 276362306a36Sopenharmony_ci u8 bEndpointAddress; 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci if (!ep->desc) 276662306a36Sopenharmony_ci continue; 276762306a36Sopenharmony_ci bEndpointAddress = ep->desc->bEndpointAddress; 276862306a36Sopenharmony_ci if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) 276962306a36Sopenharmony_ci continue; 277062306a36Sopenharmony_ci if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) 277162306a36Sopenharmony_ci return ep; 277262306a36Sopenharmony_ci } 277362306a36Sopenharmony_ci return NULL; 277462306a36Sopenharmony_ci} 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_cistatic void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r) 277762306a36Sopenharmony_ci{ 277862306a36Sopenharmony_ci u32 scratch, fsmvalue; 277962306a36Sopenharmony_ci u32 ack_wait_timeout, state; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */ 278262306a36Sopenharmony_ci scratch = get_idx_reg(dev->regs, SCRATCH); 278362306a36Sopenharmony_ci fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD); 278462306a36Sopenharmony_ci scratch &= ~(0xf << DEFECT7374_FSM_FIELD); 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) && 278762306a36Sopenharmony_ci (r.bRequestType & USB_DIR_IN))) 278862306a36Sopenharmony_ci return; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci /* This is the first Control Read for this connection: */ 279162306a36Sopenharmony_ci if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) { 279262306a36Sopenharmony_ci /* 279362306a36Sopenharmony_ci * Connection is NOT SS: 279462306a36Sopenharmony_ci * - Connection must be FS or HS. 279562306a36Sopenharmony_ci * - This FSM state should allow workaround software to 279662306a36Sopenharmony_ci * run after the next USB connection. 279762306a36Sopenharmony_ci */ 279862306a36Sopenharmony_ci scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ; 279962306a36Sopenharmony_ci dev->bug7734_patched = 1; 280062306a36Sopenharmony_ci goto restore_data_eps; 280162306a36Sopenharmony_ci } 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci /* Connection is SS: */ 280462306a36Sopenharmony_ci for (ack_wait_timeout = 0; 280562306a36Sopenharmony_ci ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS; 280662306a36Sopenharmony_ci ack_wait_timeout++) { 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci state = readl(&dev->plregs->pl_ep_status_1) 280962306a36Sopenharmony_ci & (0xff << STATE); 281062306a36Sopenharmony_ci if ((state >= (ACK_GOOD_NORMAL << STATE)) && 281162306a36Sopenharmony_ci (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) { 281262306a36Sopenharmony_ci scratch |= DEFECT7374_FSM_SS_CONTROL_READ; 281362306a36Sopenharmony_ci dev->bug7734_patched = 1; 281462306a36Sopenharmony_ci break; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci /* 281862306a36Sopenharmony_ci * We have not yet received host's Data Phase ACK 281962306a36Sopenharmony_ci * - Wait and try again. 282062306a36Sopenharmony_ci */ 282162306a36Sopenharmony_ci udelay(DEFECT_7374_PROCESSOR_WAIT_TIME); 282262306a36Sopenharmony_ci } 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) { 282662306a36Sopenharmony_ci ep_err(dev, "FAIL: Defect 7374 workaround waited but failed " 282762306a36Sopenharmony_ci "to detect SS host's data phase ACK."); 282862306a36Sopenharmony_ci ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16" 282962306a36Sopenharmony_ci "got 0x%2.2x.\n", state >> STATE); 283062306a36Sopenharmony_ci } else { 283162306a36Sopenharmony_ci ep_warn(dev, "INFO: Defect 7374 workaround waited about\n" 283262306a36Sopenharmony_ci "%duSec for Control Read Data Phase ACK\n", 283362306a36Sopenharmony_ci DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout); 283462306a36Sopenharmony_ci } 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_cirestore_data_eps: 283762306a36Sopenharmony_ci /* 283862306a36Sopenharmony_ci * Restore data EPs to their pre-workaround settings (disabled, 283962306a36Sopenharmony_ci * initialized, and other details). 284062306a36Sopenharmony_ci */ 284162306a36Sopenharmony_ci defect7374_disable_data_eps(dev); 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_ci set_idx_reg(dev->regs, SCRATCH, scratch); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci return; 284662306a36Sopenharmony_ci} 284762306a36Sopenharmony_ci 284862306a36Sopenharmony_cistatic void ep_clear_seqnum(struct net2280_ep *ep) 284962306a36Sopenharmony_ci{ 285062306a36Sopenharmony_ci struct net2280 *dev = ep->dev; 285162306a36Sopenharmony_ci u32 val; 285262306a36Sopenharmony_ci static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 }; 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci val = readl(&dev->plregs->pl_ep_ctrl) & ~0x1f; 285562306a36Sopenharmony_ci val |= ep_pl[ep->num]; 285662306a36Sopenharmony_ci writel(val, &dev->plregs->pl_ep_ctrl); 285762306a36Sopenharmony_ci val |= BIT(SEQUENCE_NUMBER_RESET); 285862306a36Sopenharmony_ci writel(val, &dev->plregs->pl_ep_ctrl); 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci return; 286162306a36Sopenharmony_ci} 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_cistatic void handle_stat0_irqs_superspeed(struct net2280 *dev, 286462306a36Sopenharmony_ci struct net2280_ep *ep, struct usb_ctrlrequest r) 286562306a36Sopenharmony_ci{ 286662306a36Sopenharmony_ci struct net2280_ep *e; 286762306a36Sopenharmony_ci u16 status; 286862306a36Sopenharmony_ci int tmp = 0; 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci#define w_value le16_to_cpu(r.wValue) 287162306a36Sopenharmony_ci#define w_index le16_to_cpu(r.wIndex) 287262306a36Sopenharmony_ci#define w_length le16_to_cpu(r.wLength) 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_ci switch (r.bRequest) { 287562306a36Sopenharmony_ci case USB_REQ_SET_CONFIGURATION: 287662306a36Sopenharmony_ci dev->addressed_state = !w_value; 287762306a36Sopenharmony_ci goto usb3_delegate; 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci case USB_REQ_GET_STATUS: 288062306a36Sopenharmony_ci switch (r.bRequestType) { 288162306a36Sopenharmony_ci case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE): 288262306a36Sopenharmony_ci status = dev->wakeup_enable ? 0x02 : 0x00; 288362306a36Sopenharmony_ci if (dev->gadget.is_selfpowered) 288462306a36Sopenharmony_ci status |= BIT(0); 288562306a36Sopenharmony_ci status |= (dev->u1_enable << 2 | dev->u2_enable << 3 | 288662306a36Sopenharmony_ci dev->ltm_enable << 4); 288762306a36Sopenharmony_ci writel(0, &dev->epregs[0].ep_irqenb); 288862306a36Sopenharmony_ci set_fifo_bytecount(ep, sizeof(status)); 288962306a36Sopenharmony_ci writel((__force u32) status, &dev->epregs[0].ep_data); 289062306a36Sopenharmony_ci allow_status_338x(ep); 289162306a36Sopenharmony_ci break; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): 289462306a36Sopenharmony_ci e = get_ep_by_addr(dev, w_index); 289562306a36Sopenharmony_ci if (!e) 289662306a36Sopenharmony_ci goto do_stall3; 289762306a36Sopenharmony_ci status = readl(&e->regs->ep_rsp) & 289862306a36Sopenharmony_ci BIT(CLEAR_ENDPOINT_HALT); 289962306a36Sopenharmony_ci writel(0, &dev->epregs[0].ep_irqenb); 290062306a36Sopenharmony_ci set_fifo_bytecount(ep, sizeof(status)); 290162306a36Sopenharmony_ci writel((__force u32) status, &dev->epregs[0].ep_data); 290262306a36Sopenharmony_ci allow_status_338x(ep); 290362306a36Sopenharmony_ci break; 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci default: 290662306a36Sopenharmony_ci goto usb3_delegate; 290762306a36Sopenharmony_ci } 290862306a36Sopenharmony_ci break; 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci case USB_REQ_CLEAR_FEATURE: 291162306a36Sopenharmony_ci switch (r.bRequestType) { 291262306a36Sopenharmony_ci case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): 291362306a36Sopenharmony_ci if (!dev->addressed_state) { 291462306a36Sopenharmony_ci switch (w_value) { 291562306a36Sopenharmony_ci case USB_DEVICE_U1_ENABLE: 291662306a36Sopenharmony_ci dev->u1_enable = 0; 291762306a36Sopenharmony_ci writel(readl(&dev->usb_ext->usbctl2) & 291862306a36Sopenharmony_ci ~BIT(U1_ENABLE), 291962306a36Sopenharmony_ci &dev->usb_ext->usbctl2); 292062306a36Sopenharmony_ci allow_status_338x(ep); 292162306a36Sopenharmony_ci goto next_endpoints3; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci case USB_DEVICE_U2_ENABLE: 292462306a36Sopenharmony_ci dev->u2_enable = 0; 292562306a36Sopenharmony_ci writel(readl(&dev->usb_ext->usbctl2) & 292662306a36Sopenharmony_ci ~BIT(U2_ENABLE), 292762306a36Sopenharmony_ci &dev->usb_ext->usbctl2); 292862306a36Sopenharmony_ci allow_status_338x(ep); 292962306a36Sopenharmony_ci goto next_endpoints3; 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci case USB_DEVICE_LTM_ENABLE: 293262306a36Sopenharmony_ci dev->ltm_enable = 0; 293362306a36Sopenharmony_ci writel(readl(&dev->usb_ext->usbctl2) & 293462306a36Sopenharmony_ci ~BIT(LTM_ENABLE), 293562306a36Sopenharmony_ci &dev->usb_ext->usbctl2); 293662306a36Sopenharmony_ci allow_status_338x(ep); 293762306a36Sopenharmony_ci goto next_endpoints3; 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci default: 294062306a36Sopenharmony_ci break; 294162306a36Sopenharmony_ci } 294262306a36Sopenharmony_ci } 294362306a36Sopenharmony_ci if (w_value == USB_DEVICE_REMOTE_WAKEUP) { 294462306a36Sopenharmony_ci dev->wakeup_enable = 0; 294562306a36Sopenharmony_ci writel(readl(&dev->usb->usbctl) & 294662306a36Sopenharmony_ci ~BIT(DEVICE_REMOTE_WAKEUP_ENABLE), 294762306a36Sopenharmony_ci &dev->usb->usbctl); 294862306a36Sopenharmony_ci allow_status_338x(ep); 294962306a36Sopenharmony_ci break; 295062306a36Sopenharmony_ci } 295162306a36Sopenharmony_ci goto usb3_delegate; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): 295462306a36Sopenharmony_ci e = get_ep_by_addr(dev, w_index); 295562306a36Sopenharmony_ci if (!e) 295662306a36Sopenharmony_ci goto do_stall3; 295762306a36Sopenharmony_ci if (w_value != USB_ENDPOINT_HALT) 295862306a36Sopenharmony_ci goto do_stall3; 295962306a36Sopenharmony_ci ep_vdbg(dev, "%s clear halt\n", e->ep.name); 296062306a36Sopenharmony_ci /* 296162306a36Sopenharmony_ci * Workaround for SS SeqNum not cleared via 296262306a36Sopenharmony_ci * Endpoint Halt (Clear) bit. select endpoint 296362306a36Sopenharmony_ci */ 296462306a36Sopenharmony_ci ep_clear_seqnum(e); 296562306a36Sopenharmony_ci clear_halt(e); 296662306a36Sopenharmony_ci if (!list_empty(&e->queue) && e->td_dma) 296762306a36Sopenharmony_ci restart_dma(e); 296862306a36Sopenharmony_ci allow_status(ep); 296962306a36Sopenharmony_ci ep->stopped = 1; 297062306a36Sopenharmony_ci break; 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci default: 297362306a36Sopenharmony_ci goto usb3_delegate; 297462306a36Sopenharmony_ci } 297562306a36Sopenharmony_ci break; 297662306a36Sopenharmony_ci case USB_REQ_SET_FEATURE: 297762306a36Sopenharmony_ci switch (r.bRequestType) { 297862306a36Sopenharmony_ci case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE): 297962306a36Sopenharmony_ci if (!dev->addressed_state) { 298062306a36Sopenharmony_ci switch (w_value) { 298162306a36Sopenharmony_ci case USB_DEVICE_U1_ENABLE: 298262306a36Sopenharmony_ci dev->u1_enable = 1; 298362306a36Sopenharmony_ci writel(readl(&dev->usb_ext->usbctl2) | 298462306a36Sopenharmony_ci BIT(U1_ENABLE), 298562306a36Sopenharmony_ci &dev->usb_ext->usbctl2); 298662306a36Sopenharmony_ci allow_status_338x(ep); 298762306a36Sopenharmony_ci goto next_endpoints3; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci case USB_DEVICE_U2_ENABLE: 299062306a36Sopenharmony_ci dev->u2_enable = 1; 299162306a36Sopenharmony_ci writel(readl(&dev->usb_ext->usbctl2) | 299262306a36Sopenharmony_ci BIT(U2_ENABLE), 299362306a36Sopenharmony_ci &dev->usb_ext->usbctl2); 299462306a36Sopenharmony_ci allow_status_338x(ep); 299562306a36Sopenharmony_ci goto next_endpoints3; 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci case USB_DEVICE_LTM_ENABLE: 299862306a36Sopenharmony_ci dev->ltm_enable = 1; 299962306a36Sopenharmony_ci writel(readl(&dev->usb_ext->usbctl2) | 300062306a36Sopenharmony_ci BIT(LTM_ENABLE), 300162306a36Sopenharmony_ci &dev->usb_ext->usbctl2); 300262306a36Sopenharmony_ci allow_status_338x(ep); 300362306a36Sopenharmony_ci goto next_endpoints3; 300462306a36Sopenharmony_ci default: 300562306a36Sopenharmony_ci break; 300662306a36Sopenharmony_ci } 300762306a36Sopenharmony_ci } 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_ci if (w_value == USB_DEVICE_REMOTE_WAKEUP) { 301062306a36Sopenharmony_ci dev->wakeup_enable = 1; 301162306a36Sopenharmony_ci writel(readl(&dev->usb->usbctl) | 301262306a36Sopenharmony_ci BIT(DEVICE_REMOTE_WAKEUP_ENABLE), 301362306a36Sopenharmony_ci &dev->usb->usbctl); 301462306a36Sopenharmony_ci allow_status_338x(ep); 301562306a36Sopenharmony_ci break; 301662306a36Sopenharmony_ci } 301762306a36Sopenharmony_ci goto usb3_delegate; 301862306a36Sopenharmony_ci 301962306a36Sopenharmony_ci case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): 302062306a36Sopenharmony_ci e = get_ep_by_addr(dev, w_index); 302162306a36Sopenharmony_ci if (!e || (w_value != USB_ENDPOINT_HALT)) 302262306a36Sopenharmony_ci goto do_stall3; 302362306a36Sopenharmony_ci ep->stopped = 1; 302462306a36Sopenharmony_ci if (ep->num == 0) 302562306a36Sopenharmony_ci ep->dev->protocol_stall = 1; 302662306a36Sopenharmony_ci else { 302762306a36Sopenharmony_ci if (ep->dma) 302862306a36Sopenharmony_ci abort_dma(ep); 302962306a36Sopenharmony_ci set_halt(ep); 303062306a36Sopenharmony_ci } 303162306a36Sopenharmony_ci allow_status_338x(ep); 303262306a36Sopenharmony_ci break; 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci default: 303562306a36Sopenharmony_ci goto usb3_delegate; 303662306a36Sopenharmony_ci } 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci break; 303962306a36Sopenharmony_ci default: 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ciusb3_delegate: 304262306a36Sopenharmony_ci ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n", 304362306a36Sopenharmony_ci r.bRequestType, r.bRequest, 304462306a36Sopenharmony_ci w_value, w_index, w_length, 304562306a36Sopenharmony_ci readl(&ep->cfg->ep_cfg)); 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci ep->responded = 0; 304862306a36Sopenharmony_ci if (dev->async_callbacks) { 304962306a36Sopenharmony_ci spin_unlock(&dev->lock); 305062306a36Sopenharmony_ci tmp = dev->driver->setup(&dev->gadget, &r); 305162306a36Sopenharmony_ci spin_lock(&dev->lock); 305262306a36Sopenharmony_ci } 305362306a36Sopenharmony_ci } 305462306a36Sopenharmony_cido_stall3: 305562306a36Sopenharmony_ci if (tmp < 0) { 305662306a36Sopenharmony_ci ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", 305762306a36Sopenharmony_ci r.bRequestType, r.bRequest, tmp); 305862306a36Sopenharmony_ci dev->protocol_stall = 1; 305962306a36Sopenharmony_ci /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */ 306062306a36Sopenharmony_ci set_halt(ep); 306162306a36Sopenharmony_ci } 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_cinext_endpoints3: 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci#undef w_value 306662306a36Sopenharmony_ci#undef w_index 306762306a36Sopenharmony_ci#undef w_length 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci return; 307062306a36Sopenharmony_ci} 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_cistatic void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0) 307362306a36Sopenharmony_ci{ 307462306a36Sopenharmony_ci u32 index; 307562306a36Sopenharmony_ci u32 bit; 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci for (index = 0; index < ARRAY_SIZE(ep_bit); index++) { 307862306a36Sopenharmony_ci bit = BIT(ep_bit[index]); 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_ci if (!stat0) 308162306a36Sopenharmony_ci break; 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci if (!(stat0 & bit)) 308462306a36Sopenharmony_ci continue; 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci stat0 &= ~bit; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci handle_ep_small(&dev->ep[index]); 308962306a36Sopenharmony_ci } 309062306a36Sopenharmony_ci} 309162306a36Sopenharmony_ci 309262306a36Sopenharmony_cistatic void handle_stat0_irqs(struct net2280 *dev, u32 stat) 309362306a36Sopenharmony_ci{ 309462306a36Sopenharmony_ci struct net2280_ep *ep; 309562306a36Sopenharmony_ci u32 num, scratch; 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci /* most of these don't need individual acks */ 309862306a36Sopenharmony_ci stat &= ~BIT(INTA_ASSERTED); 309962306a36Sopenharmony_ci if (!stat) 310062306a36Sopenharmony_ci return; 310162306a36Sopenharmony_ci /* ep_dbg(dev, "irqstat0 %04x\n", stat); */ 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci /* starting a control request? */ 310462306a36Sopenharmony_ci if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) { 310562306a36Sopenharmony_ci union { 310662306a36Sopenharmony_ci u32 raw[2]; 310762306a36Sopenharmony_ci struct usb_ctrlrequest r; 310862306a36Sopenharmony_ci } u; 310962306a36Sopenharmony_ci int tmp; 311062306a36Sopenharmony_ci struct net2280_request *req; 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci if (dev->gadget.speed == USB_SPEED_UNKNOWN) { 311362306a36Sopenharmony_ci u32 val = readl(&dev->usb->usbstat); 311462306a36Sopenharmony_ci if (val & BIT(SUPER_SPEED)) { 311562306a36Sopenharmony_ci dev->gadget.speed = USB_SPEED_SUPER; 311662306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 311762306a36Sopenharmony_ci EP0_SS_MAX_PACKET_SIZE); 311862306a36Sopenharmony_ci } else if (val & BIT(HIGH_SPEED)) { 311962306a36Sopenharmony_ci dev->gadget.speed = USB_SPEED_HIGH; 312062306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 312162306a36Sopenharmony_ci EP0_HS_MAX_PACKET_SIZE); 312262306a36Sopenharmony_ci } else { 312362306a36Sopenharmony_ci dev->gadget.speed = USB_SPEED_FULL; 312462306a36Sopenharmony_ci usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 312562306a36Sopenharmony_ci EP0_HS_MAX_PACKET_SIZE); 312662306a36Sopenharmony_ci } 312762306a36Sopenharmony_ci net2280_led_speed(dev, dev->gadget.speed); 312862306a36Sopenharmony_ci ep_dbg(dev, "%s\n", 312962306a36Sopenharmony_ci usb_speed_string(dev->gadget.speed)); 313062306a36Sopenharmony_ci } 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci ep = &dev->ep[0]; 313362306a36Sopenharmony_ci ep->irqs++; 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci /* make sure any leftover request state is cleared */ 313662306a36Sopenharmony_ci stat &= ~BIT(ENDPOINT_0_INTERRUPT); 313762306a36Sopenharmony_ci while (!list_empty(&ep->queue)) { 313862306a36Sopenharmony_ci req = list_entry(ep->queue.next, 313962306a36Sopenharmony_ci struct net2280_request, queue); 314062306a36Sopenharmony_ci done(ep, req, (req->req.actual == req->req.length) 314162306a36Sopenharmony_ci ? 0 : -EPROTO); 314262306a36Sopenharmony_ci } 314362306a36Sopenharmony_ci ep->stopped = 0; 314462306a36Sopenharmony_ci dev->protocol_stall = 0; 314562306a36Sopenharmony_ci if (!(dev->quirks & PLX_PCIE)) { 314662306a36Sopenharmony_ci if (ep->dev->quirks & PLX_2280) 314762306a36Sopenharmony_ci tmp = BIT(FIFO_OVERFLOW) | 314862306a36Sopenharmony_ci BIT(FIFO_UNDERFLOW); 314962306a36Sopenharmony_ci else 315062306a36Sopenharmony_ci tmp = 0; 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci writel(tmp | BIT(TIMEOUT) | 315362306a36Sopenharmony_ci BIT(USB_STALL_SENT) | 315462306a36Sopenharmony_ci BIT(USB_IN_NAK_SENT) | 315562306a36Sopenharmony_ci BIT(USB_IN_ACK_RCVD) | 315662306a36Sopenharmony_ci BIT(USB_OUT_PING_NAK_SENT) | 315762306a36Sopenharmony_ci BIT(USB_OUT_ACK_SENT) | 315862306a36Sopenharmony_ci BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) | 315962306a36Sopenharmony_ci BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) | 316062306a36Sopenharmony_ci BIT(DATA_PACKET_RECEIVED_INTERRUPT) | 316162306a36Sopenharmony_ci BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | 316262306a36Sopenharmony_ci BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | 316362306a36Sopenharmony_ci BIT(DATA_IN_TOKEN_INTERRUPT), 316462306a36Sopenharmony_ci &ep->regs->ep_stat); 316562306a36Sopenharmony_ci } 316662306a36Sopenharmony_ci u.raw[0] = readl(&dev->usb->setup0123); 316762306a36Sopenharmony_ci u.raw[1] = readl(&dev->usb->setup4567); 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci cpu_to_le32s(&u.raw[0]); 317062306a36Sopenharmony_ci cpu_to_le32s(&u.raw[1]); 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched) 317362306a36Sopenharmony_ci defect7374_workaround(dev, u.r); 317462306a36Sopenharmony_ci 317562306a36Sopenharmony_ci tmp = 0; 317662306a36Sopenharmony_ci 317762306a36Sopenharmony_ci#define w_value le16_to_cpu(u.r.wValue) 317862306a36Sopenharmony_ci#define w_index le16_to_cpu(u.r.wIndex) 317962306a36Sopenharmony_ci#define w_length le16_to_cpu(u.r.wLength) 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci /* ack the irq */ 318262306a36Sopenharmony_ci writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0); 318362306a36Sopenharmony_ci stat ^= BIT(SETUP_PACKET_INTERRUPT); 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci /* watch control traffic at the token level, and force 318662306a36Sopenharmony_ci * synchronization before letting the status stage happen. 318762306a36Sopenharmony_ci * FIXME ignore tokens we'll NAK, until driver responds. 318862306a36Sopenharmony_ci * that'll mean a lot less irqs for some drivers. 318962306a36Sopenharmony_ci */ 319062306a36Sopenharmony_ci ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; 319162306a36Sopenharmony_ci if (ep->is_in) { 319262306a36Sopenharmony_ci scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | 319362306a36Sopenharmony_ci BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | 319462306a36Sopenharmony_ci BIT(DATA_IN_TOKEN_INTERRUPT); 319562306a36Sopenharmony_ci stop_out_naking(ep); 319662306a36Sopenharmony_ci } else 319762306a36Sopenharmony_ci scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) | 319862306a36Sopenharmony_ci BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | 319962306a36Sopenharmony_ci BIT(DATA_IN_TOKEN_INTERRUPT); 320062306a36Sopenharmony_ci writel(scratch, &dev->epregs[0].ep_irqenb); 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci /* we made the hardware handle most lowlevel requests; 320362306a36Sopenharmony_ci * everything else goes uplevel to the gadget code. 320462306a36Sopenharmony_ci */ 320562306a36Sopenharmony_ci ep->responded = 1; 320662306a36Sopenharmony_ci 320762306a36Sopenharmony_ci if (dev->gadget.speed == USB_SPEED_SUPER) { 320862306a36Sopenharmony_ci handle_stat0_irqs_superspeed(dev, ep, u.r); 320962306a36Sopenharmony_ci goto next_endpoints; 321062306a36Sopenharmony_ci } 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci switch (u.r.bRequest) { 321362306a36Sopenharmony_ci case USB_REQ_GET_STATUS: { 321462306a36Sopenharmony_ci struct net2280_ep *e; 321562306a36Sopenharmony_ci __le32 status; 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci /* hw handles device and interface status */ 321862306a36Sopenharmony_ci if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) 321962306a36Sopenharmony_ci goto delegate; 322062306a36Sopenharmony_ci e = get_ep_by_addr(dev, w_index); 322162306a36Sopenharmony_ci if (!e || w_length > 2) 322262306a36Sopenharmony_ci goto do_stall; 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT)) 322562306a36Sopenharmony_ci status = cpu_to_le32(1); 322662306a36Sopenharmony_ci else 322762306a36Sopenharmony_ci status = cpu_to_le32(0); 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci /* don't bother with a request object! */ 323062306a36Sopenharmony_ci writel(0, &dev->epregs[0].ep_irqenb); 323162306a36Sopenharmony_ci set_fifo_bytecount(ep, w_length); 323262306a36Sopenharmony_ci writel((__force u32)status, &dev->epregs[0].ep_data); 323362306a36Sopenharmony_ci allow_status(ep); 323462306a36Sopenharmony_ci ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status); 323562306a36Sopenharmony_ci goto next_endpoints; 323662306a36Sopenharmony_ci } 323762306a36Sopenharmony_ci break; 323862306a36Sopenharmony_ci case USB_REQ_CLEAR_FEATURE: { 323962306a36Sopenharmony_ci struct net2280_ep *e; 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci /* hw handles device features */ 324262306a36Sopenharmony_ci if (u.r.bRequestType != USB_RECIP_ENDPOINT) 324362306a36Sopenharmony_ci goto delegate; 324462306a36Sopenharmony_ci if (w_value != USB_ENDPOINT_HALT || w_length != 0) 324562306a36Sopenharmony_ci goto do_stall; 324662306a36Sopenharmony_ci e = get_ep_by_addr(dev, w_index); 324762306a36Sopenharmony_ci if (!e) 324862306a36Sopenharmony_ci goto do_stall; 324962306a36Sopenharmony_ci if (e->wedged) { 325062306a36Sopenharmony_ci ep_vdbg(dev, "%s wedged, halt not cleared\n", 325162306a36Sopenharmony_ci ep->ep.name); 325262306a36Sopenharmony_ci } else { 325362306a36Sopenharmony_ci ep_vdbg(dev, "%s clear halt\n", e->ep.name); 325462306a36Sopenharmony_ci clear_halt(e); 325562306a36Sopenharmony_ci if ((ep->dev->quirks & PLX_PCIE) && 325662306a36Sopenharmony_ci !list_empty(&e->queue) && e->td_dma) 325762306a36Sopenharmony_ci restart_dma(e); 325862306a36Sopenharmony_ci } 325962306a36Sopenharmony_ci allow_status(ep); 326062306a36Sopenharmony_ci goto next_endpoints; 326162306a36Sopenharmony_ci } 326262306a36Sopenharmony_ci break; 326362306a36Sopenharmony_ci case USB_REQ_SET_FEATURE: { 326462306a36Sopenharmony_ci struct net2280_ep *e; 326562306a36Sopenharmony_ci 326662306a36Sopenharmony_ci /* hw handles device features */ 326762306a36Sopenharmony_ci if (u.r.bRequestType != USB_RECIP_ENDPOINT) 326862306a36Sopenharmony_ci goto delegate; 326962306a36Sopenharmony_ci if (w_value != USB_ENDPOINT_HALT || w_length != 0) 327062306a36Sopenharmony_ci goto do_stall; 327162306a36Sopenharmony_ci e = get_ep_by_addr(dev, w_index); 327262306a36Sopenharmony_ci if (!e) 327362306a36Sopenharmony_ci goto do_stall; 327462306a36Sopenharmony_ci if (e->ep.name == ep0name) 327562306a36Sopenharmony_ci goto do_stall; 327662306a36Sopenharmony_ci set_halt(e); 327762306a36Sopenharmony_ci if ((dev->quirks & PLX_PCIE) && e->dma) 327862306a36Sopenharmony_ci abort_dma(e); 327962306a36Sopenharmony_ci allow_status(ep); 328062306a36Sopenharmony_ci ep_vdbg(dev, "%s set halt\n", ep->ep.name); 328162306a36Sopenharmony_ci goto next_endpoints; 328262306a36Sopenharmony_ci } 328362306a36Sopenharmony_ci break; 328462306a36Sopenharmony_ci default: 328562306a36Sopenharmony_cidelegate: 328662306a36Sopenharmony_ci ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x " 328762306a36Sopenharmony_ci "ep_cfg %08x\n", 328862306a36Sopenharmony_ci u.r.bRequestType, u.r.bRequest, 328962306a36Sopenharmony_ci w_value, w_index, w_length, 329062306a36Sopenharmony_ci readl(&ep->cfg->ep_cfg)); 329162306a36Sopenharmony_ci ep->responded = 0; 329262306a36Sopenharmony_ci if (dev->async_callbacks) { 329362306a36Sopenharmony_ci spin_unlock(&dev->lock); 329462306a36Sopenharmony_ci tmp = dev->driver->setup(&dev->gadget, &u.r); 329562306a36Sopenharmony_ci spin_lock(&dev->lock); 329662306a36Sopenharmony_ci } 329762306a36Sopenharmony_ci } 329862306a36Sopenharmony_ci 329962306a36Sopenharmony_ci /* stall ep0 on error */ 330062306a36Sopenharmony_ci if (tmp < 0) { 330162306a36Sopenharmony_cido_stall: 330262306a36Sopenharmony_ci ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n", 330362306a36Sopenharmony_ci u.r.bRequestType, u.r.bRequest, tmp); 330462306a36Sopenharmony_ci dev->protocol_stall = 1; 330562306a36Sopenharmony_ci } 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_ci /* some in/out token irq should follow; maybe stall then. 330862306a36Sopenharmony_ci * driver must queue a request (even zlp) or halt ep0 330962306a36Sopenharmony_ci * before the host times out. 331062306a36Sopenharmony_ci */ 331162306a36Sopenharmony_ci } 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci#undef w_value 331462306a36Sopenharmony_ci#undef w_index 331562306a36Sopenharmony_ci#undef w_length 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_cinext_endpoints: 331862306a36Sopenharmony_ci if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) { 331962306a36Sopenharmony_ci u32 mask = (BIT(ENDPOINT_0_INTERRUPT) | 332062306a36Sopenharmony_ci USB3380_IRQSTAT0_EP_INTR_MASK_IN | 332162306a36Sopenharmony_ci USB3380_IRQSTAT0_EP_INTR_MASK_OUT); 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci if (stat & mask) { 332462306a36Sopenharmony_ci usb338x_handle_ep_intr(dev, stat & mask); 332562306a36Sopenharmony_ci stat &= ~mask; 332662306a36Sopenharmony_ci } 332762306a36Sopenharmony_ci } else { 332862306a36Sopenharmony_ci /* endpoint data irq ? */ 332962306a36Sopenharmony_ci scratch = stat & 0x7f; 333062306a36Sopenharmony_ci stat &= ~0x7f; 333162306a36Sopenharmony_ci for (num = 0; scratch; num++) { 333262306a36Sopenharmony_ci u32 t; 333362306a36Sopenharmony_ci 333462306a36Sopenharmony_ci /* do this endpoint's FIFO and queue need tending? */ 333562306a36Sopenharmony_ci t = BIT(num); 333662306a36Sopenharmony_ci if ((scratch & t) == 0) 333762306a36Sopenharmony_ci continue; 333862306a36Sopenharmony_ci scratch ^= t; 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci ep = &dev->ep[num]; 334162306a36Sopenharmony_ci handle_ep_small(ep); 334262306a36Sopenharmony_ci } 334362306a36Sopenharmony_ci } 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci if (stat) 334662306a36Sopenharmony_ci ep_dbg(dev, "unhandled irqstat0 %08x\n", stat); 334762306a36Sopenharmony_ci} 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \ 335062306a36Sopenharmony_ci BIT(DMA_C_INTERRUPT) | \ 335162306a36Sopenharmony_ci BIT(DMA_B_INTERRUPT) | \ 335262306a36Sopenharmony_ci BIT(DMA_A_INTERRUPT)) 335362306a36Sopenharmony_ci#define PCI_ERROR_INTERRUPTS ( \ 335462306a36Sopenharmony_ci BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \ 335562306a36Sopenharmony_ci BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \ 335662306a36Sopenharmony_ci BIT(PCI_RETRY_ABORT_INTERRUPT)) 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_cistatic void handle_stat1_irqs(struct net2280 *dev, u32 stat) 335962306a36Sopenharmony_ci__releases(dev->lock) 336062306a36Sopenharmony_ci__acquires(dev->lock) 336162306a36Sopenharmony_ci{ 336262306a36Sopenharmony_ci struct net2280_ep *ep; 336362306a36Sopenharmony_ci u32 tmp, num, mask, scratch; 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci /* after disconnect there's nothing else to do! */ 336662306a36Sopenharmony_ci tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT); 336762306a36Sopenharmony_ci mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED); 336862306a36Sopenharmony_ci 336962306a36Sopenharmony_ci /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set. 337062306a36Sopenharmony_ci * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and 337162306a36Sopenharmony_ci * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT 337262306a36Sopenharmony_ci * only indicates a change in the reset state). 337362306a36Sopenharmony_ci */ 337462306a36Sopenharmony_ci if (stat & tmp) { 337562306a36Sopenharmony_ci bool reset = false; 337662306a36Sopenharmony_ci bool disconnect = false; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci /* 337962306a36Sopenharmony_ci * Ignore disconnects and resets if the speed hasn't been set. 338062306a36Sopenharmony_ci * VBUS can bounce and there's always an initial reset. 338162306a36Sopenharmony_ci */ 338262306a36Sopenharmony_ci writel(tmp, &dev->regs->irqstat1); 338362306a36Sopenharmony_ci if (dev->gadget.speed != USB_SPEED_UNKNOWN) { 338462306a36Sopenharmony_ci if ((stat & BIT(VBUS_INTERRUPT)) && 338562306a36Sopenharmony_ci (readl(&dev->usb->usbctl) & 338662306a36Sopenharmony_ci BIT(VBUS_PIN)) == 0) { 338762306a36Sopenharmony_ci disconnect = true; 338862306a36Sopenharmony_ci ep_dbg(dev, "disconnect %s\n", 338962306a36Sopenharmony_ci dev->driver->driver.name); 339062306a36Sopenharmony_ci } else if ((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) && 339162306a36Sopenharmony_ci (readl(&dev->usb->usbstat) & mask) 339262306a36Sopenharmony_ci == 0) { 339362306a36Sopenharmony_ci reset = true; 339462306a36Sopenharmony_ci ep_dbg(dev, "reset %s\n", 339562306a36Sopenharmony_ci dev->driver->driver.name); 339662306a36Sopenharmony_ci } 339762306a36Sopenharmony_ci 339862306a36Sopenharmony_ci if (disconnect || reset) { 339962306a36Sopenharmony_ci stop_activity(dev, dev->driver); 340062306a36Sopenharmony_ci ep0_start(dev); 340162306a36Sopenharmony_ci if (dev->async_callbacks) { 340262306a36Sopenharmony_ci spin_unlock(&dev->lock); 340362306a36Sopenharmony_ci if (reset) 340462306a36Sopenharmony_ci usb_gadget_udc_reset(&dev->gadget, dev->driver); 340562306a36Sopenharmony_ci else 340662306a36Sopenharmony_ci (dev->driver->disconnect)(&dev->gadget); 340762306a36Sopenharmony_ci spin_lock(&dev->lock); 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci return; 341062306a36Sopenharmony_ci } 341162306a36Sopenharmony_ci } 341262306a36Sopenharmony_ci stat &= ~tmp; 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci /* vBUS can bounce ... one of many reasons to ignore the 341562306a36Sopenharmony_ci * notion of hotplug events on bus connect/disconnect! 341662306a36Sopenharmony_ci */ 341762306a36Sopenharmony_ci if (!stat) 341862306a36Sopenharmony_ci return; 341962306a36Sopenharmony_ci } 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci /* NOTE: chip stays in PCI D0 state for now, but it could 342262306a36Sopenharmony_ci * enter D1 to save more power 342362306a36Sopenharmony_ci */ 342462306a36Sopenharmony_ci tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT); 342562306a36Sopenharmony_ci if (stat & tmp) { 342662306a36Sopenharmony_ci writel(tmp, &dev->regs->irqstat1); 342762306a36Sopenharmony_ci spin_unlock(&dev->lock); 342862306a36Sopenharmony_ci if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) { 342962306a36Sopenharmony_ci if (dev->async_callbacks && dev->driver->suspend) 343062306a36Sopenharmony_ci dev->driver->suspend(&dev->gadget); 343162306a36Sopenharmony_ci if (!enable_suspend) 343262306a36Sopenharmony_ci stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT); 343362306a36Sopenharmony_ci } else { 343462306a36Sopenharmony_ci if (dev->async_callbacks && dev->driver->resume) 343562306a36Sopenharmony_ci dev->driver->resume(&dev->gadget); 343662306a36Sopenharmony_ci /* at high speed, note erratum 0133 */ 343762306a36Sopenharmony_ci } 343862306a36Sopenharmony_ci spin_lock(&dev->lock); 343962306a36Sopenharmony_ci stat &= ~tmp; 344062306a36Sopenharmony_ci } 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci /* clear any other status/irqs */ 344362306a36Sopenharmony_ci if (stat) 344462306a36Sopenharmony_ci writel(stat, &dev->regs->irqstat1); 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_ci /* some status we can just ignore */ 344762306a36Sopenharmony_ci if (dev->quirks & PLX_2280) 344862306a36Sopenharmony_ci stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | 344962306a36Sopenharmony_ci BIT(SUSPEND_REQUEST_INTERRUPT) | 345062306a36Sopenharmony_ci BIT(RESUME_INTERRUPT) | 345162306a36Sopenharmony_ci BIT(SOF_INTERRUPT)); 345262306a36Sopenharmony_ci else 345362306a36Sopenharmony_ci stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) | 345462306a36Sopenharmony_ci BIT(RESUME_INTERRUPT) | 345562306a36Sopenharmony_ci BIT(SOF_DOWN_INTERRUPT) | 345662306a36Sopenharmony_ci BIT(SOF_INTERRUPT)); 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci if (!stat) 345962306a36Sopenharmony_ci return; 346062306a36Sopenharmony_ci /* ep_dbg(dev, "irqstat1 %08x\n", stat);*/ 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci /* DMA status, for ep-{a,b,c,d} */ 346362306a36Sopenharmony_ci scratch = stat & DMA_INTERRUPTS; 346462306a36Sopenharmony_ci stat &= ~DMA_INTERRUPTS; 346562306a36Sopenharmony_ci scratch >>= 9; 346662306a36Sopenharmony_ci for (num = 0; scratch; num++) { 346762306a36Sopenharmony_ci struct net2280_dma_regs __iomem *dma; 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci tmp = BIT(num); 347062306a36Sopenharmony_ci if ((tmp & scratch) == 0) 347162306a36Sopenharmony_ci continue; 347262306a36Sopenharmony_ci scratch ^= tmp; 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci ep = &dev->ep[num + 1]; 347562306a36Sopenharmony_ci dma = ep->dma; 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci if (!dma) 347862306a36Sopenharmony_ci continue; 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_ci /* clear ep's dma status */ 348162306a36Sopenharmony_ci tmp = readl(&dma->dmastat); 348262306a36Sopenharmony_ci writel(tmp, &dma->dmastat); 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci /* dma sync*/ 348562306a36Sopenharmony_ci if (dev->quirks & PLX_PCIE) { 348662306a36Sopenharmony_ci u32 r_dmacount = readl(&dma->dmacount); 348762306a36Sopenharmony_ci if (!ep->is_in && (r_dmacount & 0x00FFFFFF) && 348862306a36Sopenharmony_ci (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) 348962306a36Sopenharmony_ci continue; 349062306a36Sopenharmony_ci } 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_ci if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) { 349362306a36Sopenharmony_ci ep_dbg(ep->dev, "%s no xact done? %08x\n", 349462306a36Sopenharmony_ci ep->ep.name, tmp); 349562306a36Sopenharmony_ci continue; 349662306a36Sopenharmony_ci } 349762306a36Sopenharmony_ci stop_dma(ep->dma); 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci /* OUT transfers terminate when the data from the 350062306a36Sopenharmony_ci * host is in our memory. Process whatever's done. 350162306a36Sopenharmony_ci * On this path, we know transfer's last packet wasn't 350262306a36Sopenharmony_ci * less than req->length. NAK_OUT_PACKETS may be set, 350362306a36Sopenharmony_ci * or the FIFO may already be holding new packets. 350462306a36Sopenharmony_ci * 350562306a36Sopenharmony_ci * IN transfers can linger in the FIFO for a very 350662306a36Sopenharmony_ci * long time ... we ignore that for now, accounting 350762306a36Sopenharmony_ci * precisely (like PIO does) needs per-packet irqs 350862306a36Sopenharmony_ci */ 350962306a36Sopenharmony_ci scan_dma_completions(ep); 351062306a36Sopenharmony_ci 351162306a36Sopenharmony_ci /* disable dma on inactive queues; else maybe restart */ 351262306a36Sopenharmony_ci if (!list_empty(&ep->queue)) { 351362306a36Sopenharmony_ci tmp = readl(&dma->dmactl); 351462306a36Sopenharmony_ci restart_dma(ep); 351562306a36Sopenharmony_ci } 351662306a36Sopenharmony_ci ep->irqs++; 351762306a36Sopenharmony_ci } 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_ci /* NOTE: there are other PCI errors we might usefully notice. 352062306a36Sopenharmony_ci * if they appear very often, here's where to try recovering. 352162306a36Sopenharmony_ci */ 352262306a36Sopenharmony_ci if (stat & PCI_ERROR_INTERRUPTS) { 352362306a36Sopenharmony_ci ep_err(dev, "pci dma error; stat %08x\n", stat); 352462306a36Sopenharmony_ci stat &= ~PCI_ERROR_INTERRUPTS; 352562306a36Sopenharmony_ci /* these are fatal errors, but "maybe" they won't 352662306a36Sopenharmony_ci * happen again ... 352762306a36Sopenharmony_ci */ 352862306a36Sopenharmony_ci stop_activity(dev, dev->driver); 352962306a36Sopenharmony_ci ep0_start(dev); 353062306a36Sopenharmony_ci stat = 0; 353162306a36Sopenharmony_ci } 353262306a36Sopenharmony_ci 353362306a36Sopenharmony_ci if (stat) 353462306a36Sopenharmony_ci ep_dbg(dev, "unhandled irqstat1 %08x\n", stat); 353562306a36Sopenharmony_ci} 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_cistatic irqreturn_t net2280_irq(int irq, void *_dev) 353862306a36Sopenharmony_ci{ 353962306a36Sopenharmony_ci struct net2280 *dev = _dev; 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_ci /* shared interrupt, not ours */ 354262306a36Sopenharmony_ci if ((dev->quirks & PLX_LEGACY) && 354362306a36Sopenharmony_ci (!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED)))) 354462306a36Sopenharmony_ci return IRQ_NONE; 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci spin_lock(&dev->lock); 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci /* handle disconnect, dma, and more */ 354962306a36Sopenharmony_ci handle_stat1_irqs(dev, readl(&dev->regs->irqstat1)); 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_ci /* control requests and PIO */ 355262306a36Sopenharmony_ci handle_stat0_irqs(dev, readl(&dev->regs->irqstat0)); 355362306a36Sopenharmony_ci 355462306a36Sopenharmony_ci if (dev->quirks & PLX_PCIE) { 355562306a36Sopenharmony_ci /* re-enable interrupt to trigger any possible new interrupt */ 355662306a36Sopenharmony_ci u32 pciirqenb1 = readl(&dev->regs->pciirqenb1); 355762306a36Sopenharmony_ci writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1); 355862306a36Sopenharmony_ci writel(pciirqenb1, &dev->regs->pciirqenb1); 355962306a36Sopenharmony_ci } 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci spin_unlock(&dev->lock); 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_ci return IRQ_HANDLED; 356462306a36Sopenharmony_ci} 356562306a36Sopenharmony_ci 356662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_cistatic void gadget_release(struct device *_dev) 356962306a36Sopenharmony_ci{ 357062306a36Sopenharmony_ci struct net2280 *dev = container_of(_dev, struct net2280, gadget.dev); 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_ci kfree(dev); 357362306a36Sopenharmony_ci} 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci/* tear down the binding between this driver and the pci device */ 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_cistatic void net2280_remove(struct pci_dev *pdev) 357862306a36Sopenharmony_ci{ 357962306a36Sopenharmony_ci struct net2280 *dev = pci_get_drvdata(pdev); 358062306a36Sopenharmony_ci 358162306a36Sopenharmony_ci if (dev->added) 358262306a36Sopenharmony_ci usb_del_gadget(&dev->gadget); 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci BUG_ON(dev->driver); 358562306a36Sopenharmony_ci 358662306a36Sopenharmony_ci /* then clean up the resources we allocated during probe() */ 358762306a36Sopenharmony_ci if (dev->requests) { 358862306a36Sopenharmony_ci int i; 358962306a36Sopenharmony_ci for (i = 1; i < 5; i++) { 359062306a36Sopenharmony_ci if (!dev->ep[i].dummy) 359162306a36Sopenharmony_ci continue; 359262306a36Sopenharmony_ci dma_pool_free(dev->requests, dev->ep[i].dummy, 359362306a36Sopenharmony_ci dev->ep[i].td_dma); 359462306a36Sopenharmony_ci } 359562306a36Sopenharmony_ci dma_pool_destroy(dev->requests); 359662306a36Sopenharmony_ci } 359762306a36Sopenharmony_ci if (dev->got_irq) 359862306a36Sopenharmony_ci free_irq(pdev->irq, dev); 359962306a36Sopenharmony_ci if (dev->quirks & PLX_PCIE) 360062306a36Sopenharmony_ci pci_disable_msi(pdev); 360162306a36Sopenharmony_ci if (dev->regs) { 360262306a36Sopenharmony_ci net2280_led_shutdown(dev); 360362306a36Sopenharmony_ci iounmap(dev->regs); 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci if (dev->region) 360662306a36Sopenharmony_ci release_mem_region(pci_resource_start(pdev, 0), 360762306a36Sopenharmony_ci pci_resource_len(pdev, 0)); 360862306a36Sopenharmony_ci if (dev->enabled) 360962306a36Sopenharmony_ci pci_disable_device(pdev); 361062306a36Sopenharmony_ci device_remove_file(&pdev->dev, &dev_attr_registers); 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci ep_info(dev, "unbind\n"); 361362306a36Sopenharmony_ci usb_put_gadget(&dev->gadget); 361462306a36Sopenharmony_ci} 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_ci/* wrap this driver around the specified device, but 361762306a36Sopenharmony_ci * don't respond over USB until a gadget driver binds to us. 361862306a36Sopenharmony_ci */ 361962306a36Sopenharmony_ci 362062306a36Sopenharmony_cistatic int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) 362162306a36Sopenharmony_ci{ 362262306a36Sopenharmony_ci struct net2280 *dev; 362362306a36Sopenharmony_ci unsigned long resource, len; 362462306a36Sopenharmony_ci void __iomem *base = NULL; 362562306a36Sopenharmony_ci int retval, i; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci /* alloc, and start init */ 362862306a36Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 362962306a36Sopenharmony_ci if (dev == NULL) { 363062306a36Sopenharmony_ci retval = -ENOMEM; 363162306a36Sopenharmony_ci goto done; 363262306a36Sopenharmony_ci } 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 363562306a36Sopenharmony_ci usb_initialize_gadget(&pdev->dev, &dev->gadget, gadget_release); 363662306a36Sopenharmony_ci spin_lock_init(&dev->lock); 363762306a36Sopenharmony_ci dev->quirks = id->driver_data; 363862306a36Sopenharmony_ci dev->pdev = pdev; 363962306a36Sopenharmony_ci dev->gadget.ops = &net2280_ops; 364062306a36Sopenharmony_ci dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ? 364162306a36Sopenharmony_ci USB_SPEED_SUPER : USB_SPEED_HIGH; 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci /* the "gadget" abstracts/virtualizes the controller */ 364462306a36Sopenharmony_ci dev->gadget.name = driver_name; 364562306a36Sopenharmony_ci 364662306a36Sopenharmony_ci /* now all the pci goodies ... */ 364762306a36Sopenharmony_ci if (pci_enable_device(pdev) < 0) { 364862306a36Sopenharmony_ci retval = -ENODEV; 364962306a36Sopenharmony_ci goto done; 365062306a36Sopenharmony_ci } 365162306a36Sopenharmony_ci dev->enabled = 1; 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci /* BAR 0 holds all the registers 365462306a36Sopenharmony_ci * BAR 1 is 8051 memory; unused here (note erratum 0103) 365562306a36Sopenharmony_ci * BAR 2 is fifo memory; unused here 365662306a36Sopenharmony_ci */ 365762306a36Sopenharmony_ci resource = pci_resource_start(pdev, 0); 365862306a36Sopenharmony_ci len = pci_resource_len(pdev, 0); 365962306a36Sopenharmony_ci if (!request_mem_region(resource, len, driver_name)) { 366062306a36Sopenharmony_ci ep_dbg(dev, "controller already in use\n"); 366162306a36Sopenharmony_ci retval = -EBUSY; 366262306a36Sopenharmony_ci goto done; 366362306a36Sopenharmony_ci } 366462306a36Sopenharmony_ci dev->region = 1; 366562306a36Sopenharmony_ci 366662306a36Sopenharmony_ci /* FIXME provide firmware download interface to put 366762306a36Sopenharmony_ci * 8051 code into the chip, e.g. to turn on PCI PM. 366862306a36Sopenharmony_ci */ 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_ci base = ioremap(resource, len); 367162306a36Sopenharmony_ci if (base == NULL) { 367262306a36Sopenharmony_ci ep_dbg(dev, "can't map memory\n"); 367362306a36Sopenharmony_ci retval = -EFAULT; 367462306a36Sopenharmony_ci goto done; 367562306a36Sopenharmony_ci } 367662306a36Sopenharmony_ci dev->regs = (struct net2280_regs __iomem *) base; 367762306a36Sopenharmony_ci dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080); 367862306a36Sopenharmony_ci dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100); 367962306a36Sopenharmony_ci dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180); 368062306a36Sopenharmony_ci dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); 368162306a36Sopenharmony_ci dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci if (dev->quirks & PLX_PCIE) { 368462306a36Sopenharmony_ci u32 fsmvalue; 368562306a36Sopenharmony_ci u32 usbstat; 368662306a36Sopenharmony_ci dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) 368762306a36Sopenharmony_ci (base + 0x00b4); 368862306a36Sopenharmony_ci dev->llregs = (struct usb338x_ll_regs __iomem *) 368962306a36Sopenharmony_ci (base + 0x0700); 369062306a36Sopenharmony_ci dev->plregs = (struct usb338x_pl_regs __iomem *) 369162306a36Sopenharmony_ci (base + 0x0800); 369262306a36Sopenharmony_ci usbstat = readl(&dev->usb->usbstat); 369362306a36Sopenharmony_ci dev->enhanced_mode = !!(usbstat & BIT(11)); 369462306a36Sopenharmony_ci dev->n_ep = (dev->enhanced_mode) ? 9 : 5; 369562306a36Sopenharmony_ci /* put into initial config, link up all endpoints */ 369662306a36Sopenharmony_ci fsmvalue = get_idx_reg(dev->regs, SCRATCH) & 369762306a36Sopenharmony_ci (0xf << DEFECT7374_FSM_FIELD); 369862306a36Sopenharmony_ci /* See if firmware needs to set up for workaround: */ 369962306a36Sopenharmony_ci if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) { 370062306a36Sopenharmony_ci dev->bug7734_patched = 1; 370162306a36Sopenharmony_ci writel(0, &dev->usb->usbctl); 370262306a36Sopenharmony_ci } else 370362306a36Sopenharmony_ci dev->bug7734_patched = 0; 370462306a36Sopenharmony_ci } else { 370562306a36Sopenharmony_ci dev->enhanced_mode = 0; 370662306a36Sopenharmony_ci dev->n_ep = 7; 370762306a36Sopenharmony_ci /* put into initial config, link up all endpoints */ 370862306a36Sopenharmony_ci writel(0, &dev->usb->usbctl); 370962306a36Sopenharmony_ci } 371062306a36Sopenharmony_ci 371162306a36Sopenharmony_ci usb_reset(dev); 371262306a36Sopenharmony_ci usb_reinit(dev); 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci /* irq setup after old hardware is cleaned up */ 371562306a36Sopenharmony_ci if (!pdev->irq) { 371662306a36Sopenharmony_ci ep_err(dev, "No IRQ. Check PCI setup!\n"); 371762306a36Sopenharmony_ci retval = -ENODEV; 371862306a36Sopenharmony_ci goto done; 371962306a36Sopenharmony_ci } 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_ci if (dev->quirks & PLX_PCIE) 372262306a36Sopenharmony_ci if (pci_enable_msi(pdev)) 372362306a36Sopenharmony_ci ep_err(dev, "Failed to enable MSI mode\n"); 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED, 372662306a36Sopenharmony_ci driver_name, dev)) { 372762306a36Sopenharmony_ci ep_err(dev, "request interrupt %d failed\n", pdev->irq); 372862306a36Sopenharmony_ci retval = -EBUSY; 372962306a36Sopenharmony_ci goto done; 373062306a36Sopenharmony_ci } 373162306a36Sopenharmony_ci dev->got_irq = 1; 373262306a36Sopenharmony_ci 373362306a36Sopenharmony_ci /* DMA setup */ 373462306a36Sopenharmony_ci /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */ 373562306a36Sopenharmony_ci dev->requests = dma_pool_create("requests", &pdev->dev, 373662306a36Sopenharmony_ci sizeof(struct net2280_dma), 373762306a36Sopenharmony_ci 0 /* no alignment requirements */, 373862306a36Sopenharmony_ci 0 /* or page-crossing issues */); 373962306a36Sopenharmony_ci if (!dev->requests) { 374062306a36Sopenharmony_ci ep_dbg(dev, "can't get request pool\n"); 374162306a36Sopenharmony_ci retval = -ENOMEM; 374262306a36Sopenharmony_ci goto done; 374362306a36Sopenharmony_ci } 374462306a36Sopenharmony_ci for (i = 1; i < 5; i++) { 374562306a36Sopenharmony_ci struct net2280_dma *td; 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ci td = dma_pool_alloc(dev->requests, GFP_KERNEL, 374862306a36Sopenharmony_ci &dev->ep[i].td_dma); 374962306a36Sopenharmony_ci if (!td) { 375062306a36Sopenharmony_ci ep_dbg(dev, "can't get dummy %d\n", i); 375162306a36Sopenharmony_ci retval = -ENOMEM; 375262306a36Sopenharmony_ci goto done; 375362306a36Sopenharmony_ci } 375462306a36Sopenharmony_ci td->dmacount = 0; /* not VALID */ 375562306a36Sopenharmony_ci td->dmadesc = td->dmaaddr; 375662306a36Sopenharmony_ci dev->ep[i].dummy = td; 375762306a36Sopenharmony_ci } 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_ci /* enable lower-overhead pci memory bursts during DMA */ 376062306a36Sopenharmony_ci if (dev->quirks & PLX_LEGACY) 376162306a36Sopenharmony_ci writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) | 376262306a36Sopenharmony_ci /* 376362306a36Sopenharmony_ci * 256 write retries may not be enough... 376462306a36Sopenharmony_ci BIT(PCI_RETRY_ABORT_ENABLE) | 376562306a36Sopenharmony_ci */ 376662306a36Sopenharmony_ci BIT(DMA_READ_MULTIPLE_ENABLE) | 376762306a36Sopenharmony_ci BIT(DMA_READ_LINE_ENABLE), 376862306a36Sopenharmony_ci &dev->pci->pcimstctl); 376962306a36Sopenharmony_ci /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ 377062306a36Sopenharmony_ci pci_set_master(pdev); 377162306a36Sopenharmony_ci pci_try_set_mwi(pdev); 377262306a36Sopenharmony_ci 377362306a36Sopenharmony_ci /* ... also flushes any posted pci writes */ 377462306a36Sopenharmony_ci dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff; 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci /* done */ 377762306a36Sopenharmony_ci ep_info(dev, "%s\n", driver_desc); 377862306a36Sopenharmony_ci ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n", 377962306a36Sopenharmony_ci pdev->irq, base, dev->chiprev); 378062306a36Sopenharmony_ci ep_info(dev, "version: " DRIVER_VERSION "; %s\n", 378162306a36Sopenharmony_ci dev->enhanced_mode ? "enhanced mode" : "legacy mode"); 378262306a36Sopenharmony_ci retval = device_create_file(&pdev->dev, &dev_attr_registers); 378362306a36Sopenharmony_ci if (retval) 378462306a36Sopenharmony_ci goto done; 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_ci retval = usb_add_gadget(&dev->gadget); 378762306a36Sopenharmony_ci if (retval) 378862306a36Sopenharmony_ci goto done; 378962306a36Sopenharmony_ci dev->added = 1; 379062306a36Sopenharmony_ci return 0; 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_cidone: 379362306a36Sopenharmony_ci if (dev) { 379462306a36Sopenharmony_ci net2280_remove(pdev); 379562306a36Sopenharmony_ci kfree(dev); 379662306a36Sopenharmony_ci } 379762306a36Sopenharmony_ci return retval; 379862306a36Sopenharmony_ci} 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci/* make sure the board is quiescent; otherwise it will continue 380162306a36Sopenharmony_ci * generating IRQs across the upcoming reboot. 380262306a36Sopenharmony_ci */ 380362306a36Sopenharmony_ci 380462306a36Sopenharmony_cistatic void net2280_shutdown(struct pci_dev *pdev) 380562306a36Sopenharmony_ci{ 380662306a36Sopenharmony_ci struct net2280 *dev = pci_get_drvdata(pdev); 380762306a36Sopenharmony_ci 380862306a36Sopenharmony_ci /* disable IRQs */ 380962306a36Sopenharmony_ci writel(0, &dev->regs->pciirqenb0); 381062306a36Sopenharmony_ci writel(0, &dev->regs->pciirqenb1); 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_ci /* disable the pullup so the host will think we're gone */ 381362306a36Sopenharmony_ci writel(0, &dev->usb->usbctl); 381462306a36Sopenharmony_ci 381562306a36Sopenharmony_ci} 381662306a36Sopenharmony_ci 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_cistatic const struct pci_device_id pci_ids[] = { { 382162306a36Sopenharmony_ci .class = PCI_CLASS_SERIAL_USB_DEVICE, 382262306a36Sopenharmony_ci .class_mask = ~0, 382362306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX_LEGACY, 382462306a36Sopenharmony_ci .device = 0x2280, 382562306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 382662306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 382762306a36Sopenharmony_ci .driver_data = PLX_LEGACY | PLX_2280, 382862306a36Sopenharmony_ci }, { 382962306a36Sopenharmony_ci .class = PCI_CLASS_SERIAL_USB_DEVICE, 383062306a36Sopenharmony_ci .class_mask = ~0, 383162306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX_LEGACY, 383262306a36Sopenharmony_ci .device = 0x2282, 383362306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 383462306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 383562306a36Sopenharmony_ci .driver_data = PLX_LEGACY, 383662306a36Sopenharmony_ci }, 383762306a36Sopenharmony_ci { 383862306a36Sopenharmony_ci .class = PCI_CLASS_SERIAL_USB_DEVICE, 383962306a36Sopenharmony_ci .class_mask = ~0, 384062306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX, 384162306a36Sopenharmony_ci .device = 0x2380, 384262306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 384362306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 384462306a36Sopenharmony_ci .driver_data = PLX_PCIE, 384562306a36Sopenharmony_ci }, 384662306a36Sopenharmony_ci { 384762306a36Sopenharmony_ci .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), 384862306a36Sopenharmony_ci .class_mask = ~0, 384962306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX, 385062306a36Sopenharmony_ci .device = 0x3380, 385162306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 385262306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 385362306a36Sopenharmony_ci .driver_data = PLX_PCIE | PLX_SUPERSPEED, 385462306a36Sopenharmony_ci }, 385562306a36Sopenharmony_ci { 385662306a36Sopenharmony_ci .class = PCI_CLASS_SERIAL_USB_DEVICE, 385762306a36Sopenharmony_ci .class_mask = ~0, 385862306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_PLX, 385962306a36Sopenharmony_ci .device = 0x3382, 386062306a36Sopenharmony_ci .subvendor = PCI_ANY_ID, 386162306a36Sopenharmony_ci .subdevice = PCI_ANY_ID, 386262306a36Sopenharmony_ci .driver_data = PLX_PCIE | PLX_SUPERSPEED, 386362306a36Sopenharmony_ci }, 386462306a36Sopenharmony_ci{ /* end: all zeroes */ } 386562306a36Sopenharmony_ci}; 386662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pci_ids); 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci/* pci driver glue; this is a "new style" PCI driver module */ 386962306a36Sopenharmony_cistatic struct pci_driver net2280_pci_driver = { 387062306a36Sopenharmony_ci .name = driver_name, 387162306a36Sopenharmony_ci .id_table = pci_ids, 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci .probe = net2280_probe, 387462306a36Sopenharmony_ci .remove = net2280_remove, 387562306a36Sopenharmony_ci .shutdown = net2280_shutdown, 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_ci /* FIXME add power management support */ 387862306a36Sopenharmony_ci}; 387962306a36Sopenharmony_ci 388062306a36Sopenharmony_cimodule_pci_driver(net2280_pci_driver); 388162306a36Sopenharmony_ci 388262306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 388362306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell"); 388462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3885