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(&regs->pciirqenb0);
39862306a36Sopenharmony_ci		tmp &= ~BIT(ep->num);
39962306a36Sopenharmony_ci		writel(tmp, &regs->pciirqenb0);
40062306a36Sopenharmony_ci	} else {
40162306a36Sopenharmony_ci		tmp = readl(&regs->pciirqenb1);
40262306a36Sopenharmony_ci		tmp &= ~BIT((8 + ep->num));	/* completion */
40362306a36Sopenharmony_ci		writel(tmp, &regs->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(&regs->pciirqenb0);
48362306a36Sopenharmony_ci		tmp &= ~BIT(ep_bit[ep->num]);
48462306a36Sopenharmony_ci		writel(tmp, &regs->pciirqenb0);
48562306a36Sopenharmony_ci	} else {
48662306a36Sopenharmony_ci		if (ep->num < 5) {
48762306a36Sopenharmony_ci			tmp = readl(&regs->pciirqenb1);
48862306a36Sopenharmony_ci			tmp &= ~BIT((8 + ep->num));	/* completion */
48962306a36Sopenharmony_ci			writel(tmp, &regs->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, &regs->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, &regs->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(&regs->ep_avail);
73662306a36Sopenharmony_ci	if (unlikely(count == 0)) {
73762306a36Sopenharmony_ci		udelay(1);
73862306a36Sopenharmony_ci		tmp = readl(&ep->regs->ep_stat);
73962306a36Sopenharmony_ci		count = readl(&regs->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(&regs->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(&regs->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