162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * bcm63xx_udc.c -- BCM63xx UDC high/full speed USB device controller
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
662306a36Sopenharmony_ci * Copyright (C) 2012 Broadcom Corporation
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/bug.h>
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/compiler.h>
1362306a36Sopenharmony_ci#include <linux/debugfs.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/device.h>
1662306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1762306a36Sopenharmony_ci#include <linux/errno.h>
1862306a36Sopenharmony_ci#include <linux/interrupt.h>
1962306a36Sopenharmony_ci#include <linux/ioport.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/list.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/moduleparam.h>
2462306a36Sopenharmony_ci#include <linux/platform_device.h>
2562306a36Sopenharmony_ci#include <linux/sched.h>
2662306a36Sopenharmony_ci#include <linux/seq_file.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/timer.h>
2962306a36Sopenharmony_ci#include <linux/usb.h>
3062306a36Sopenharmony_ci#include <linux/usb/ch9.h>
3162306a36Sopenharmony_ci#include <linux/usb/gadget.h>
3262306a36Sopenharmony_ci#include <linux/workqueue.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include <bcm63xx_cpu.h>
3562306a36Sopenharmony_ci#include <bcm63xx_iudma.h>
3662306a36Sopenharmony_ci#include <bcm63xx_dev_usb_usbd.h>
3762306a36Sopenharmony_ci#include <bcm63xx_io.h>
3862306a36Sopenharmony_ci#include <bcm63xx_regs.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define DRV_MODULE_NAME		"bcm63xx_udc"
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic const char bcm63xx_ep0name[] = "ep0";
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const struct {
4562306a36Sopenharmony_ci	const char *name;
4662306a36Sopenharmony_ci	const struct usb_ep_caps caps;
4762306a36Sopenharmony_ci} bcm63xx_ep_info[] = {
4862306a36Sopenharmony_ci#define EP_INFO(_name, _caps) \
4962306a36Sopenharmony_ci	{ \
5062306a36Sopenharmony_ci		.name = _name, \
5162306a36Sopenharmony_ci		.caps = _caps, \
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	EP_INFO(bcm63xx_ep0name,
5562306a36Sopenharmony_ci		USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, USB_EP_CAPS_DIR_ALL)),
5662306a36Sopenharmony_ci	EP_INFO("ep1in-bulk",
5762306a36Sopenharmony_ci		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
5862306a36Sopenharmony_ci	EP_INFO("ep2out-bulk",
5962306a36Sopenharmony_ci		USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
6062306a36Sopenharmony_ci	EP_INFO("ep3in-int",
6162306a36Sopenharmony_ci		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_IN)),
6262306a36Sopenharmony_ci	EP_INFO("ep4out-int",
6362306a36Sopenharmony_ci		USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, USB_EP_CAPS_DIR_OUT)),
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#undef EP_INFO
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic bool use_fullspeed;
6962306a36Sopenharmony_cimodule_param(use_fullspeed, bool, S_IRUGO);
7062306a36Sopenharmony_ciMODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * RX IRQ coalescing options:
7462306a36Sopenharmony_ci *
7562306a36Sopenharmony_ci * false (default) - one IRQ per DATAx packet.  Slow but reliable.  The
7662306a36Sopenharmony_ci * driver is able to pass the "testusb" suite and recover from conditions like:
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci *   1) Device queues up a 2048-byte RX IUDMA transaction on an OUT bulk ep
7962306a36Sopenharmony_ci *   2) Host sends 512 bytes of data
8062306a36Sopenharmony_ci *   3) Host decides to reconfigure the device and sends SET_INTERFACE
8162306a36Sopenharmony_ci *   4) Device shuts down the endpoint and cancels the RX transaction
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * true - one IRQ per transfer, for transfers <= 2048B.  Generates
8462306a36Sopenharmony_ci * considerably fewer IRQs, but error recovery is less robust.  Does not
8562306a36Sopenharmony_ci * reliably pass "testusb".
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * TX always uses coalescing, because we can cancel partially complete TX
8862306a36Sopenharmony_ci * transfers by repeatedly flushing the FIFO.  The hardware doesn't allow
8962306a36Sopenharmony_ci * this on RX.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_cistatic bool irq_coalesce;
9262306a36Sopenharmony_cimodule_param(irq_coalesce, bool, S_IRUGO);
9362306a36Sopenharmony_ciMODULE_PARM_DESC(irq_coalesce, "take one IRQ per RX transfer");
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define BCM63XX_NUM_EP			5
9662306a36Sopenharmony_ci#define BCM63XX_NUM_IUDMA		6
9762306a36Sopenharmony_ci#define BCM63XX_NUM_FIFO_PAIRS		3
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci#define IUDMA_RESET_TIMEOUT_US		10000
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define IUDMA_EP0_RXCHAN		0
10262306a36Sopenharmony_ci#define IUDMA_EP0_TXCHAN		1
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define IUDMA_MAX_FRAGMENT		2048
10562306a36Sopenharmony_ci#define BCM63XX_MAX_CTRL_PKT		64
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#define BCMEP_CTRL			0x00
10862306a36Sopenharmony_ci#define BCMEP_ISOC			0x01
10962306a36Sopenharmony_ci#define BCMEP_BULK			0x02
11062306a36Sopenharmony_ci#define BCMEP_INTR			0x03
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define BCMEP_OUT			0x00
11362306a36Sopenharmony_ci#define BCMEP_IN			0x01
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define BCM63XX_SPD_FULL		1
11662306a36Sopenharmony_ci#define BCM63XX_SPD_HIGH		0
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define IUDMA_DMAC_OFFSET		0x200
11962306a36Sopenharmony_ci#define IUDMA_DMAS_OFFSET		0x400
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cienum bcm63xx_ep0_state {
12262306a36Sopenharmony_ci	EP0_REQUEUE,
12362306a36Sopenharmony_ci	EP0_IDLE,
12462306a36Sopenharmony_ci	EP0_IN_DATA_PHASE_SETUP,
12562306a36Sopenharmony_ci	EP0_IN_DATA_PHASE_COMPLETE,
12662306a36Sopenharmony_ci	EP0_OUT_DATA_PHASE_SETUP,
12762306a36Sopenharmony_ci	EP0_OUT_DATA_PHASE_COMPLETE,
12862306a36Sopenharmony_ci	EP0_OUT_STATUS_PHASE,
12962306a36Sopenharmony_ci	EP0_IN_FAKE_STATUS_PHASE,
13062306a36Sopenharmony_ci	EP0_SHUTDOWN,
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic const char __maybe_unused bcm63xx_ep0_state_names[][32] = {
13462306a36Sopenharmony_ci	"REQUEUE",
13562306a36Sopenharmony_ci	"IDLE",
13662306a36Sopenharmony_ci	"IN_DATA_PHASE_SETUP",
13762306a36Sopenharmony_ci	"IN_DATA_PHASE_COMPLETE",
13862306a36Sopenharmony_ci	"OUT_DATA_PHASE_SETUP",
13962306a36Sopenharmony_ci	"OUT_DATA_PHASE_COMPLETE",
14062306a36Sopenharmony_ci	"OUT_STATUS_PHASE",
14162306a36Sopenharmony_ci	"IN_FAKE_STATUS_PHASE",
14262306a36Sopenharmony_ci	"SHUTDOWN",
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/**
14662306a36Sopenharmony_ci * struct iudma_ch_cfg - Static configuration for an IUDMA channel.
14762306a36Sopenharmony_ci * @ep_num: USB endpoint number.
14862306a36Sopenharmony_ci * @n_bds: Number of buffer descriptors in the ring.
14962306a36Sopenharmony_ci * @ep_type: Endpoint type (control, bulk, interrupt).
15062306a36Sopenharmony_ci * @dir: Direction (in, out).
15162306a36Sopenharmony_ci * @n_fifo_slots: Number of FIFO entries to allocate for this channel.
15262306a36Sopenharmony_ci * @max_pkt_hs: Maximum packet size in high speed mode.
15362306a36Sopenharmony_ci * @max_pkt_fs: Maximum packet size in full speed mode.
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_cistruct iudma_ch_cfg {
15662306a36Sopenharmony_ci	int				ep_num;
15762306a36Sopenharmony_ci	int				n_bds;
15862306a36Sopenharmony_ci	int				ep_type;
15962306a36Sopenharmony_ci	int				dir;
16062306a36Sopenharmony_ci	int				n_fifo_slots;
16162306a36Sopenharmony_ci	int				max_pkt_hs;
16262306a36Sopenharmony_ci	int				max_pkt_fs;
16362306a36Sopenharmony_ci};
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic const struct iudma_ch_cfg iudma_defaults[] = {
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/* This controller was designed to support a CDC/RNDIS application.
16862306a36Sopenharmony_ci	   It may be possible to reconfigure some of the endpoints, but
16962306a36Sopenharmony_ci	   the hardware limitations (FIFO sizing and number of DMA channels)
17062306a36Sopenharmony_ci	   may significantly impact flexibility and/or stability.  Change
17162306a36Sopenharmony_ci	   these values at your own risk.
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	      ep_num       ep_type           n_fifo_slots    max_pkt_fs
17462306a36Sopenharmony_ci	idx      |  n_bds     |         dir       |  max_pkt_hs  |
17562306a36Sopenharmony_ci	 |       |    |       |          |        |      |       |       */
17662306a36Sopenharmony_ci	[0] = { -1,   4, BCMEP_CTRL, BCMEP_OUT,  32,    64,     64 },
17762306a36Sopenharmony_ci	[1] = {  0,   4, BCMEP_CTRL, BCMEP_OUT,  32,    64,     64 },
17862306a36Sopenharmony_ci	[2] = {  2,  16, BCMEP_BULK, BCMEP_OUT, 128,   512,     64 },
17962306a36Sopenharmony_ci	[3] = {  1,  16, BCMEP_BULK, BCMEP_IN,  128,   512,     64 },
18062306a36Sopenharmony_ci	[4] = {  4,   4, BCMEP_INTR, BCMEP_OUT,  32,    64,     64 },
18162306a36Sopenharmony_ci	[5] = {  3,   4, BCMEP_INTR, BCMEP_IN,   32,    64,     64 },
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistruct bcm63xx_udc;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * struct iudma_ch - Represents the current state of a single IUDMA channel.
18862306a36Sopenharmony_ci * @ch_idx: IUDMA channel index (0 to BCM63XX_NUM_IUDMA-1).
18962306a36Sopenharmony_ci * @ep_num: USB endpoint number.  -1 for ep0 RX.
19062306a36Sopenharmony_ci * @enabled: Whether bcm63xx_ep_enable() has been called.
19162306a36Sopenharmony_ci * @max_pkt: "Chunk size" on the USB interface.  Based on interface speed.
19262306a36Sopenharmony_ci * @is_tx: true for TX, false for RX.
19362306a36Sopenharmony_ci * @bep: Pointer to the associated endpoint.  NULL for ep0 RX.
19462306a36Sopenharmony_ci * @udc: Reference to the device controller.
19562306a36Sopenharmony_ci * @read_bd: Next buffer descriptor to reap from the hardware.
19662306a36Sopenharmony_ci * @write_bd: Next BD available for a new packet.
19762306a36Sopenharmony_ci * @end_bd: Points to the final BD in the ring.
19862306a36Sopenharmony_ci * @n_bds_used: Number of BD entries currently occupied.
19962306a36Sopenharmony_ci * @bd_ring: Base pointer to the BD ring.
20062306a36Sopenharmony_ci * @bd_ring_dma: Physical (DMA) address of bd_ring.
20162306a36Sopenharmony_ci * @n_bds: Total number of BDs in the ring.
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * ep0 has two IUDMA channels (IUDMA_EP0_RXCHAN and IUDMA_EP0_TXCHAN), as it is
20462306a36Sopenharmony_ci * bidirectional.  The "struct usb_ep" associated with ep0 is for TX (IN)
20562306a36Sopenharmony_ci * only.
20662306a36Sopenharmony_ci *
20762306a36Sopenharmony_ci * Each bulk/intr endpoint has a single IUDMA channel and a single
20862306a36Sopenharmony_ci * struct usb_ep.
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_cistruct iudma_ch {
21162306a36Sopenharmony_ci	unsigned int			ch_idx;
21262306a36Sopenharmony_ci	int				ep_num;
21362306a36Sopenharmony_ci	bool				enabled;
21462306a36Sopenharmony_ci	int				max_pkt;
21562306a36Sopenharmony_ci	bool				is_tx;
21662306a36Sopenharmony_ci	struct bcm63xx_ep		*bep;
21762306a36Sopenharmony_ci	struct bcm63xx_udc		*udc;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	struct bcm_enet_desc		*read_bd;
22062306a36Sopenharmony_ci	struct bcm_enet_desc		*write_bd;
22162306a36Sopenharmony_ci	struct bcm_enet_desc		*end_bd;
22262306a36Sopenharmony_ci	int				n_bds_used;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	struct bcm_enet_desc		*bd_ring;
22562306a36Sopenharmony_ci	dma_addr_t			bd_ring_dma;
22662306a36Sopenharmony_ci	unsigned int			n_bds;
22762306a36Sopenharmony_ci};
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/**
23062306a36Sopenharmony_ci * struct bcm63xx_ep - Internal (driver) state of a single endpoint.
23162306a36Sopenharmony_ci * @ep_num: USB endpoint number.
23262306a36Sopenharmony_ci * @iudma: Pointer to IUDMA channel state.
23362306a36Sopenharmony_ci * @ep: USB gadget layer representation of the EP.
23462306a36Sopenharmony_ci * @udc: Reference to the device controller.
23562306a36Sopenharmony_ci * @queue: Linked list of outstanding requests for this EP.
23662306a36Sopenharmony_ci * @halted: 1 if the EP is stalled; 0 otherwise.
23762306a36Sopenharmony_ci */
23862306a36Sopenharmony_cistruct bcm63xx_ep {
23962306a36Sopenharmony_ci	unsigned int			ep_num;
24062306a36Sopenharmony_ci	struct iudma_ch			*iudma;
24162306a36Sopenharmony_ci	struct usb_ep			ep;
24262306a36Sopenharmony_ci	struct bcm63xx_udc		*udc;
24362306a36Sopenharmony_ci	struct list_head		queue;
24462306a36Sopenharmony_ci	unsigned			halted:1;
24562306a36Sopenharmony_ci};
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/**
24862306a36Sopenharmony_ci * struct bcm63xx_req - Internal (driver) state of a single request.
24962306a36Sopenharmony_ci * @queue: Links back to the EP's request list.
25062306a36Sopenharmony_ci * @req: USB gadget layer representation of the request.
25162306a36Sopenharmony_ci * @offset: Current byte offset into the data buffer (next byte to queue).
25262306a36Sopenharmony_ci * @bd_bytes: Number of data bytes in outstanding BD entries.
25362306a36Sopenharmony_ci * @iudma: IUDMA channel used for the request.
25462306a36Sopenharmony_ci */
25562306a36Sopenharmony_cistruct bcm63xx_req {
25662306a36Sopenharmony_ci	struct list_head		queue;		/* ep's requests */
25762306a36Sopenharmony_ci	struct usb_request		req;
25862306a36Sopenharmony_ci	unsigned int			offset;
25962306a36Sopenharmony_ci	unsigned int			bd_bytes;
26062306a36Sopenharmony_ci	struct iudma_ch			*iudma;
26162306a36Sopenharmony_ci};
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci/**
26462306a36Sopenharmony_ci * struct bcm63xx_udc - Driver/hardware private context.
26562306a36Sopenharmony_ci * @lock: Spinlock to mediate access to this struct, and (most) HW regs.
26662306a36Sopenharmony_ci * @dev: Generic Linux device structure.
26762306a36Sopenharmony_ci * @pd: Platform data (board/port info).
26862306a36Sopenharmony_ci * @usbd_clk: Clock descriptor for the USB device block.
26962306a36Sopenharmony_ci * @usbh_clk: Clock descriptor for the USB host block.
27062306a36Sopenharmony_ci * @gadget: USB device.
27162306a36Sopenharmony_ci * @driver: Driver for USB device.
27262306a36Sopenharmony_ci * @usbd_regs: Base address of the USBD/USB20D block.
27362306a36Sopenharmony_ci * @iudma_regs: Base address of the USBD's associated IUDMA block.
27462306a36Sopenharmony_ci * @bep: Array of endpoints, including ep0.
27562306a36Sopenharmony_ci * @iudma: Array of all IUDMA channels used by this controller.
27662306a36Sopenharmony_ci * @cfg: USB configuration number, from SET_CONFIGURATION wValue.
27762306a36Sopenharmony_ci * @iface: USB interface number, from SET_INTERFACE wIndex.
27862306a36Sopenharmony_ci * @alt_iface: USB alt interface number, from SET_INTERFACE wValue.
27962306a36Sopenharmony_ci * @ep0_ctrl_req: Request object for bcm63xx_udc-initiated ep0 transactions.
28062306a36Sopenharmony_ci * @ep0_ctrl_buf: Data buffer for ep0_ctrl_req.
28162306a36Sopenharmony_ci * @ep0state: Current state of the ep0 state machine.
28262306a36Sopenharmony_ci * @ep0_wq: Workqueue struct used to wake up the ep0 state machine.
28362306a36Sopenharmony_ci * @wedgemap: Bitmap of wedged endpoints.
28462306a36Sopenharmony_ci * @ep0_req_reset: USB reset is pending.
28562306a36Sopenharmony_ci * @ep0_req_set_cfg: Need to spoof a SET_CONFIGURATION packet.
28662306a36Sopenharmony_ci * @ep0_req_set_iface: Need to spoof a SET_INTERFACE packet.
28762306a36Sopenharmony_ci * @ep0_req_shutdown: Driver is shutting down; requesting ep0 to halt activity.
28862306a36Sopenharmony_ci * @ep0_req_completed: ep0 request has completed; worker has not seen it yet.
28962306a36Sopenharmony_ci * @ep0_reply: Pending reply from gadget driver.
29062306a36Sopenharmony_ci * @ep0_request: Outstanding ep0 request.
29162306a36Sopenharmony_ci */
29262306a36Sopenharmony_cistruct bcm63xx_udc {
29362306a36Sopenharmony_ci	spinlock_t			lock;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	struct device			*dev;
29662306a36Sopenharmony_ci	struct bcm63xx_usbd_platform_data *pd;
29762306a36Sopenharmony_ci	struct clk			*usbd_clk;
29862306a36Sopenharmony_ci	struct clk			*usbh_clk;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	struct usb_gadget		gadget;
30162306a36Sopenharmony_ci	struct usb_gadget_driver	*driver;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	void __iomem			*usbd_regs;
30462306a36Sopenharmony_ci	void __iomem			*iudma_regs;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	struct bcm63xx_ep		bep[BCM63XX_NUM_EP];
30762306a36Sopenharmony_ci	struct iudma_ch			iudma[BCM63XX_NUM_IUDMA];
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	int				cfg;
31062306a36Sopenharmony_ci	int				iface;
31162306a36Sopenharmony_ci	int				alt_iface;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	struct bcm63xx_req		ep0_ctrl_req;
31462306a36Sopenharmony_ci	u8				*ep0_ctrl_buf;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	int				ep0state;
31762306a36Sopenharmony_ci	struct work_struct		ep0_wq;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	unsigned long			wedgemap;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	unsigned			ep0_req_reset:1;
32262306a36Sopenharmony_ci	unsigned			ep0_req_set_cfg:1;
32362306a36Sopenharmony_ci	unsigned			ep0_req_set_iface:1;
32462306a36Sopenharmony_ci	unsigned			ep0_req_shutdown:1;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	unsigned			ep0_req_completed:1;
32762306a36Sopenharmony_ci	struct usb_request		*ep0_reply;
32862306a36Sopenharmony_ci	struct usb_request		*ep0_request;
32962306a36Sopenharmony_ci};
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic const struct usb_ep_ops bcm63xx_udc_ep_ops;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/***********************************************************************
33462306a36Sopenharmony_ci * Convenience functions
33562306a36Sopenharmony_ci ***********************************************************************/
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic inline struct bcm63xx_udc *gadget_to_udc(struct usb_gadget *g)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	return container_of(g, struct bcm63xx_udc, gadget);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic inline struct bcm63xx_ep *our_ep(struct usb_ep *ep)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	return container_of(ep, struct bcm63xx_ep, ep);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic inline struct bcm63xx_req *our_req(struct usb_request *req)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	return container_of(req, struct bcm63xx_req, req);
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic inline u32 usbd_readl(struct bcm63xx_udc *udc, u32 off)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	return bcm_readl(udc->usbd_regs + off);
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic inline void usbd_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	bcm_writel(val, udc->usbd_regs + off);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic inline u32 usb_dma_readl(struct bcm63xx_udc *udc, u32 off)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	return bcm_readl(udc->iudma_regs + off);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic inline void usb_dma_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	bcm_writel(val, udc->iudma_regs + off);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic inline u32 usb_dmac_readl(struct bcm63xx_udc *udc, u32 off, int chan)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	return bcm_readl(udc->iudma_regs + IUDMA_DMAC_OFFSET + off +
37562306a36Sopenharmony_ci			(ENETDMA_CHAN_WIDTH * chan));
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic inline void usb_dmac_writel(struct bcm63xx_udc *udc, u32 val, u32 off,
37962306a36Sopenharmony_ci					int chan)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	bcm_writel(val, udc->iudma_regs + IUDMA_DMAC_OFFSET + off +
38262306a36Sopenharmony_ci			(ENETDMA_CHAN_WIDTH * chan));
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic inline u32 usb_dmas_readl(struct bcm63xx_udc *udc, u32 off, int chan)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	return bcm_readl(udc->iudma_regs + IUDMA_DMAS_OFFSET + off +
38862306a36Sopenharmony_ci			(ENETDMA_CHAN_WIDTH * chan));
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic inline void usb_dmas_writel(struct bcm63xx_udc *udc, u32 val, u32 off,
39262306a36Sopenharmony_ci					int chan)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	bcm_writel(val, udc->iudma_regs + IUDMA_DMAS_OFFSET + off +
39562306a36Sopenharmony_ci			(ENETDMA_CHAN_WIDTH * chan));
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic inline void set_clocks(struct bcm63xx_udc *udc, bool is_enabled)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	if (is_enabled) {
40162306a36Sopenharmony_ci		clk_enable(udc->usbh_clk);
40262306a36Sopenharmony_ci		clk_enable(udc->usbd_clk);
40362306a36Sopenharmony_ci		udelay(10);
40462306a36Sopenharmony_ci	} else {
40562306a36Sopenharmony_ci		clk_disable(udc->usbd_clk);
40662306a36Sopenharmony_ci		clk_disable(udc->usbh_clk);
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci/***********************************************************************
41162306a36Sopenharmony_ci * Low-level IUDMA / FIFO operations
41262306a36Sopenharmony_ci ***********************************************************************/
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/**
41562306a36Sopenharmony_ci * bcm63xx_ep_dma_select - Helper function to set up the init_sel signal.
41662306a36Sopenharmony_ci * @udc: Reference to the device controller.
41762306a36Sopenharmony_ci * @idx: Desired init_sel value.
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * The "init_sel" signal is used as a selection index for both endpoints
42062306a36Sopenharmony_ci * and IUDMA channels.  Since these do not map 1:1, the use of this signal
42162306a36Sopenharmony_ci * depends on the context.
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_cistatic void bcm63xx_ep_dma_select(struct bcm63xx_udc *udc, int idx)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	u32 val = usbd_readl(udc, USBD_CONTROL_REG);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	val &= ~USBD_CONTROL_INIT_SEL_MASK;
42862306a36Sopenharmony_ci	val |= idx << USBD_CONTROL_INIT_SEL_SHIFT;
42962306a36Sopenharmony_ci	usbd_writel(udc, val, USBD_CONTROL_REG);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci/**
43362306a36Sopenharmony_ci * bcm63xx_set_stall - Enable/disable stall on one endpoint.
43462306a36Sopenharmony_ci * @udc: Reference to the device controller.
43562306a36Sopenharmony_ci * @bep: Endpoint on which to operate.
43662306a36Sopenharmony_ci * @is_stalled: true to enable stall, false to disable.
43762306a36Sopenharmony_ci *
43862306a36Sopenharmony_ci * See notes in bcm63xx_update_wedge() regarding automatic clearing of
43962306a36Sopenharmony_ci * halt/stall conditions.
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_cistatic void bcm63xx_set_stall(struct bcm63xx_udc *udc, struct bcm63xx_ep *bep,
44262306a36Sopenharmony_ci	bool is_stalled)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	u32 val;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	val = USBD_STALL_UPDATE_MASK |
44762306a36Sopenharmony_ci		(is_stalled ? USBD_STALL_ENABLE_MASK : 0) |
44862306a36Sopenharmony_ci		(bep->ep_num << USBD_STALL_EPNUM_SHIFT);
44962306a36Sopenharmony_ci	usbd_writel(udc, val, USBD_STALL_REG);
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci/**
45362306a36Sopenharmony_ci * bcm63xx_fifo_setup - (Re)initialize FIFO boundaries and settings.
45462306a36Sopenharmony_ci * @udc: Reference to the device controller.
45562306a36Sopenharmony_ci *
45662306a36Sopenharmony_ci * These parameters depend on the USB link speed.  Settings are
45762306a36Sopenharmony_ci * per-IUDMA-channel-pair.
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic void bcm63xx_fifo_setup(struct bcm63xx_udc *udc)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	int is_hs = udc->gadget.speed == USB_SPEED_HIGH;
46262306a36Sopenharmony_ci	u32 i, val, rx_fifo_slot, tx_fifo_slot;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* set up FIFO boundaries and packet sizes; this is done in pairs */
46562306a36Sopenharmony_ci	rx_fifo_slot = tx_fifo_slot = 0;
46662306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_IUDMA; i += 2) {
46762306a36Sopenharmony_ci		const struct iudma_ch_cfg *rx_cfg = &iudma_defaults[i];
46862306a36Sopenharmony_ci		const struct iudma_ch_cfg *tx_cfg = &iudma_defaults[i + 1];
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci		bcm63xx_ep_dma_select(udc, i >> 1);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		val = (rx_fifo_slot << USBD_RXFIFO_CONFIG_START_SHIFT) |
47362306a36Sopenharmony_ci			((rx_fifo_slot + rx_cfg->n_fifo_slots - 1) <<
47462306a36Sopenharmony_ci			 USBD_RXFIFO_CONFIG_END_SHIFT);
47562306a36Sopenharmony_ci		rx_fifo_slot += rx_cfg->n_fifo_slots;
47662306a36Sopenharmony_ci		usbd_writel(udc, val, USBD_RXFIFO_CONFIG_REG);
47762306a36Sopenharmony_ci		usbd_writel(udc,
47862306a36Sopenharmony_ci			    is_hs ? rx_cfg->max_pkt_hs : rx_cfg->max_pkt_fs,
47962306a36Sopenharmony_ci			    USBD_RXFIFO_EPSIZE_REG);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci		val = (tx_fifo_slot << USBD_TXFIFO_CONFIG_START_SHIFT) |
48262306a36Sopenharmony_ci			((tx_fifo_slot + tx_cfg->n_fifo_slots - 1) <<
48362306a36Sopenharmony_ci			 USBD_TXFIFO_CONFIG_END_SHIFT);
48462306a36Sopenharmony_ci		tx_fifo_slot += tx_cfg->n_fifo_slots;
48562306a36Sopenharmony_ci		usbd_writel(udc, val, USBD_TXFIFO_CONFIG_REG);
48662306a36Sopenharmony_ci		usbd_writel(udc,
48762306a36Sopenharmony_ci			    is_hs ? tx_cfg->max_pkt_hs : tx_cfg->max_pkt_fs,
48862306a36Sopenharmony_ci			    USBD_TXFIFO_EPSIZE_REG);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		usbd_readl(udc, USBD_TXFIFO_EPSIZE_REG);
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci/**
49562306a36Sopenharmony_ci * bcm63xx_fifo_reset_ep - Flush a single endpoint's FIFO.
49662306a36Sopenharmony_ci * @udc: Reference to the device controller.
49762306a36Sopenharmony_ci * @ep_num: Endpoint number.
49862306a36Sopenharmony_ci */
49962306a36Sopenharmony_cistatic void bcm63xx_fifo_reset_ep(struct bcm63xx_udc *udc, int ep_num)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	u32 val;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	bcm63xx_ep_dma_select(udc, ep_num);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	val = usbd_readl(udc, USBD_CONTROL_REG);
50662306a36Sopenharmony_ci	val |= USBD_CONTROL_FIFO_RESET_MASK;
50762306a36Sopenharmony_ci	usbd_writel(udc, val, USBD_CONTROL_REG);
50862306a36Sopenharmony_ci	usbd_readl(udc, USBD_CONTROL_REG);
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci/**
51262306a36Sopenharmony_ci * bcm63xx_fifo_reset - Flush all hardware FIFOs.
51362306a36Sopenharmony_ci * @udc: Reference to the device controller.
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_cistatic void bcm63xx_fifo_reset(struct bcm63xx_udc *udc)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	int i;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_FIFO_PAIRS; i++)
52062306a36Sopenharmony_ci		bcm63xx_fifo_reset_ep(udc, i);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci/**
52462306a36Sopenharmony_ci * bcm63xx_ep_init - Initial (one-time) endpoint initialization.
52562306a36Sopenharmony_ci * @udc: Reference to the device controller.
52662306a36Sopenharmony_ci */
52762306a36Sopenharmony_cistatic void bcm63xx_ep_init(struct bcm63xx_udc *udc)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	u32 i, val;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
53262306a36Sopenharmony_ci		const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci		if (cfg->ep_num < 0)
53562306a36Sopenharmony_ci			continue;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci		bcm63xx_ep_dma_select(udc, cfg->ep_num);
53862306a36Sopenharmony_ci		val = (cfg->ep_type << USBD_EPNUM_TYPEMAP_TYPE_SHIFT) |
53962306a36Sopenharmony_ci			((i >> 1) << USBD_EPNUM_TYPEMAP_DMA_CH_SHIFT);
54062306a36Sopenharmony_ci		usbd_writel(udc, val, USBD_EPNUM_TYPEMAP_REG);
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci/**
54562306a36Sopenharmony_ci * bcm63xx_ep_setup - Configure per-endpoint settings.
54662306a36Sopenharmony_ci * @udc: Reference to the device controller.
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * This needs to be rerun if the speed/cfg/intf/altintf changes.
54962306a36Sopenharmony_ci */
55062306a36Sopenharmony_cistatic void bcm63xx_ep_setup(struct bcm63xx_udc *udc)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	u32 val, i;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	usbd_writel(udc, USBD_CSR_SETUPADDR_DEF, USBD_CSR_SETUPADDR_REG);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
55762306a36Sopenharmony_ci		const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
55862306a36Sopenharmony_ci		int max_pkt = udc->gadget.speed == USB_SPEED_HIGH ?
55962306a36Sopenharmony_ci			      cfg->max_pkt_hs : cfg->max_pkt_fs;
56062306a36Sopenharmony_ci		int idx = cfg->ep_num;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		udc->iudma[i].max_pkt = max_pkt;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		if (idx < 0)
56562306a36Sopenharmony_ci			continue;
56662306a36Sopenharmony_ci		usb_ep_set_maxpacket_limit(&udc->bep[idx].ep, max_pkt);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		val = (idx << USBD_CSR_EP_LOG_SHIFT) |
56962306a36Sopenharmony_ci		      (cfg->dir << USBD_CSR_EP_DIR_SHIFT) |
57062306a36Sopenharmony_ci		      (cfg->ep_type << USBD_CSR_EP_TYPE_SHIFT) |
57162306a36Sopenharmony_ci		      (udc->cfg << USBD_CSR_EP_CFG_SHIFT) |
57262306a36Sopenharmony_ci		      (udc->iface << USBD_CSR_EP_IFACE_SHIFT) |
57362306a36Sopenharmony_ci		      (udc->alt_iface << USBD_CSR_EP_ALTIFACE_SHIFT) |
57462306a36Sopenharmony_ci		      (max_pkt << USBD_CSR_EP_MAXPKT_SHIFT);
57562306a36Sopenharmony_ci		usbd_writel(udc, val, USBD_CSR_EP_REG(idx));
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci/**
58062306a36Sopenharmony_ci * iudma_write - Queue a single IUDMA transaction.
58162306a36Sopenharmony_ci * @udc: Reference to the device controller.
58262306a36Sopenharmony_ci * @iudma: IUDMA channel to use.
58362306a36Sopenharmony_ci * @breq: Request containing the transaction data.
58462306a36Sopenharmony_ci *
58562306a36Sopenharmony_ci * For RX IUDMA, this will queue a single buffer descriptor, as RX IUDMA
58662306a36Sopenharmony_ci * does not honor SOP/EOP so the handling of multiple buffers is ambiguous.
58762306a36Sopenharmony_ci * So iudma_write() may be called several times to fulfill a single
58862306a36Sopenharmony_ci * usb_request.
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * For TX IUDMA, this can queue multiple buffer descriptors if needed.
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_cistatic void iudma_write(struct bcm63xx_udc *udc, struct iudma_ch *iudma,
59362306a36Sopenharmony_ci	struct bcm63xx_req *breq)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	int first_bd = 1, last_bd = 0, extra_zero_pkt = 0;
59662306a36Sopenharmony_ci	unsigned int bytes_left = breq->req.length - breq->offset;
59762306a36Sopenharmony_ci	const int max_bd_bytes = !irq_coalesce && !iudma->is_tx ?
59862306a36Sopenharmony_ci		iudma->max_pkt : IUDMA_MAX_FRAGMENT;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	iudma->n_bds_used = 0;
60162306a36Sopenharmony_ci	breq->bd_bytes = 0;
60262306a36Sopenharmony_ci	breq->iudma = iudma;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if ((bytes_left % iudma->max_pkt == 0) && bytes_left && breq->req.zero)
60562306a36Sopenharmony_ci		extra_zero_pkt = 1;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	do {
60862306a36Sopenharmony_ci		struct bcm_enet_desc *d = iudma->write_bd;
60962306a36Sopenharmony_ci		u32 dmaflags = 0;
61062306a36Sopenharmony_ci		unsigned int n_bytes;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		if (d == iudma->end_bd) {
61362306a36Sopenharmony_ci			dmaflags |= DMADESC_WRAP_MASK;
61462306a36Sopenharmony_ci			iudma->write_bd = iudma->bd_ring;
61562306a36Sopenharmony_ci		} else {
61662306a36Sopenharmony_ci			iudma->write_bd++;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci		iudma->n_bds_used++;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		n_bytes = min_t(int, bytes_left, max_bd_bytes);
62162306a36Sopenharmony_ci		if (n_bytes)
62262306a36Sopenharmony_ci			dmaflags |= n_bytes << DMADESC_LENGTH_SHIFT;
62362306a36Sopenharmony_ci		else
62462306a36Sopenharmony_ci			dmaflags |= (1 << DMADESC_LENGTH_SHIFT) |
62562306a36Sopenharmony_ci				    DMADESC_USB_ZERO_MASK;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		dmaflags |= DMADESC_OWNER_MASK;
62862306a36Sopenharmony_ci		if (first_bd) {
62962306a36Sopenharmony_ci			dmaflags |= DMADESC_SOP_MASK;
63062306a36Sopenharmony_ci			first_bd = 0;
63162306a36Sopenharmony_ci		}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci		/*
63462306a36Sopenharmony_ci		 * extra_zero_pkt forces one more iteration through the loop
63562306a36Sopenharmony_ci		 * after all data is queued up, to send the zero packet
63662306a36Sopenharmony_ci		 */
63762306a36Sopenharmony_ci		if (extra_zero_pkt && !bytes_left)
63862306a36Sopenharmony_ci			extra_zero_pkt = 0;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci		if (!iudma->is_tx || iudma->n_bds_used == iudma->n_bds ||
64162306a36Sopenharmony_ci		    (n_bytes == bytes_left && !extra_zero_pkt)) {
64262306a36Sopenharmony_ci			last_bd = 1;
64362306a36Sopenharmony_ci			dmaflags |= DMADESC_EOP_MASK;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		d->address = breq->req.dma + breq->offset;
64762306a36Sopenharmony_ci		mb();
64862306a36Sopenharmony_ci		d->len_stat = dmaflags;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci		breq->offset += n_bytes;
65162306a36Sopenharmony_ci		breq->bd_bytes += n_bytes;
65262306a36Sopenharmony_ci		bytes_left -= n_bytes;
65362306a36Sopenharmony_ci	} while (!last_bd);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	usb_dmac_writel(udc, ENETDMAC_CHANCFG_EN_MASK,
65662306a36Sopenharmony_ci			ENETDMAC_CHANCFG_REG, iudma->ch_idx);
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci/**
66062306a36Sopenharmony_ci * iudma_read - Check for IUDMA buffer completion.
66162306a36Sopenharmony_ci * @udc: Reference to the device controller.
66262306a36Sopenharmony_ci * @iudma: IUDMA channel to use.
66362306a36Sopenharmony_ci *
66462306a36Sopenharmony_ci * This checks to see if ALL of the outstanding BDs on the DMA channel
66562306a36Sopenharmony_ci * have been filled.  If so, it returns the actual transfer length;
66662306a36Sopenharmony_ci * otherwise it returns -EBUSY.
66762306a36Sopenharmony_ci */
66862306a36Sopenharmony_cistatic int iudma_read(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	int i, actual_len = 0;
67162306a36Sopenharmony_ci	struct bcm_enet_desc *d = iudma->read_bd;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	if (!iudma->n_bds_used)
67462306a36Sopenharmony_ci		return -EINVAL;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	for (i = 0; i < iudma->n_bds_used; i++) {
67762306a36Sopenharmony_ci		u32 dmaflags;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		dmaflags = d->len_stat;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		if (dmaflags & DMADESC_OWNER_MASK)
68262306a36Sopenharmony_ci			return -EBUSY;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		actual_len += (dmaflags & DMADESC_LENGTH_MASK) >>
68562306a36Sopenharmony_ci			      DMADESC_LENGTH_SHIFT;
68662306a36Sopenharmony_ci		if (d == iudma->end_bd)
68762306a36Sopenharmony_ci			d = iudma->bd_ring;
68862306a36Sopenharmony_ci		else
68962306a36Sopenharmony_ci			d++;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	iudma->read_bd = d;
69362306a36Sopenharmony_ci	iudma->n_bds_used = 0;
69462306a36Sopenharmony_ci	return actual_len;
69562306a36Sopenharmony_ci}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci/**
69862306a36Sopenharmony_ci * iudma_reset_channel - Stop DMA on a single channel.
69962306a36Sopenharmony_ci * @udc: Reference to the device controller.
70062306a36Sopenharmony_ci * @iudma: IUDMA channel to reset.
70162306a36Sopenharmony_ci */
70262306a36Sopenharmony_cistatic void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	int timeout = IUDMA_RESET_TIMEOUT_US;
70562306a36Sopenharmony_ci	struct bcm_enet_desc *d;
70662306a36Sopenharmony_ci	int ch_idx = iudma->ch_idx;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (!iudma->is_tx)
70962306a36Sopenharmony_ci		bcm63xx_fifo_reset_ep(udc, max(0, iudma->ep_num));
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	/* stop DMA, then wait for the hardware to wrap up */
71262306a36Sopenharmony_ci	usb_dmac_writel(udc, 0, ENETDMAC_CHANCFG_REG, ch_idx);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	while (usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG, ch_idx) &
71562306a36Sopenharmony_ci				   ENETDMAC_CHANCFG_EN_MASK) {
71662306a36Sopenharmony_ci		udelay(1);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		/* repeatedly flush the FIFO data until the BD completes */
71962306a36Sopenharmony_ci		if (iudma->is_tx && iudma->ep_num >= 0)
72062306a36Sopenharmony_ci			bcm63xx_fifo_reset_ep(udc, iudma->ep_num);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		if (!timeout--) {
72362306a36Sopenharmony_ci			dev_err(udc->dev, "can't reset IUDMA channel %d\n",
72462306a36Sopenharmony_ci				ch_idx);
72562306a36Sopenharmony_ci			break;
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci		if (timeout == IUDMA_RESET_TIMEOUT_US / 2) {
72862306a36Sopenharmony_ci			dev_warn(udc->dev, "forcibly halting IUDMA channel %d\n",
72962306a36Sopenharmony_ci				 ch_idx);
73062306a36Sopenharmony_ci			usb_dmac_writel(udc, ENETDMAC_CHANCFG_BUFHALT_MASK,
73162306a36Sopenharmony_ci					ENETDMAC_CHANCFG_REG, ch_idx);
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci	usb_dmac_writel(udc, ~0, ENETDMAC_IR_REG, ch_idx);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* don't leave "live" HW-owned entries for the next guy to step on */
73762306a36Sopenharmony_ci	for (d = iudma->bd_ring; d <= iudma->end_bd; d++)
73862306a36Sopenharmony_ci		d->len_stat = 0;
73962306a36Sopenharmony_ci	mb();
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	iudma->read_bd = iudma->write_bd = iudma->bd_ring;
74262306a36Sopenharmony_ci	iudma->n_bds_used = 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* set up IRQs, UBUS burst size, and BD base for this channel */
74562306a36Sopenharmony_ci	usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
74662306a36Sopenharmony_ci			ENETDMAC_IRMASK_REG, ch_idx);
74762306a36Sopenharmony_ci	usb_dmac_writel(udc, 8, ENETDMAC_MAXBURST_REG, ch_idx);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	usb_dmas_writel(udc, iudma->bd_ring_dma, ENETDMAS_RSTART_REG, ch_idx);
75062306a36Sopenharmony_ci	usb_dmas_writel(udc, 0, ENETDMAS_SRAM2_REG, ch_idx);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci/**
75462306a36Sopenharmony_ci * iudma_init_channel - One-time IUDMA channel initialization.
75562306a36Sopenharmony_ci * @udc: Reference to the device controller.
75662306a36Sopenharmony_ci * @ch_idx: Channel to initialize.
75762306a36Sopenharmony_ci */
75862306a36Sopenharmony_cistatic int iudma_init_channel(struct bcm63xx_udc *udc, unsigned int ch_idx)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct iudma_ch *iudma = &udc->iudma[ch_idx];
76162306a36Sopenharmony_ci	const struct iudma_ch_cfg *cfg = &iudma_defaults[ch_idx];
76262306a36Sopenharmony_ci	unsigned int n_bds = cfg->n_bds;
76362306a36Sopenharmony_ci	struct bcm63xx_ep *bep = NULL;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	iudma->ep_num = cfg->ep_num;
76662306a36Sopenharmony_ci	iudma->ch_idx = ch_idx;
76762306a36Sopenharmony_ci	iudma->is_tx = !!(ch_idx & 0x01);
76862306a36Sopenharmony_ci	if (iudma->ep_num >= 0) {
76962306a36Sopenharmony_ci		bep = &udc->bep[iudma->ep_num];
77062306a36Sopenharmony_ci		bep->iudma = iudma;
77162306a36Sopenharmony_ci		INIT_LIST_HEAD(&bep->queue);
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	iudma->bep = bep;
77562306a36Sopenharmony_ci	iudma->udc = udc;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* ep0 is always active; others are controlled by the gadget driver */
77862306a36Sopenharmony_ci	if (iudma->ep_num <= 0)
77962306a36Sopenharmony_ci		iudma->enabled = true;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	iudma->n_bds = n_bds;
78262306a36Sopenharmony_ci	iudma->bd_ring = dmam_alloc_coherent(udc->dev,
78362306a36Sopenharmony_ci		n_bds * sizeof(struct bcm_enet_desc),
78462306a36Sopenharmony_ci		&iudma->bd_ring_dma, GFP_KERNEL);
78562306a36Sopenharmony_ci	if (!iudma->bd_ring)
78662306a36Sopenharmony_ci		return -ENOMEM;
78762306a36Sopenharmony_ci	iudma->end_bd = &iudma->bd_ring[n_bds - 1];
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	return 0;
79062306a36Sopenharmony_ci}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci/**
79362306a36Sopenharmony_ci * iudma_init - One-time initialization of all IUDMA channels.
79462306a36Sopenharmony_ci * @udc: Reference to the device controller.
79562306a36Sopenharmony_ci *
79662306a36Sopenharmony_ci * Enable DMA, flush channels, and enable global IUDMA IRQs.
79762306a36Sopenharmony_ci */
79862306a36Sopenharmony_cistatic int iudma_init(struct bcm63xx_udc *udc)
79962306a36Sopenharmony_ci{
80062306a36Sopenharmony_ci	int i, rc;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	usb_dma_writel(udc, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
80562306a36Sopenharmony_ci		rc = iudma_init_channel(udc, i);
80662306a36Sopenharmony_ci		if (rc)
80762306a36Sopenharmony_ci			return rc;
80862306a36Sopenharmony_ci		iudma_reset_channel(udc, &udc->iudma[i]);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	usb_dma_writel(udc, BIT(BCM63XX_NUM_IUDMA)-1, ENETDMA_GLB_IRQMASK_REG);
81262306a36Sopenharmony_ci	return 0;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci/**
81662306a36Sopenharmony_ci * iudma_uninit - Uninitialize IUDMA channels.
81762306a36Sopenharmony_ci * @udc: Reference to the device controller.
81862306a36Sopenharmony_ci *
81962306a36Sopenharmony_ci * Kill global IUDMA IRQs, flush channels, and kill DMA.
82062306a36Sopenharmony_ci */
82162306a36Sopenharmony_cistatic void iudma_uninit(struct bcm63xx_udc *udc)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	int i;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	usb_dma_writel(udc, 0, ENETDMA_GLB_IRQMASK_REG);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_IUDMA; i++)
82862306a36Sopenharmony_ci		iudma_reset_channel(udc, &udc->iudma[i]);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	usb_dma_writel(udc, 0, ENETDMA_CFG_REG);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci/***********************************************************************
83462306a36Sopenharmony_ci * Other low-level USBD operations
83562306a36Sopenharmony_ci ***********************************************************************/
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci/**
83862306a36Sopenharmony_ci * bcm63xx_set_ctrl_irqs - Mask/unmask control path interrupts.
83962306a36Sopenharmony_ci * @udc: Reference to the device controller.
84062306a36Sopenharmony_ci * @enable_irqs: true to enable, false to disable.
84162306a36Sopenharmony_ci */
84262306a36Sopenharmony_cistatic void bcm63xx_set_ctrl_irqs(struct bcm63xx_udc *udc, bool enable_irqs)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	u32 val;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	usbd_writel(udc, 0, USBD_STATUS_REG);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	val = BIT(USBD_EVENT_IRQ_USB_RESET) |
84962306a36Sopenharmony_ci	      BIT(USBD_EVENT_IRQ_SETUP) |
85062306a36Sopenharmony_ci	      BIT(USBD_EVENT_IRQ_SETCFG) |
85162306a36Sopenharmony_ci	      BIT(USBD_EVENT_IRQ_SETINTF) |
85262306a36Sopenharmony_ci	      BIT(USBD_EVENT_IRQ_USB_LINK);
85362306a36Sopenharmony_ci	usbd_writel(udc, enable_irqs ? val : 0, USBD_EVENT_IRQ_MASK_REG);
85462306a36Sopenharmony_ci	usbd_writel(udc, val, USBD_EVENT_IRQ_STATUS_REG);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci/**
85862306a36Sopenharmony_ci * bcm63xx_select_phy_mode - Select between USB device and host mode.
85962306a36Sopenharmony_ci * @udc: Reference to the device controller.
86062306a36Sopenharmony_ci * @is_device: true for device, false for host.
86162306a36Sopenharmony_ci *
86262306a36Sopenharmony_ci * This should probably be reworked to use the drivers/usb/otg
86362306a36Sopenharmony_ci * infrastructure.
86462306a36Sopenharmony_ci *
86562306a36Sopenharmony_ci * By default, the AFE/pullups are disabled in device mode, until
86662306a36Sopenharmony_ci * bcm63xx_select_pullup() is called.
86762306a36Sopenharmony_ci */
86862306a36Sopenharmony_cistatic void bcm63xx_select_phy_mode(struct bcm63xx_udc *udc, bool is_device)
86962306a36Sopenharmony_ci{
87062306a36Sopenharmony_ci	u32 val, portmask = BIT(udc->pd->port_no);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	if (BCMCPU_IS_6328()) {
87362306a36Sopenharmony_ci		/* configure pinmux to sense VBUS signal */
87462306a36Sopenharmony_ci		val = bcm_gpio_readl(GPIO_PINMUX_OTHR_REG);
87562306a36Sopenharmony_ci		val &= ~GPIO_PINMUX_OTHR_6328_USB_MASK;
87662306a36Sopenharmony_ci		val |= is_device ? GPIO_PINMUX_OTHR_6328_USB_DEV :
87762306a36Sopenharmony_ci			       GPIO_PINMUX_OTHR_6328_USB_HOST;
87862306a36Sopenharmony_ci		bcm_gpio_writel(val, GPIO_PINMUX_OTHR_REG);
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
88262306a36Sopenharmony_ci	if (is_device) {
88362306a36Sopenharmony_ci		val |= (portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
88462306a36Sopenharmony_ci		val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
88562306a36Sopenharmony_ci	} else {
88662306a36Sopenharmony_ci		val &= ~(portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
88762306a36Sopenharmony_ci		val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci	bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6368_REG);
89262306a36Sopenharmony_ci	if (is_device)
89362306a36Sopenharmony_ci		val |= USBH_PRIV_SWAP_USBD_MASK;
89462306a36Sopenharmony_ci	else
89562306a36Sopenharmony_ci		val &= ~USBH_PRIV_SWAP_USBD_MASK;
89662306a36Sopenharmony_ci	bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_SWAP_6368_REG);
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/**
90062306a36Sopenharmony_ci * bcm63xx_select_pullup - Enable/disable the pullup on D+
90162306a36Sopenharmony_ci * @udc: Reference to the device controller.
90262306a36Sopenharmony_ci * @is_on: true to enable the pullup, false to disable.
90362306a36Sopenharmony_ci *
90462306a36Sopenharmony_ci * If the pullup is active, the host will sense a FS/HS device connected to
90562306a36Sopenharmony_ci * the port.  If the pullup is inactive, the host will think the USB
90662306a36Sopenharmony_ci * device has been disconnected.
90762306a36Sopenharmony_ci */
90862306a36Sopenharmony_cistatic void bcm63xx_select_pullup(struct bcm63xx_udc *udc, bool is_on)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	u32 val, portmask = BIT(udc->pd->port_no);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
91362306a36Sopenharmony_ci	if (is_on)
91462306a36Sopenharmony_ci		val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
91562306a36Sopenharmony_ci	else
91662306a36Sopenharmony_ci		val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
91762306a36Sopenharmony_ci	bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci/**
92162306a36Sopenharmony_ci * bcm63xx_uninit_udc_hw - Shut down the hardware prior to driver removal.
92262306a36Sopenharmony_ci * @udc: Reference to the device controller.
92362306a36Sopenharmony_ci *
92462306a36Sopenharmony_ci * This just masks the IUDMA IRQs and releases the clocks.  It is assumed
92562306a36Sopenharmony_ci * that bcm63xx_udc_stop() has already run, and the clocks are stopped.
92662306a36Sopenharmony_ci */
92762306a36Sopenharmony_cistatic void bcm63xx_uninit_udc_hw(struct bcm63xx_udc *udc)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	set_clocks(udc, true);
93062306a36Sopenharmony_ci	iudma_uninit(udc);
93162306a36Sopenharmony_ci	set_clocks(udc, false);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	clk_put(udc->usbd_clk);
93462306a36Sopenharmony_ci	clk_put(udc->usbh_clk);
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/**
93862306a36Sopenharmony_ci * bcm63xx_init_udc_hw - Initialize the controller hardware and data structures.
93962306a36Sopenharmony_ci * @udc: Reference to the device controller.
94062306a36Sopenharmony_ci */
94162306a36Sopenharmony_cistatic int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	int i, rc = 0;
94462306a36Sopenharmony_ci	u32 val;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	udc->ep0_ctrl_buf = devm_kzalloc(udc->dev, BCM63XX_MAX_CTRL_PKT,
94762306a36Sopenharmony_ci					 GFP_KERNEL);
94862306a36Sopenharmony_ci	if (!udc->ep0_ctrl_buf)
94962306a36Sopenharmony_ci		return -ENOMEM;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	INIT_LIST_HEAD(&udc->gadget.ep_list);
95262306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_EP; i++) {
95362306a36Sopenharmony_ci		struct bcm63xx_ep *bep = &udc->bep[i];
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci		bep->ep.name = bcm63xx_ep_info[i].name;
95662306a36Sopenharmony_ci		bep->ep.caps = bcm63xx_ep_info[i].caps;
95762306a36Sopenharmony_ci		bep->ep_num = i;
95862306a36Sopenharmony_ci		bep->ep.ops = &bcm63xx_udc_ep_ops;
95962306a36Sopenharmony_ci		list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list);
96062306a36Sopenharmony_ci		bep->halted = 0;
96162306a36Sopenharmony_ci		usb_ep_set_maxpacket_limit(&bep->ep, BCM63XX_MAX_CTRL_PKT);
96262306a36Sopenharmony_ci		bep->udc = udc;
96362306a36Sopenharmony_ci		bep->ep.desc = NULL;
96462306a36Sopenharmony_ci		INIT_LIST_HEAD(&bep->queue);
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	udc->gadget.ep0 = &udc->bep[0].ep;
96862306a36Sopenharmony_ci	list_del(&udc->bep[0].ep.ep_list);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	udc->gadget.speed = USB_SPEED_UNKNOWN;
97162306a36Sopenharmony_ci	udc->ep0state = EP0_SHUTDOWN;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	udc->usbh_clk = clk_get(udc->dev, "usbh");
97462306a36Sopenharmony_ci	if (IS_ERR(udc->usbh_clk))
97562306a36Sopenharmony_ci		return -EIO;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	udc->usbd_clk = clk_get(udc->dev, "usbd");
97862306a36Sopenharmony_ci	if (IS_ERR(udc->usbd_clk)) {
97962306a36Sopenharmony_ci		clk_put(udc->usbh_clk);
98062306a36Sopenharmony_ci		return -EIO;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	set_clocks(udc, true);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	val = USBD_CONTROL_AUTO_CSRS_MASK |
98662306a36Sopenharmony_ci	      USBD_CONTROL_DONE_CSRS_MASK |
98762306a36Sopenharmony_ci	      (irq_coalesce ? USBD_CONTROL_RXZSCFG_MASK : 0);
98862306a36Sopenharmony_ci	usbd_writel(udc, val, USBD_CONTROL_REG);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	val = USBD_STRAPS_APP_SELF_PWR_MASK |
99162306a36Sopenharmony_ci	      USBD_STRAPS_APP_RAM_IF_MASK |
99262306a36Sopenharmony_ci	      USBD_STRAPS_APP_CSRPRGSUP_MASK |
99362306a36Sopenharmony_ci	      USBD_STRAPS_APP_8BITPHY_MASK |
99462306a36Sopenharmony_ci	      USBD_STRAPS_APP_RMTWKUP_MASK;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (udc->gadget.max_speed == USB_SPEED_HIGH)
99762306a36Sopenharmony_ci		val |= (BCM63XX_SPD_HIGH << USBD_STRAPS_SPEED_SHIFT);
99862306a36Sopenharmony_ci	else
99962306a36Sopenharmony_ci		val |= (BCM63XX_SPD_FULL << USBD_STRAPS_SPEED_SHIFT);
100062306a36Sopenharmony_ci	usbd_writel(udc, val, USBD_STRAPS_REG);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	bcm63xx_set_ctrl_irqs(udc, false);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	usbd_writel(udc, 0, USBD_EVENT_IRQ_CFG_LO_REG);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	val = USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_ENUM_ON) |
100762306a36Sopenharmony_ci	      USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_SET_CSRS);
100862306a36Sopenharmony_ci	usbd_writel(udc, val, USBD_EVENT_IRQ_CFG_HI_REG);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	rc = iudma_init(udc);
101162306a36Sopenharmony_ci	set_clocks(udc, false);
101262306a36Sopenharmony_ci	if (rc)
101362306a36Sopenharmony_ci		bcm63xx_uninit_udc_hw(udc);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	return 0;
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci/***********************************************************************
101962306a36Sopenharmony_ci * Standard EP gadget operations
102062306a36Sopenharmony_ci ***********************************************************************/
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci/**
102362306a36Sopenharmony_ci * bcm63xx_ep_enable - Enable one endpoint.
102462306a36Sopenharmony_ci * @ep: Endpoint to enable.
102562306a36Sopenharmony_ci * @desc: Contains max packet, direction, etc.
102662306a36Sopenharmony_ci *
102762306a36Sopenharmony_ci * Most of the endpoint parameters are fixed in this controller, so there
102862306a36Sopenharmony_ci * isn't much for this function to do.
102962306a36Sopenharmony_ci */
103062306a36Sopenharmony_cistatic int bcm63xx_ep_enable(struct usb_ep *ep,
103162306a36Sopenharmony_ci	const struct usb_endpoint_descriptor *desc)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct bcm63xx_ep *bep = our_ep(ep);
103462306a36Sopenharmony_ci	struct bcm63xx_udc *udc = bep->udc;
103562306a36Sopenharmony_ci	struct iudma_ch *iudma = bep->iudma;
103662306a36Sopenharmony_ci	unsigned long flags;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (!ep || !desc || ep->name == bcm63xx_ep0name)
103962306a36Sopenharmony_ci		return -EINVAL;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (!udc->driver)
104262306a36Sopenharmony_ci		return -ESHUTDOWN;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
104562306a36Sopenharmony_ci	if (iudma->enabled) {
104662306a36Sopenharmony_ci		spin_unlock_irqrestore(&udc->lock, flags);
104762306a36Sopenharmony_ci		return -EINVAL;
104862306a36Sopenharmony_ci	}
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	iudma->enabled = true;
105162306a36Sopenharmony_ci	BUG_ON(!list_empty(&bep->queue));
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	iudma_reset_channel(udc, iudma);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	bep->halted = 0;
105662306a36Sopenharmony_ci	bcm63xx_set_stall(udc, bep, false);
105762306a36Sopenharmony_ci	clear_bit(bep->ep_num, &udc->wedgemap);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	ep->desc = desc;
106062306a36Sopenharmony_ci	ep->maxpacket = usb_endpoint_maxp(desc);
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
106362306a36Sopenharmony_ci	return 0;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci/**
106762306a36Sopenharmony_ci * bcm63xx_ep_disable - Disable one endpoint.
106862306a36Sopenharmony_ci * @ep: Endpoint to disable.
106962306a36Sopenharmony_ci */
107062306a36Sopenharmony_cistatic int bcm63xx_ep_disable(struct usb_ep *ep)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	struct bcm63xx_ep *bep = our_ep(ep);
107362306a36Sopenharmony_ci	struct bcm63xx_udc *udc = bep->udc;
107462306a36Sopenharmony_ci	struct iudma_ch *iudma = bep->iudma;
107562306a36Sopenharmony_ci	struct bcm63xx_req *breq, *n;
107662306a36Sopenharmony_ci	unsigned long flags;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (!ep || !ep->desc)
107962306a36Sopenharmony_ci		return -EINVAL;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
108262306a36Sopenharmony_ci	if (!iudma->enabled) {
108362306a36Sopenharmony_ci		spin_unlock_irqrestore(&udc->lock, flags);
108462306a36Sopenharmony_ci		return -EINVAL;
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci	iudma->enabled = false;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	iudma_reset_channel(udc, iudma);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if (!list_empty(&bep->queue)) {
109162306a36Sopenharmony_ci		list_for_each_entry_safe(breq, n, &bep->queue, queue) {
109262306a36Sopenharmony_ci			usb_gadget_unmap_request(&udc->gadget, &breq->req,
109362306a36Sopenharmony_ci						 iudma->is_tx);
109462306a36Sopenharmony_ci			list_del(&breq->queue);
109562306a36Sopenharmony_ci			breq->req.status = -ESHUTDOWN;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci			spin_unlock_irqrestore(&udc->lock, flags);
109862306a36Sopenharmony_ci			usb_gadget_giveback_request(&iudma->bep->ep, &breq->req);
109962306a36Sopenharmony_ci			spin_lock_irqsave(&udc->lock, flags);
110062306a36Sopenharmony_ci		}
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci	ep->desc = NULL;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
110562306a36Sopenharmony_ci	return 0;
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci/**
110962306a36Sopenharmony_ci * bcm63xx_udc_alloc_request - Allocate a new request.
111062306a36Sopenharmony_ci * @ep: Endpoint associated with the request.
111162306a36Sopenharmony_ci * @mem_flags: Flags to pass to kzalloc().
111262306a36Sopenharmony_ci */
111362306a36Sopenharmony_cistatic struct usb_request *bcm63xx_udc_alloc_request(struct usb_ep *ep,
111462306a36Sopenharmony_ci	gfp_t mem_flags)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct bcm63xx_req *breq;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	breq = kzalloc(sizeof(*breq), mem_flags);
111962306a36Sopenharmony_ci	if (!breq)
112062306a36Sopenharmony_ci		return NULL;
112162306a36Sopenharmony_ci	return &breq->req;
112262306a36Sopenharmony_ci}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci/**
112562306a36Sopenharmony_ci * bcm63xx_udc_free_request - Free a request.
112662306a36Sopenharmony_ci * @ep: Endpoint associated with the request.
112762306a36Sopenharmony_ci * @req: Request to free.
112862306a36Sopenharmony_ci */
112962306a36Sopenharmony_cistatic void bcm63xx_udc_free_request(struct usb_ep *ep,
113062306a36Sopenharmony_ci	struct usb_request *req)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct bcm63xx_req *breq = our_req(req);
113362306a36Sopenharmony_ci	kfree(breq);
113462306a36Sopenharmony_ci}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci/**
113762306a36Sopenharmony_ci * bcm63xx_udc_queue - Queue up a new request.
113862306a36Sopenharmony_ci * @ep: Endpoint associated with the request.
113962306a36Sopenharmony_ci * @req: Request to add.
114062306a36Sopenharmony_ci * @mem_flags: Unused.
114162306a36Sopenharmony_ci *
114262306a36Sopenharmony_ci * If the queue is empty, start this request immediately.  Otherwise, add
114362306a36Sopenharmony_ci * it to the list.
114462306a36Sopenharmony_ci *
114562306a36Sopenharmony_ci * ep0 replies are sent through this function from the gadget driver, but
114662306a36Sopenharmony_ci * they are treated differently because they need to be handled by the ep0
114762306a36Sopenharmony_ci * state machine.  (Sometimes they are replies to control requests that
114862306a36Sopenharmony_ci * were spoofed by this driver, and so they shouldn't be transmitted at all.)
114962306a36Sopenharmony_ci */
115062306a36Sopenharmony_cistatic int bcm63xx_udc_queue(struct usb_ep *ep, struct usb_request *req,
115162306a36Sopenharmony_ci	gfp_t mem_flags)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	struct bcm63xx_ep *bep = our_ep(ep);
115462306a36Sopenharmony_ci	struct bcm63xx_udc *udc = bep->udc;
115562306a36Sopenharmony_ci	struct bcm63xx_req *breq = our_req(req);
115662306a36Sopenharmony_ci	unsigned long flags;
115762306a36Sopenharmony_ci	int rc = 0;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	if (unlikely(!req || !req->complete || !req->buf || !ep))
116062306a36Sopenharmony_ci		return -EINVAL;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	req->actual = 0;
116362306a36Sopenharmony_ci	req->status = 0;
116462306a36Sopenharmony_ci	breq->offset = 0;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	if (bep == &udc->bep[0]) {
116762306a36Sopenharmony_ci		/* only one reply per request, please */
116862306a36Sopenharmony_ci		if (udc->ep0_reply)
116962306a36Sopenharmony_ci			return -EINVAL;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		udc->ep0_reply = req;
117262306a36Sopenharmony_ci		schedule_work(&udc->ep0_wq);
117362306a36Sopenharmony_ci		return 0;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
117762306a36Sopenharmony_ci	if (!bep->iudma->enabled) {
117862306a36Sopenharmony_ci		rc = -ESHUTDOWN;
117962306a36Sopenharmony_ci		goto out;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	rc = usb_gadget_map_request(&udc->gadget, req, bep->iudma->is_tx);
118362306a36Sopenharmony_ci	if (rc == 0) {
118462306a36Sopenharmony_ci		list_add_tail(&breq->queue, &bep->queue);
118562306a36Sopenharmony_ci		if (list_is_singular(&bep->queue))
118662306a36Sopenharmony_ci			iudma_write(udc, bep->iudma, breq);
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ciout:
119062306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
119162306a36Sopenharmony_ci	return rc;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci/**
119562306a36Sopenharmony_ci * bcm63xx_udc_dequeue - Remove a pending request from the queue.
119662306a36Sopenharmony_ci * @ep: Endpoint associated with the request.
119762306a36Sopenharmony_ci * @req: Request to remove.
119862306a36Sopenharmony_ci *
119962306a36Sopenharmony_ci * If the request is not at the head of the queue, this is easy - just nuke
120062306a36Sopenharmony_ci * it.  If the request is at the head of the queue, we'll need to stop the
120162306a36Sopenharmony_ci * DMA transaction and then queue up the successor.
120262306a36Sopenharmony_ci */
120362306a36Sopenharmony_cistatic int bcm63xx_udc_dequeue(struct usb_ep *ep, struct usb_request *req)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	struct bcm63xx_ep *bep = our_ep(ep);
120662306a36Sopenharmony_ci	struct bcm63xx_udc *udc = bep->udc;
120762306a36Sopenharmony_ci	struct bcm63xx_req *breq = our_req(req), *cur;
120862306a36Sopenharmony_ci	unsigned long flags;
120962306a36Sopenharmony_ci	int rc = 0;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
121262306a36Sopenharmony_ci	if (list_empty(&bep->queue)) {
121362306a36Sopenharmony_ci		rc = -EINVAL;
121462306a36Sopenharmony_ci		goto out;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	cur = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
121862306a36Sopenharmony_ci	usb_gadget_unmap_request(&udc->gadget, &breq->req, bep->iudma->is_tx);
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	if (breq == cur) {
122162306a36Sopenharmony_ci		iudma_reset_channel(udc, bep->iudma);
122262306a36Sopenharmony_ci		list_del(&breq->queue);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		if (!list_empty(&bep->queue)) {
122562306a36Sopenharmony_ci			struct bcm63xx_req *next;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci			next = list_first_entry(&bep->queue,
122862306a36Sopenharmony_ci				struct bcm63xx_req, queue);
122962306a36Sopenharmony_ci			iudma_write(udc, bep->iudma, next);
123062306a36Sopenharmony_ci		}
123162306a36Sopenharmony_ci	} else {
123262306a36Sopenharmony_ci		list_del(&breq->queue);
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ciout:
123662306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	req->status = -ESHUTDOWN;
123962306a36Sopenharmony_ci	req->complete(ep, req);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return rc;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci/**
124562306a36Sopenharmony_ci * bcm63xx_udc_set_halt - Enable/disable STALL flag in the hardware.
124662306a36Sopenharmony_ci * @ep: Endpoint to halt.
124762306a36Sopenharmony_ci * @value: Zero to clear halt; nonzero to set halt.
124862306a36Sopenharmony_ci *
124962306a36Sopenharmony_ci * See comments in bcm63xx_update_wedge().
125062306a36Sopenharmony_ci */
125162306a36Sopenharmony_cistatic int bcm63xx_udc_set_halt(struct usb_ep *ep, int value)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	struct bcm63xx_ep *bep = our_ep(ep);
125462306a36Sopenharmony_ci	struct bcm63xx_udc *udc = bep->udc;
125562306a36Sopenharmony_ci	unsigned long flags;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
125862306a36Sopenharmony_ci	bcm63xx_set_stall(udc, bep, !!value);
125962306a36Sopenharmony_ci	bep->halted = value;
126062306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return 0;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci/**
126662306a36Sopenharmony_ci * bcm63xx_udc_set_wedge - Stall the endpoint until the next reset.
126762306a36Sopenharmony_ci * @ep: Endpoint to wedge.
126862306a36Sopenharmony_ci *
126962306a36Sopenharmony_ci * See comments in bcm63xx_update_wedge().
127062306a36Sopenharmony_ci */
127162306a36Sopenharmony_cistatic int bcm63xx_udc_set_wedge(struct usb_ep *ep)
127262306a36Sopenharmony_ci{
127362306a36Sopenharmony_ci	struct bcm63xx_ep *bep = our_ep(ep);
127462306a36Sopenharmony_ci	struct bcm63xx_udc *udc = bep->udc;
127562306a36Sopenharmony_ci	unsigned long flags;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
127862306a36Sopenharmony_ci	set_bit(bep->ep_num, &udc->wedgemap);
127962306a36Sopenharmony_ci	bcm63xx_set_stall(udc, bep, true);
128062306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	return 0;
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_cistatic const struct usb_ep_ops bcm63xx_udc_ep_ops = {
128662306a36Sopenharmony_ci	.enable		= bcm63xx_ep_enable,
128762306a36Sopenharmony_ci	.disable	= bcm63xx_ep_disable,
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	.alloc_request	= bcm63xx_udc_alloc_request,
129062306a36Sopenharmony_ci	.free_request	= bcm63xx_udc_free_request,
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	.queue		= bcm63xx_udc_queue,
129362306a36Sopenharmony_ci	.dequeue	= bcm63xx_udc_dequeue,
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	.set_halt	= bcm63xx_udc_set_halt,
129662306a36Sopenharmony_ci	.set_wedge	= bcm63xx_udc_set_wedge,
129762306a36Sopenharmony_ci};
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci/***********************************************************************
130062306a36Sopenharmony_ci * EP0 handling
130162306a36Sopenharmony_ci ***********************************************************************/
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci/**
130462306a36Sopenharmony_ci * bcm63xx_ep0_setup_callback - Drop spinlock to invoke ->setup callback.
130562306a36Sopenharmony_ci * @udc: Reference to the device controller.
130662306a36Sopenharmony_ci * @ctrl: 8-byte SETUP request.
130762306a36Sopenharmony_ci */
130862306a36Sopenharmony_cistatic int bcm63xx_ep0_setup_callback(struct bcm63xx_udc *udc,
130962306a36Sopenharmony_ci	struct usb_ctrlrequest *ctrl)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	int rc;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	spin_unlock_irq(&udc->lock);
131462306a36Sopenharmony_ci	rc = udc->driver->setup(&udc->gadget, ctrl);
131562306a36Sopenharmony_ci	spin_lock_irq(&udc->lock);
131662306a36Sopenharmony_ci	return rc;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci/**
132062306a36Sopenharmony_ci * bcm63xx_ep0_spoof_set_cfg - Synthesize a SET_CONFIGURATION request.
132162306a36Sopenharmony_ci * @udc: Reference to the device controller.
132262306a36Sopenharmony_ci *
132362306a36Sopenharmony_ci * Many standard requests are handled automatically in the hardware, but
132462306a36Sopenharmony_ci * we still need to pass them to the gadget driver so that it can
132562306a36Sopenharmony_ci * reconfigure the interfaces/endpoints if necessary.
132662306a36Sopenharmony_ci *
132762306a36Sopenharmony_ci * Unfortunately we are not able to send a STALL response if the host
132862306a36Sopenharmony_ci * requests an invalid configuration.  If this happens, we'll have to be
132962306a36Sopenharmony_ci * content with printing a warning.
133062306a36Sopenharmony_ci */
133162306a36Sopenharmony_cistatic int bcm63xx_ep0_spoof_set_cfg(struct bcm63xx_udc *udc)
133262306a36Sopenharmony_ci{
133362306a36Sopenharmony_ci	struct usb_ctrlrequest ctrl;
133462306a36Sopenharmony_ci	int rc;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_DEVICE;
133762306a36Sopenharmony_ci	ctrl.bRequest = USB_REQ_SET_CONFIGURATION;
133862306a36Sopenharmony_ci	ctrl.wValue = cpu_to_le16(udc->cfg);
133962306a36Sopenharmony_ci	ctrl.wIndex = 0;
134062306a36Sopenharmony_ci	ctrl.wLength = 0;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
134362306a36Sopenharmony_ci	if (rc < 0) {
134462306a36Sopenharmony_ci		dev_warn_ratelimited(udc->dev,
134562306a36Sopenharmony_ci			"hardware auto-acked bad SET_CONFIGURATION(%d) request\n",
134662306a36Sopenharmony_ci			udc->cfg);
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci	return rc;
134962306a36Sopenharmony_ci}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci/**
135262306a36Sopenharmony_ci * bcm63xx_ep0_spoof_set_iface - Synthesize a SET_INTERFACE request.
135362306a36Sopenharmony_ci * @udc: Reference to the device controller.
135462306a36Sopenharmony_ci */
135562306a36Sopenharmony_cistatic int bcm63xx_ep0_spoof_set_iface(struct bcm63xx_udc *udc)
135662306a36Sopenharmony_ci{
135762306a36Sopenharmony_ci	struct usb_ctrlrequest ctrl;
135862306a36Sopenharmony_ci	int rc;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_INTERFACE;
136162306a36Sopenharmony_ci	ctrl.bRequest = USB_REQ_SET_INTERFACE;
136262306a36Sopenharmony_ci	ctrl.wValue = cpu_to_le16(udc->alt_iface);
136362306a36Sopenharmony_ci	ctrl.wIndex = cpu_to_le16(udc->iface);
136462306a36Sopenharmony_ci	ctrl.wLength = 0;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
136762306a36Sopenharmony_ci	if (rc < 0) {
136862306a36Sopenharmony_ci		dev_warn_ratelimited(udc->dev,
136962306a36Sopenharmony_ci			"hardware auto-acked bad SET_INTERFACE(%d,%d) request\n",
137062306a36Sopenharmony_ci			udc->iface, udc->alt_iface);
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci	return rc;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci/**
137662306a36Sopenharmony_ci * bcm63xx_ep0_map_write - dma_map and iudma_write a single request.
137762306a36Sopenharmony_ci * @udc: Reference to the device controller.
137862306a36Sopenharmony_ci * @ch_idx: IUDMA channel number.
137962306a36Sopenharmony_ci * @req: USB gadget layer representation of the request.
138062306a36Sopenharmony_ci */
138162306a36Sopenharmony_cistatic void bcm63xx_ep0_map_write(struct bcm63xx_udc *udc, int ch_idx,
138262306a36Sopenharmony_ci	struct usb_request *req)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	struct bcm63xx_req *breq = our_req(req);
138562306a36Sopenharmony_ci	struct iudma_ch *iudma = &udc->iudma[ch_idx];
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	BUG_ON(udc->ep0_request);
138862306a36Sopenharmony_ci	udc->ep0_request = req;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	req->actual = 0;
139162306a36Sopenharmony_ci	breq->offset = 0;
139262306a36Sopenharmony_ci	usb_gadget_map_request(&udc->gadget, req, iudma->is_tx);
139362306a36Sopenharmony_ci	iudma_write(udc, iudma, breq);
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci/**
139762306a36Sopenharmony_ci * bcm63xx_ep0_complete - Set completion status and "stage" the callback.
139862306a36Sopenharmony_ci * @udc: Reference to the device controller.
139962306a36Sopenharmony_ci * @req: USB gadget layer representation of the request.
140062306a36Sopenharmony_ci * @status: Status to return to the gadget driver.
140162306a36Sopenharmony_ci */
140262306a36Sopenharmony_cistatic void bcm63xx_ep0_complete(struct bcm63xx_udc *udc,
140362306a36Sopenharmony_ci	struct usb_request *req, int status)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	req->status = status;
140662306a36Sopenharmony_ci	if (status)
140762306a36Sopenharmony_ci		req->actual = 0;
140862306a36Sopenharmony_ci	if (req->complete) {
140962306a36Sopenharmony_ci		spin_unlock_irq(&udc->lock);
141062306a36Sopenharmony_ci		req->complete(&udc->bep[0].ep, req);
141162306a36Sopenharmony_ci		spin_lock_irq(&udc->lock);
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci/**
141662306a36Sopenharmony_ci * bcm63xx_ep0_nuke_reply - Abort request from the gadget driver due to
141762306a36Sopenharmony_ci *   reset/shutdown.
141862306a36Sopenharmony_ci * @udc: Reference to the device controller.
141962306a36Sopenharmony_ci * @is_tx: Nonzero for TX (IN), zero for RX (OUT).
142062306a36Sopenharmony_ci */
142162306a36Sopenharmony_cistatic void bcm63xx_ep0_nuke_reply(struct bcm63xx_udc *udc, int is_tx)
142262306a36Sopenharmony_ci{
142362306a36Sopenharmony_ci	struct usb_request *req = udc->ep0_reply;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	udc->ep0_reply = NULL;
142662306a36Sopenharmony_ci	usb_gadget_unmap_request(&udc->gadget, req, is_tx);
142762306a36Sopenharmony_ci	if (udc->ep0_request == req) {
142862306a36Sopenharmony_ci		udc->ep0_req_completed = 0;
142962306a36Sopenharmony_ci		udc->ep0_request = NULL;
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci	bcm63xx_ep0_complete(udc, req, -ESHUTDOWN);
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci/**
143562306a36Sopenharmony_ci * bcm63xx_ep0_read_complete - Close out the pending ep0 request; return
143662306a36Sopenharmony_ci *   transfer len.
143762306a36Sopenharmony_ci * @udc: Reference to the device controller.
143862306a36Sopenharmony_ci */
143962306a36Sopenharmony_cistatic int bcm63xx_ep0_read_complete(struct bcm63xx_udc *udc)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	struct usb_request *req = udc->ep0_request;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	udc->ep0_req_completed = 0;
144462306a36Sopenharmony_ci	udc->ep0_request = NULL;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	return req->actual;
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci/**
145062306a36Sopenharmony_ci * bcm63xx_ep0_internal_request - Helper function to submit an ep0 request.
145162306a36Sopenharmony_ci * @udc: Reference to the device controller.
145262306a36Sopenharmony_ci * @ch_idx: IUDMA channel number.
145362306a36Sopenharmony_ci * @length: Number of bytes to TX/RX.
145462306a36Sopenharmony_ci *
145562306a36Sopenharmony_ci * Used for simple transfers performed by the ep0 worker.  This will always
145662306a36Sopenharmony_ci * use ep0_ctrl_req / ep0_ctrl_buf.
145762306a36Sopenharmony_ci */
145862306a36Sopenharmony_cistatic void bcm63xx_ep0_internal_request(struct bcm63xx_udc *udc, int ch_idx,
145962306a36Sopenharmony_ci	int length)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	struct usb_request *req = &udc->ep0_ctrl_req.req;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	req->buf = udc->ep0_ctrl_buf;
146462306a36Sopenharmony_ci	req->length = length;
146562306a36Sopenharmony_ci	req->complete = NULL;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	bcm63xx_ep0_map_write(udc, ch_idx, req);
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci/**
147162306a36Sopenharmony_ci * bcm63xx_ep0_do_setup - Parse new SETUP packet and decide how to handle it.
147262306a36Sopenharmony_ci * @udc: Reference to the device controller.
147362306a36Sopenharmony_ci *
147462306a36Sopenharmony_ci * EP0_IDLE probably shouldn't ever happen.  EP0_REQUEUE means we're ready
147562306a36Sopenharmony_ci * for the next packet.  Anything else means the transaction requires multiple
147662306a36Sopenharmony_ci * stages of handling.
147762306a36Sopenharmony_ci */
147862306a36Sopenharmony_cistatic enum bcm63xx_ep0_state bcm63xx_ep0_do_setup(struct bcm63xx_udc *udc)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	int rc;
148162306a36Sopenharmony_ci	struct usb_ctrlrequest *ctrl = (void *)udc->ep0_ctrl_buf;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	rc = bcm63xx_ep0_read_complete(udc);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	if (rc < 0) {
148662306a36Sopenharmony_ci		dev_err(udc->dev, "missing SETUP packet\n");
148762306a36Sopenharmony_ci		return EP0_IDLE;
148862306a36Sopenharmony_ci	}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	/*
149162306a36Sopenharmony_ci	 * Handle 0-byte IN STATUS acknowledgement.  The hardware doesn't
149262306a36Sopenharmony_ci	 * ALWAYS deliver these 100% of the time, so if we happen to see one,
149362306a36Sopenharmony_ci	 * just throw it away.
149462306a36Sopenharmony_ci	 */
149562306a36Sopenharmony_ci	if (rc == 0)
149662306a36Sopenharmony_ci		return EP0_REQUEUE;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	/* Drop malformed SETUP packets */
149962306a36Sopenharmony_ci	if (rc != sizeof(*ctrl)) {
150062306a36Sopenharmony_ci		dev_warn_ratelimited(udc->dev,
150162306a36Sopenharmony_ci			"malformed SETUP packet (%d bytes)\n", rc);
150262306a36Sopenharmony_ci		return EP0_REQUEUE;
150362306a36Sopenharmony_ci	}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	/* Process new SETUP packet arriving on ep0 */
150662306a36Sopenharmony_ci	rc = bcm63xx_ep0_setup_callback(udc, ctrl);
150762306a36Sopenharmony_ci	if (rc < 0) {
150862306a36Sopenharmony_ci		bcm63xx_set_stall(udc, &udc->bep[0], true);
150962306a36Sopenharmony_ci		return EP0_REQUEUE;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (!ctrl->wLength)
151362306a36Sopenharmony_ci		return EP0_REQUEUE;
151462306a36Sopenharmony_ci	else if (ctrl->bRequestType & USB_DIR_IN)
151562306a36Sopenharmony_ci		return EP0_IN_DATA_PHASE_SETUP;
151662306a36Sopenharmony_ci	else
151762306a36Sopenharmony_ci		return EP0_OUT_DATA_PHASE_SETUP;
151862306a36Sopenharmony_ci}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci/**
152162306a36Sopenharmony_ci * bcm63xx_ep0_do_idle - Check for outstanding requests if ep0 is idle.
152262306a36Sopenharmony_ci * @udc: Reference to the device controller.
152362306a36Sopenharmony_ci *
152462306a36Sopenharmony_ci * In state EP0_IDLE, the RX descriptor is either pending, or has been
152562306a36Sopenharmony_ci * filled with a SETUP packet from the host.  This function handles new
152662306a36Sopenharmony_ci * SETUP packets, control IRQ events (which can generate fake SETUP packets),
152762306a36Sopenharmony_ci * and reset/shutdown events.
152862306a36Sopenharmony_ci *
152962306a36Sopenharmony_ci * Returns 0 if work was done; -EAGAIN if nothing to do.
153062306a36Sopenharmony_ci */
153162306a36Sopenharmony_cistatic int bcm63xx_ep0_do_idle(struct bcm63xx_udc *udc)
153262306a36Sopenharmony_ci{
153362306a36Sopenharmony_ci	if (udc->ep0_req_reset) {
153462306a36Sopenharmony_ci		udc->ep0_req_reset = 0;
153562306a36Sopenharmony_ci	} else if (udc->ep0_req_set_cfg) {
153662306a36Sopenharmony_ci		udc->ep0_req_set_cfg = 0;
153762306a36Sopenharmony_ci		if (bcm63xx_ep0_spoof_set_cfg(udc) >= 0)
153862306a36Sopenharmony_ci			udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
153962306a36Sopenharmony_ci	} else if (udc->ep0_req_set_iface) {
154062306a36Sopenharmony_ci		udc->ep0_req_set_iface = 0;
154162306a36Sopenharmony_ci		if (bcm63xx_ep0_spoof_set_iface(udc) >= 0)
154262306a36Sopenharmony_ci			udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
154362306a36Sopenharmony_ci	} else if (udc->ep0_req_completed) {
154462306a36Sopenharmony_ci		udc->ep0state = bcm63xx_ep0_do_setup(udc);
154562306a36Sopenharmony_ci		return udc->ep0state == EP0_IDLE ? -EAGAIN : 0;
154662306a36Sopenharmony_ci	} else if (udc->ep0_req_shutdown) {
154762306a36Sopenharmony_ci		udc->ep0_req_shutdown = 0;
154862306a36Sopenharmony_ci		udc->ep0_req_completed = 0;
154962306a36Sopenharmony_ci		udc->ep0_request = NULL;
155062306a36Sopenharmony_ci		iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
155162306a36Sopenharmony_ci		usb_gadget_unmap_request(&udc->gadget,
155262306a36Sopenharmony_ci			&udc->ep0_ctrl_req.req, 0);
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ci		/* bcm63xx_udc_pullup() is waiting for this */
155562306a36Sopenharmony_ci		mb();
155662306a36Sopenharmony_ci		udc->ep0state = EP0_SHUTDOWN;
155762306a36Sopenharmony_ci	} else if (udc->ep0_reply) {
155862306a36Sopenharmony_ci		/*
155962306a36Sopenharmony_ci		 * This could happen if a USB RESET shows up during an ep0
156062306a36Sopenharmony_ci		 * transaction (especially if a laggy driver like gadgetfs
156162306a36Sopenharmony_ci		 * is in use).
156262306a36Sopenharmony_ci		 */
156362306a36Sopenharmony_ci		dev_warn(udc->dev, "nuking unexpected reply\n");
156462306a36Sopenharmony_ci		bcm63xx_ep0_nuke_reply(udc, 0);
156562306a36Sopenharmony_ci	} else {
156662306a36Sopenharmony_ci		return -EAGAIN;
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	return 0;
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci/**
157362306a36Sopenharmony_ci * bcm63xx_ep0_one_round - Handle the current ep0 state.
157462306a36Sopenharmony_ci * @udc: Reference to the device controller.
157562306a36Sopenharmony_ci *
157662306a36Sopenharmony_ci * Returns 0 if work was done; -EAGAIN if nothing to do.
157762306a36Sopenharmony_ci */
157862306a36Sopenharmony_cistatic int bcm63xx_ep0_one_round(struct bcm63xx_udc *udc)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	enum bcm63xx_ep0_state ep0state = udc->ep0state;
158162306a36Sopenharmony_ci	bool shutdown = udc->ep0_req_reset || udc->ep0_req_shutdown;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	switch (udc->ep0state) {
158462306a36Sopenharmony_ci	case EP0_REQUEUE:
158562306a36Sopenharmony_ci		/* set up descriptor to receive SETUP packet */
158662306a36Sopenharmony_ci		bcm63xx_ep0_internal_request(udc, IUDMA_EP0_RXCHAN,
158762306a36Sopenharmony_ci					     BCM63XX_MAX_CTRL_PKT);
158862306a36Sopenharmony_ci		ep0state = EP0_IDLE;
158962306a36Sopenharmony_ci		break;
159062306a36Sopenharmony_ci	case EP0_IDLE:
159162306a36Sopenharmony_ci		return bcm63xx_ep0_do_idle(udc);
159262306a36Sopenharmony_ci	case EP0_IN_DATA_PHASE_SETUP:
159362306a36Sopenharmony_ci		/*
159462306a36Sopenharmony_ci		 * Normal case: TX request is in ep0_reply (queued by the
159562306a36Sopenharmony_ci		 * callback), or will be queued shortly.  When it's here,
159662306a36Sopenharmony_ci		 * send it to the HW and go to EP0_IN_DATA_PHASE_COMPLETE.
159762306a36Sopenharmony_ci		 *
159862306a36Sopenharmony_ci		 * Shutdown case: Stop waiting for the reply.  Just
159962306a36Sopenharmony_ci		 * REQUEUE->IDLE.  The gadget driver is NOT expected to
160062306a36Sopenharmony_ci		 * queue anything else now.
160162306a36Sopenharmony_ci		 */
160262306a36Sopenharmony_ci		if (udc->ep0_reply) {
160362306a36Sopenharmony_ci			bcm63xx_ep0_map_write(udc, IUDMA_EP0_TXCHAN,
160462306a36Sopenharmony_ci					      udc->ep0_reply);
160562306a36Sopenharmony_ci			ep0state = EP0_IN_DATA_PHASE_COMPLETE;
160662306a36Sopenharmony_ci		} else if (shutdown) {
160762306a36Sopenharmony_ci			ep0state = EP0_REQUEUE;
160862306a36Sopenharmony_ci		}
160962306a36Sopenharmony_ci		break;
161062306a36Sopenharmony_ci	case EP0_IN_DATA_PHASE_COMPLETE: {
161162306a36Sopenharmony_ci		/*
161262306a36Sopenharmony_ci		 * Normal case: TX packet (ep0_reply) is in flight; wait for
161362306a36Sopenharmony_ci		 * it to finish, then go back to REQUEUE->IDLE.
161462306a36Sopenharmony_ci		 *
161562306a36Sopenharmony_ci		 * Shutdown case: Reset the TX channel, send -ESHUTDOWN
161662306a36Sopenharmony_ci		 * completion to the gadget driver, then REQUEUE->IDLE.
161762306a36Sopenharmony_ci		 */
161862306a36Sopenharmony_ci		if (udc->ep0_req_completed) {
161962306a36Sopenharmony_ci			udc->ep0_reply = NULL;
162062306a36Sopenharmony_ci			bcm63xx_ep0_read_complete(udc);
162162306a36Sopenharmony_ci			/*
162262306a36Sopenharmony_ci			 * the "ack" sometimes gets eaten (see
162362306a36Sopenharmony_ci			 * bcm63xx_ep0_do_idle)
162462306a36Sopenharmony_ci			 */
162562306a36Sopenharmony_ci			ep0state = EP0_REQUEUE;
162662306a36Sopenharmony_ci		} else if (shutdown) {
162762306a36Sopenharmony_ci			iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
162862306a36Sopenharmony_ci			bcm63xx_ep0_nuke_reply(udc, 1);
162962306a36Sopenharmony_ci			ep0state = EP0_REQUEUE;
163062306a36Sopenharmony_ci		}
163162306a36Sopenharmony_ci		break;
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci	case EP0_OUT_DATA_PHASE_SETUP:
163462306a36Sopenharmony_ci		/* Similar behavior to EP0_IN_DATA_PHASE_SETUP */
163562306a36Sopenharmony_ci		if (udc->ep0_reply) {
163662306a36Sopenharmony_ci			bcm63xx_ep0_map_write(udc, IUDMA_EP0_RXCHAN,
163762306a36Sopenharmony_ci					      udc->ep0_reply);
163862306a36Sopenharmony_ci			ep0state = EP0_OUT_DATA_PHASE_COMPLETE;
163962306a36Sopenharmony_ci		} else if (shutdown) {
164062306a36Sopenharmony_ci			ep0state = EP0_REQUEUE;
164162306a36Sopenharmony_ci		}
164262306a36Sopenharmony_ci		break;
164362306a36Sopenharmony_ci	case EP0_OUT_DATA_PHASE_COMPLETE: {
164462306a36Sopenharmony_ci		/* Similar behavior to EP0_IN_DATA_PHASE_COMPLETE */
164562306a36Sopenharmony_ci		if (udc->ep0_req_completed) {
164662306a36Sopenharmony_ci			udc->ep0_reply = NULL;
164762306a36Sopenharmony_ci			bcm63xx_ep0_read_complete(udc);
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci			/* send 0-byte ack to host */
165062306a36Sopenharmony_ci			bcm63xx_ep0_internal_request(udc, IUDMA_EP0_TXCHAN, 0);
165162306a36Sopenharmony_ci			ep0state = EP0_OUT_STATUS_PHASE;
165262306a36Sopenharmony_ci		} else if (shutdown) {
165362306a36Sopenharmony_ci			iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
165462306a36Sopenharmony_ci			bcm63xx_ep0_nuke_reply(udc, 0);
165562306a36Sopenharmony_ci			ep0state = EP0_REQUEUE;
165662306a36Sopenharmony_ci		}
165762306a36Sopenharmony_ci		break;
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci	case EP0_OUT_STATUS_PHASE:
166062306a36Sopenharmony_ci		/*
166162306a36Sopenharmony_ci		 * Normal case: 0-byte OUT ack packet is in flight; wait
166262306a36Sopenharmony_ci		 * for it to finish, then go back to REQUEUE->IDLE.
166362306a36Sopenharmony_ci		 *
166462306a36Sopenharmony_ci		 * Shutdown case: just cancel the transmission.  Don't bother
166562306a36Sopenharmony_ci		 * calling the completion, because it originated from this
166662306a36Sopenharmony_ci		 * function anyway.  Then go back to REQUEUE->IDLE.
166762306a36Sopenharmony_ci		 */
166862306a36Sopenharmony_ci		if (udc->ep0_req_completed) {
166962306a36Sopenharmony_ci			bcm63xx_ep0_read_complete(udc);
167062306a36Sopenharmony_ci			ep0state = EP0_REQUEUE;
167162306a36Sopenharmony_ci		} else if (shutdown) {
167262306a36Sopenharmony_ci			iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
167362306a36Sopenharmony_ci			udc->ep0_request = NULL;
167462306a36Sopenharmony_ci			ep0state = EP0_REQUEUE;
167562306a36Sopenharmony_ci		}
167662306a36Sopenharmony_ci		break;
167762306a36Sopenharmony_ci	case EP0_IN_FAKE_STATUS_PHASE: {
167862306a36Sopenharmony_ci		/*
167962306a36Sopenharmony_ci		 * Normal case: we spoofed a SETUP packet and are now
168062306a36Sopenharmony_ci		 * waiting for the gadget driver to send a 0-byte reply.
168162306a36Sopenharmony_ci		 * This doesn't actually get sent to the HW because the
168262306a36Sopenharmony_ci		 * HW has already sent its own reply.  Once we get the
168362306a36Sopenharmony_ci		 * response, return to IDLE.
168462306a36Sopenharmony_ci		 *
168562306a36Sopenharmony_ci		 * Shutdown case: return to IDLE immediately.
168662306a36Sopenharmony_ci		 *
168762306a36Sopenharmony_ci		 * Note that the ep0 RX descriptor has remained queued
168862306a36Sopenharmony_ci		 * (and possibly unfilled) during this entire transaction.
168962306a36Sopenharmony_ci		 * The HW datapath (IUDMA) never even sees SET_CONFIGURATION
169062306a36Sopenharmony_ci		 * or SET_INTERFACE transactions.
169162306a36Sopenharmony_ci		 */
169262306a36Sopenharmony_ci		struct usb_request *r = udc->ep0_reply;
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci		if (!r) {
169562306a36Sopenharmony_ci			if (shutdown)
169662306a36Sopenharmony_ci				ep0state = EP0_IDLE;
169762306a36Sopenharmony_ci			break;
169862306a36Sopenharmony_ci		}
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci		bcm63xx_ep0_complete(udc, r, 0);
170162306a36Sopenharmony_ci		udc->ep0_reply = NULL;
170262306a36Sopenharmony_ci		ep0state = EP0_IDLE;
170362306a36Sopenharmony_ci		break;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci	case EP0_SHUTDOWN:
170662306a36Sopenharmony_ci		break;
170762306a36Sopenharmony_ci	}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	if (udc->ep0state == ep0state)
171062306a36Sopenharmony_ci		return -EAGAIN;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	udc->ep0state = ep0state;
171362306a36Sopenharmony_ci	return 0;
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci/**
171762306a36Sopenharmony_ci * bcm63xx_ep0_process - ep0 worker thread / state machine.
171862306a36Sopenharmony_ci * @w: Workqueue struct.
171962306a36Sopenharmony_ci *
172062306a36Sopenharmony_ci * bcm63xx_ep0_process is triggered any time an event occurs on ep0.  It
172162306a36Sopenharmony_ci * is used to synchronize ep0 events and ensure that both HW and SW events
172262306a36Sopenharmony_ci * occur in a well-defined order.  When the ep0 IUDMA queues are idle, it may
172362306a36Sopenharmony_ci * synthesize SET_CONFIGURATION / SET_INTERFACE requests that were consumed
172462306a36Sopenharmony_ci * by the USBD hardware.
172562306a36Sopenharmony_ci *
172662306a36Sopenharmony_ci * The worker function will continue iterating around the state machine
172762306a36Sopenharmony_ci * until there is nothing left to do.  Usually "nothing left to do" means
172862306a36Sopenharmony_ci * that we're waiting for a new event from the hardware.
172962306a36Sopenharmony_ci */
173062306a36Sopenharmony_cistatic void bcm63xx_ep0_process(struct work_struct *w)
173162306a36Sopenharmony_ci{
173262306a36Sopenharmony_ci	struct bcm63xx_udc *udc = container_of(w, struct bcm63xx_udc, ep0_wq);
173362306a36Sopenharmony_ci	spin_lock_irq(&udc->lock);
173462306a36Sopenharmony_ci	while (bcm63xx_ep0_one_round(udc) == 0)
173562306a36Sopenharmony_ci		;
173662306a36Sopenharmony_ci	spin_unlock_irq(&udc->lock);
173762306a36Sopenharmony_ci}
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci/***********************************************************************
174062306a36Sopenharmony_ci * Standard UDC gadget operations
174162306a36Sopenharmony_ci ***********************************************************************/
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci/**
174462306a36Sopenharmony_ci * bcm63xx_udc_get_frame - Read current SOF frame number from the HW.
174562306a36Sopenharmony_ci * @gadget: USB device.
174662306a36Sopenharmony_ci */
174762306a36Sopenharmony_cistatic int bcm63xx_udc_get_frame(struct usb_gadget *gadget)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	return (usbd_readl(udc, USBD_STATUS_REG) &
175262306a36Sopenharmony_ci		USBD_STATUS_SOF_MASK) >> USBD_STATUS_SOF_SHIFT;
175362306a36Sopenharmony_ci}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci/**
175662306a36Sopenharmony_ci * bcm63xx_udc_pullup - Enable/disable pullup on D+ line.
175762306a36Sopenharmony_ci * @gadget: USB device.
175862306a36Sopenharmony_ci * @is_on: 0 to disable pullup, 1 to enable.
175962306a36Sopenharmony_ci *
176062306a36Sopenharmony_ci * See notes in bcm63xx_select_pullup().
176162306a36Sopenharmony_ci */
176262306a36Sopenharmony_cistatic int bcm63xx_udc_pullup(struct usb_gadget *gadget, int is_on)
176362306a36Sopenharmony_ci{
176462306a36Sopenharmony_ci	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
176562306a36Sopenharmony_ci	unsigned long flags;
176662306a36Sopenharmony_ci	int i, rc = -EINVAL;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
176962306a36Sopenharmony_ci	if (is_on && udc->ep0state == EP0_SHUTDOWN) {
177062306a36Sopenharmony_ci		udc->gadget.speed = USB_SPEED_UNKNOWN;
177162306a36Sopenharmony_ci		udc->ep0state = EP0_REQUEUE;
177262306a36Sopenharmony_ci		bcm63xx_fifo_setup(udc);
177362306a36Sopenharmony_ci		bcm63xx_fifo_reset(udc);
177462306a36Sopenharmony_ci		bcm63xx_ep_setup(udc);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci		bitmap_zero(&udc->wedgemap, BCM63XX_NUM_EP);
177762306a36Sopenharmony_ci		for (i = 0; i < BCM63XX_NUM_EP; i++)
177862306a36Sopenharmony_ci			bcm63xx_set_stall(udc, &udc->bep[i], false);
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci		bcm63xx_set_ctrl_irqs(udc, true);
178162306a36Sopenharmony_ci		bcm63xx_select_pullup(gadget_to_udc(gadget), true);
178262306a36Sopenharmony_ci		rc = 0;
178362306a36Sopenharmony_ci	} else if (!is_on && udc->ep0state != EP0_SHUTDOWN) {
178462306a36Sopenharmony_ci		bcm63xx_select_pullup(gadget_to_udc(gadget), false);
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci		udc->ep0_req_shutdown = 1;
178762306a36Sopenharmony_ci		spin_unlock_irqrestore(&udc->lock, flags);
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci		while (1) {
179062306a36Sopenharmony_ci			schedule_work(&udc->ep0_wq);
179162306a36Sopenharmony_ci			if (udc->ep0state == EP0_SHUTDOWN)
179262306a36Sopenharmony_ci				break;
179362306a36Sopenharmony_ci			msleep(50);
179462306a36Sopenharmony_ci		}
179562306a36Sopenharmony_ci		bcm63xx_set_ctrl_irqs(udc, false);
179662306a36Sopenharmony_ci		cancel_work_sync(&udc->ep0_wq);
179762306a36Sopenharmony_ci		return 0;
179862306a36Sopenharmony_ci	}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
180162306a36Sopenharmony_ci	return rc;
180262306a36Sopenharmony_ci}
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci/**
180562306a36Sopenharmony_ci * bcm63xx_udc_start - Start the controller.
180662306a36Sopenharmony_ci * @gadget: USB device.
180762306a36Sopenharmony_ci * @driver: Driver for USB device.
180862306a36Sopenharmony_ci */
180962306a36Sopenharmony_cistatic int bcm63xx_udc_start(struct usb_gadget *gadget,
181062306a36Sopenharmony_ci		struct usb_gadget_driver *driver)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
181362306a36Sopenharmony_ci	unsigned long flags;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	if (!driver || driver->max_speed < USB_SPEED_HIGH ||
181662306a36Sopenharmony_ci	    !driver->setup)
181762306a36Sopenharmony_ci		return -EINVAL;
181862306a36Sopenharmony_ci	if (!udc)
181962306a36Sopenharmony_ci		return -ENODEV;
182062306a36Sopenharmony_ci	if (udc->driver)
182162306a36Sopenharmony_ci		return -EBUSY;
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	set_clocks(udc, true);
182662306a36Sopenharmony_ci	bcm63xx_fifo_setup(udc);
182762306a36Sopenharmony_ci	bcm63xx_ep_init(udc);
182862306a36Sopenharmony_ci	bcm63xx_ep_setup(udc);
182962306a36Sopenharmony_ci	bcm63xx_fifo_reset(udc);
183062306a36Sopenharmony_ci	bcm63xx_select_phy_mode(udc, true);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	udc->driver = driver;
183362306a36Sopenharmony_ci	udc->gadget.dev.of_node = udc->dev->of_node;
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
183662306a36Sopenharmony_ci
183762306a36Sopenharmony_ci	return 0;
183862306a36Sopenharmony_ci}
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci/**
184162306a36Sopenharmony_ci * bcm63xx_udc_stop - Shut down the controller.
184262306a36Sopenharmony_ci * @gadget: USB device.
184362306a36Sopenharmony_ci * @driver: Driver for USB device.
184462306a36Sopenharmony_ci */
184562306a36Sopenharmony_cistatic int bcm63xx_udc_stop(struct usb_gadget *gadget)
184662306a36Sopenharmony_ci{
184762306a36Sopenharmony_ci	struct bcm63xx_udc *udc = gadget_to_udc(gadget);
184862306a36Sopenharmony_ci	unsigned long flags;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	spin_lock_irqsave(&udc->lock, flags);
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	udc->driver = NULL;
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	/*
185562306a36Sopenharmony_ci	 * If we switch the PHY too abruptly after dropping D+, the host
185662306a36Sopenharmony_ci	 * will often complain:
185762306a36Sopenharmony_ci	 *
185862306a36Sopenharmony_ci	 *     hub 1-0:1.0: port 1 disabled by hub (EMI?), re-enabling...
185962306a36Sopenharmony_ci	 */
186062306a36Sopenharmony_ci	msleep(100);
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	bcm63xx_select_phy_mode(udc, false);
186362306a36Sopenharmony_ci	set_clocks(udc, false);
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	spin_unlock_irqrestore(&udc->lock, flags);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	return 0;
186862306a36Sopenharmony_ci}
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_cistatic const struct usb_gadget_ops bcm63xx_udc_ops = {
187162306a36Sopenharmony_ci	.get_frame	= bcm63xx_udc_get_frame,
187262306a36Sopenharmony_ci	.pullup		= bcm63xx_udc_pullup,
187362306a36Sopenharmony_ci	.udc_start	= bcm63xx_udc_start,
187462306a36Sopenharmony_ci	.udc_stop	= bcm63xx_udc_stop,
187562306a36Sopenharmony_ci};
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci/***********************************************************************
187862306a36Sopenharmony_ci * IRQ handling
187962306a36Sopenharmony_ci ***********************************************************************/
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci/**
188262306a36Sopenharmony_ci * bcm63xx_update_cfg_iface - Read current configuration/interface settings.
188362306a36Sopenharmony_ci * @udc: Reference to the device controller.
188462306a36Sopenharmony_ci *
188562306a36Sopenharmony_ci * This controller intercepts SET_CONFIGURATION and SET_INTERFACE messages.
188662306a36Sopenharmony_ci * The driver never sees the raw control packets coming in on the ep0
188762306a36Sopenharmony_ci * IUDMA channel, but at least we get an interrupt event to tell us that
188862306a36Sopenharmony_ci * new values are waiting in the USBD_STATUS register.
188962306a36Sopenharmony_ci */
189062306a36Sopenharmony_cistatic void bcm63xx_update_cfg_iface(struct bcm63xx_udc *udc)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	u32 reg = usbd_readl(udc, USBD_STATUS_REG);
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	udc->cfg = (reg & USBD_STATUS_CFG_MASK) >> USBD_STATUS_CFG_SHIFT;
189562306a36Sopenharmony_ci	udc->iface = (reg & USBD_STATUS_INTF_MASK) >> USBD_STATUS_INTF_SHIFT;
189662306a36Sopenharmony_ci	udc->alt_iface = (reg & USBD_STATUS_ALTINTF_MASK) >>
189762306a36Sopenharmony_ci			 USBD_STATUS_ALTINTF_SHIFT;
189862306a36Sopenharmony_ci	bcm63xx_ep_setup(udc);
189962306a36Sopenharmony_ci}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci/**
190262306a36Sopenharmony_ci * bcm63xx_update_link_speed - Check to see if the link speed has changed.
190362306a36Sopenharmony_ci * @udc: Reference to the device controller.
190462306a36Sopenharmony_ci *
190562306a36Sopenharmony_ci * The link speed update coincides with a SETUP IRQ.  Returns 1 if the
190662306a36Sopenharmony_ci * speed has changed, so that the caller can update the endpoint settings.
190762306a36Sopenharmony_ci */
190862306a36Sopenharmony_cistatic int bcm63xx_update_link_speed(struct bcm63xx_udc *udc)
190962306a36Sopenharmony_ci{
191062306a36Sopenharmony_ci	u32 reg = usbd_readl(udc, USBD_STATUS_REG);
191162306a36Sopenharmony_ci	enum usb_device_speed oldspeed = udc->gadget.speed;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	switch ((reg & USBD_STATUS_SPD_MASK) >> USBD_STATUS_SPD_SHIFT) {
191462306a36Sopenharmony_ci	case BCM63XX_SPD_HIGH:
191562306a36Sopenharmony_ci		udc->gadget.speed = USB_SPEED_HIGH;
191662306a36Sopenharmony_ci		break;
191762306a36Sopenharmony_ci	case BCM63XX_SPD_FULL:
191862306a36Sopenharmony_ci		udc->gadget.speed = USB_SPEED_FULL;
191962306a36Sopenharmony_ci		break;
192062306a36Sopenharmony_ci	default:
192162306a36Sopenharmony_ci		/* this should never happen */
192262306a36Sopenharmony_ci		udc->gadget.speed = USB_SPEED_UNKNOWN;
192362306a36Sopenharmony_ci		dev_err(udc->dev,
192462306a36Sopenharmony_ci			"received SETUP packet with invalid link speed\n");
192562306a36Sopenharmony_ci		return 0;
192662306a36Sopenharmony_ci	}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	if (udc->gadget.speed != oldspeed) {
192962306a36Sopenharmony_ci		dev_info(udc->dev, "link up, %s-speed mode\n",
193062306a36Sopenharmony_ci			 udc->gadget.speed == USB_SPEED_HIGH ? "high" : "full");
193162306a36Sopenharmony_ci		return 1;
193262306a36Sopenharmony_ci	} else {
193362306a36Sopenharmony_ci		return 0;
193462306a36Sopenharmony_ci	}
193562306a36Sopenharmony_ci}
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci/**
193862306a36Sopenharmony_ci * bcm63xx_update_wedge - Iterate through wedged endpoints.
193962306a36Sopenharmony_ci * @udc: Reference to the device controller.
194062306a36Sopenharmony_ci * @new_status: true to "refresh" wedge status; false to clear it.
194162306a36Sopenharmony_ci *
194262306a36Sopenharmony_ci * On a SETUP interrupt, we need to manually "refresh" the wedge status
194362306a36Sopenharmony_ci * because the controller hardware is designed to automatically clear
194462306a36Sopenharmony_ci * stalls in response to a CLEAR_FEATURE request from the host.
194562306a36Sopenharmony_ci *
194662306a36Sopenharmony_ci * On a RESET interrupt, we do want to restore all wedged endpoints.
194762306a36Sopenharmony_ci */
194862306a36Sopenharmony_cistatic void bcm63xx_update_wedge(struct bcm63xx_udc *udc, bool new_status)
194962306a36Sopenharmony_ci{
195062306a36Sopenharmony_ci	int i;
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	for_each_set_bit(i, &udc->wedgemap, BCM63XX_NUM_EP) {
195362306a36Sopenharmony_ci		bcm63xx_set_stall(udc, &udc->bep[i], new_status);
195462306a36Sopenharmony_ci		if (!new_status)
195562306a36Sopenharmony_ci			clear_bit(i, &udc->wedgemap);
195662306a36Sopenharmony_ci	}
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci/**
196062306a36Sopenharmony_ci * bcm63xx_udc_ctrl_isr - ISR for control path events (USBD).
196162306a36Sopenharmony_ci * @irq: IRQ number (unused).
196262306a36Sopenharmony_ci * @dev_id: Reference to the device controller.
196362306a36Sopenharmony_ci *
196462306a36Sopenharmony_ci * This is where we handle link (VBUS) down, USB reset, speed changes,
196562306a36Sopenharmony_ci * SET_CONFIGURATION, and SET_INTERFACE events.
196662306a36Sopenharmony_ci */
196762306a36Sopenharmony_cistatic irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
196862306a36Sopenharmony_ci{
196962306a36Sopenharmony_ci	struct bcm63xx_udc *udc = dev_id;
197062306a36Sopenharmony_ci	u32 stat;
197162306a36Sopenharmony_ci	bool disconnected = false, bus_reset = false;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	stat = usbd_readl(udc, USBD_EVENT_IRQ_STATUS_REG) &
197462306a36Sopenharmony_ci	       usbd_readl(udc, USBD_EVENT_IRQ_MASK_REG);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	usbd_writel(udc, stat, USBD_EVENT_IRQ_STATUS_REG);
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	spin_lock(&udc->lock);
197962306a36Sopenharmony_ci	if (stat & BIT(USBD_EVENT_IRQ_USB_LINK)) {
198062306a36Sopenharmony_ci		/* VBUS toggled */
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci		if (!(usbd_readl(udc, USBD_EVENTS_REG) &
198362306a36Sopenharmony_ci		      USBD_EVENTS_USB_LINK_MASK) &&
198462306a36Sopenharmony_ci		      udc->gadget.speed != USB_SPEED_UNKNOWN)
198562306a36Sopenharmony_ci			dev_info(udc->dev, "link down\n");
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci		udc->gadget.speed = USB_SPEED_UNKNOWN;
198862306a36Sopenharmony_ci		disconnected = true;
198962306a36Sopenharmony_ci	}
199062306a36Sopenharmony_ci	if (stat & BIT(USBD_EVENT_IRQ_USB_RESET)) {
199162306a36Sopenharmony_ci		bcm63xx_fifo_setup(udc);
199262306a36Sopenharmony_ci		bcm63xx_fifo_reset(udc);
199362306a36Sopenharmony_ci		bcm63xx_ep_setup(udc);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci		bcm63xx_update_wedge(udc, false);
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci		udc->ep0_req_reset = 1;
199862306a36Sopenharmony_ci		schedule_work(&udc->ep0_wq);
199962306a36Sopenharmony_ci		bus_reset = true;
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci	if (stat & BIT(USBD_EVENT_IRQ_SETUP)) {
200262306a36Sopenharmony_ci		if (bcm63xx_update_link_speed(udc)) {
200362306a36Sopenharmony_ci			bcm63xx_fifo_setup(udc);
200462306a36Sopenharmony_ci			bcm63xx_ep_setup(udc);
200562306a36Sopenharmony_ci		}
200662306a36Sopenharmony_ci		bcm63xx_update_wedge(udc, true);
200762306a36Sopenharmony_ci	}
200862306a36Sopenharmony_ci	if (stat & BIT(USBD_EVENT_IRQ_SETCFG)) {
200962306a36Sopenharmony_ci		bcm63xx_update_cfg_iface(udc);
201062306a36Sopenharmony_ci		udc->ep0_req_set_cfg = 1;
201162306a36Sopenharmony_ci		schedule_work(&udc->ep0_wq);
201262306a36Sopenharmony_ci	}
201362306a36Sopenharmony_ci	if (stat & BIT(USBD_EVENT_IRQ_SETINTF)) {
201462306a36Sopenharmony_ci		bcm63xx_update_cfg_iface(udc);
201562306a36Sopenharmony_ci		udc->ep0_req_set_iface = 1;
201662306a36Sopenharmony_ci		schedule_work(&udc->ep0_wq);
201762306a36Sopenharmony_ci	}
201862306a36Sopenharmony_ci	spin_unlock(&udc->lock);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	if (disconnected && udc->driver)
202162306a36Sopenharmony_ci		udc->driver->disconnect(&udc->gadget);
202262306a36Sopenharmony_ci	else if (bus_reset && udc->driver)
202362306a36Sopenharmony_ci		usb_gadget_udc_reset(&udc->gadget, udc->driver);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	return IRQ_HANDLED;
202662306a36Sopenharmony_ci}
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci/**
202962306a36Sopenharmony_ci * bcm63xx_udc_data_isr - ISR for data path events (IUDMA).
203062306a36Sopenharmony_ci * @irq: IRQ number (unused).
203162306a36Sopenharmony_ci * @dev_id: Reference to the IUDMA channel that generated the interrupt.
203262306a36Sopenharmony_ci *
203362306a36Sopenharmony_ci * For the two ep0 channels, we have special handling that triggers the
203462306a36Sopenharmony_ci * ep0 worker thread.  For normal bulk/intr channels, either queue up
203562306a36Sopenharmony_ci * the next buffer descriptor for the transaction (incomplete transaction),
203662306a36Sopenharmony_ci * or invoke the completion callback (complete transactions).
203762306a36Sopenharmony_ci */
203862306a36Sopenharmony_cistatic irqreturn_t bcm63xx_udc_data_isr(int irq, void *dev_id)
203962306a36Sopenharmony_ci{
204062306a36Sopenharmony_ci	struct iudma_ch *iudma = dev_id;
204162306a36Sopenharmony_ci	struct bcm63xx_udc *udc = iudma->udc;
204262306a36Sopenharmony_ci	struct bcm63xx_ep *bep;
204362306a36Sopenharmony_ci	struct usb_request *req = NULL;
204462306a36Sopenharmony_ci	struct bcm63xx_req *breq = NULL;
204562306a36Sopenharmony_ci	int rc;
204662306a36Sopenharmony_ci	bool is_done = false;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	spin_lock(&udc->lock);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
205162306a36Sopenharmony_ci			ENETDMAC_IR_REG, iudma->ch_idx);
205262306a36Sopenharmony_ci	bep = iudma->bep;
205362306a36Sopenharmony_ci	rc = iudma_read(udc, iudma);
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	/* special handling for EP0 RX (0) and TX (1) */
205662306a36Sopenharmony_ci	if (iudma->ch_idx == IUDMA_EP0_RXCHAN ||
205762306a36Sopenharmony_ci	    iudma->ch_idx == IUDMA_EP0_TXCHAN) {
205862306a36Sopenharmony_ci		req = udc->ep0_request;
205962306a36Sopenharmony_ci		breq = our_req(req);
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci		/* a single request could require multiple submissions */
206262306a36Sopenharmony_ci		if (rc >= 0) {
206362306a36Sopenharmony_ci			req->actual += rc;
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci			if (req->actual >= req->length || breq->bd_bytes > rc) {
206662306a36Sopenharmony_ci				udc->ep0_req_completed = 1;
206762306a36Sopenharmony_ci				is_done = true;
206862306a36Sopenharmony_ci				schedule_work(&udc->ep0_wq);
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci				/* "actual" on a ZLP is 1 byte */
207162306a36Sopenharmony_ci				req->actual = min(req->actual, req->length);
207262306a36Sopenharmony_ci			} else {
207362306a36Sopenharmony_ci				/* queue up the next BD (same request) */
207462306a36Sopenharmony_ci				iudma_write(udc, iudma, breq);
207562306a36Sopenharmony_ci			}
207662306a36Sopenharmony_ci		}
207762306a36Sopenharmony_ci	} else if (!list_empty(&bep->queue)) {
207862306a36Sopenharmony_ci		breq = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
207962306a36Sopenharmony_ci		req = &breq->req;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci		if (rc >= 0) {
208262306a36Sopenharmony_ci			req->actual += rc;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci			if (req->actual >= req->length || breq->bd_bytes > rc) {
208562306a36Sopenharmony_ci				is_done = true;
208662306a36Sopenharmony_ci				list_del(&breq->queue);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci				req->actual = min(req->actual, req->length);
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci				if (!list_empty(&bep->queue)) {
209162306a36Sopenharmony_ci					struct bcm63xx_req *next;
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci					next = list_first_entry(&bep->queue,
209462306a36Sopenharmony_ci						struct bcm63xx_req, queue);
209562306a36Sopenharmony_ci					iudma_write(udc, iudma, next);
209662306a36Sopenharmony_ci				}
209762306a36Sopenharmony_ci			} else {
209862306a36Sopenharmony_ci				iudma_write(udc, iudma, breq);
209962306a36Sopenharmony_ci			}
210062306a36Sopenharmony_ci		}
210162306a36Sopenharmony_ci	}
210262306a36Sopenharmony_ci	spin_unlock(&udc->lock);
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	if (is_done) {
210562306a36Sopenharmony_ci		usb_gadget_unmap_request(&udc->gadget, req, iudma->is_tx);
210662306a36Sopenharmony_ci		if (req->complete)
210762306a36Sopenharmony_ci			req->complete(&bep->ep, req);
210862306a36Sopenharmony_ci	}
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	return IRQ_HANDLED;
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci/***********************************************************************
211462306a36Sopenharmony_ci * Debug filesystem
211562306a36Sopenharmony_ci ***********************************************************************/
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci/*
211862306a36Sopenharmony_ci * bcm63xx_usbd_dbg_show - Show USBD controller state.
211962306a36Sopenharmony_ci * @s: seq_file to which the information will be written.
212062306a36Sopenharmony_ci * @p: Unused.
212162306a36Sopenharmony_ci *
212262306a36Sopenharmony_ci * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/usbd
212362306a36Sopenharmony_ci */
212462306a36Sopenharmony_cistatic int bcm63xx_usbd_dbg_show(struct seq_file *s, void *p)
212562306a36Sopenharmony_ci{
212662306a36Sopenharmony_ci	struct bcm63xx_udc *udc = s->private;
212762306a36Sopenharmony_ci
212862306a36Sopenharmony_ci	if (!udc->driver)
212962306a36Sopenharmony_ci		return -ENODEV;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	seq_printf(s, "ep0 state: %s\n",
213262306a36Sopenharmony_ci		   bcm63xx_ep0_state_names[udc->ep0state]);
213362306a36Sopenharmony_ci	seq_printf(s, "  pending requests: %s%s%s%s%s%s%s\n",
213462306a36Sopenharmony_ci		   udc->ep0_req_reset ? "reset " : "",
213562306a36Sopenharmony_ci		   udc->ep0_req_set_cfg ? "set_cfg " : "",
213662306a36Sopenharmony_ci		   udc->ep0_req_set_iface ? "set_iface " : "",
213762306a36Sopenharmony_ci		   udc->ep0_req_shutdown ? "shutdown " : "",
213862306a36Sopenharmony_ci		   udc->ep0_request ? "pending " : "",
213962306a36Sopenharmony_ci		   udc->ep0_req_completed ? "completed " : "",
214062306a36Sopenharmony_ci		   udc->ep0_reply ? "reply " : "");
214162306a36Sopenharmony_ci	seq_printf(s, "cfg: %d; iface: %d; alt_iface: %d\n",
214262306a36Sopenharmony_ci		   udc->cfg, udc->iface, udc->alt_iface);
214362306a36Sopenharmony_ci	seq_printf(s, "regs:\n");
214462306a36Sopenharmony_ci	seq_printf(s, "  control: %08x; straps: %08x; status: %08x\n",
214562306a36Sopenharmony_ci		   usbd_readl(udc, USBD_CONTROL_REG),
214662306a36Sopenharmony_ci		   usbd_readl(udc, USBD_STRAPS_REG),
214762306a36Sopenharmony_ci		   usbd_readl(udc, USBD_STATUS_REG));
214862306a36Sopenharmony_ci	seq_printf(s, "  events:  %08x; stall:  %08x\n",
214962306a36Sopenharmony_ci		   usbd_readl(udc, USBD_EVENTS_REG),
215062306a36Sopenharmony_ci		   usbd_readl(udc, USBD_STALL_REG));
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	return 0;
215362306a36Sopenharmony_ci}
215462306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(bcm63xx_usbd_dbg);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci/*
215762306a36Sopenharmony_ci * bcm63xx_iudma_dbg_show - Show IUDMA status and descriptors.
215862306a36Sopenharmony_ci * @s: seq_file to which the information will be written.
215962306a36Sopenharmony_ci * @p: Unused.
216062306a36Sopenharmony_ci *
216162306a36Sopenharmony_ci * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/iudma
216262306a36Sopenharmony_ci */
216362306a36Sopenharmony_cistatic int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p)
216462306a36Sopenharmony_ci{
216562306a36Sopenharmony_ci	struct bcm63xx_udc *udc = s->private;
216662306a36Sopenharmony_ci	int ch_idx, i;
216762306a36Sopenharmony_ci	u32 sram2, sram3;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	if (!udc->driver)
217062306a36Sopenharmony_ci		return -ENODEV;
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	for (ch_idx = 0; ch_idx < BCM63XX_NUM_IUDMA; ch_idx++) {
217362306a36Sopenharmony_ci		struct iudma_ch *iudma = &udc->iudma[ch_idx];
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci		seq_printf(s, "IUDMA channel %d -- ", ch_idx);
217662306a36Sopenharmony_ci		switch (iudma_defaults[ch_idx].ep_type) {
217762306a36Sopenharmony_ci		case BCMEP_CTRL:
217862306a36Sopenharmony_ci			seq_printf(s, "control");
217962306a36Sopenharmony_ci			break;
218062306a36Sopenharmony_ci		case BCMEP_BULK:
218162306a36Sopenharmony_ci			seq_printf(s, "bulk");
218262306a36Sopenharmony_ci			break;
218362306a36Sopenharmony_ci		case BCMEP_INTR:
218462306a36Sopenharmony_ci			seq_printf(s, "interrupt");
218562306a36Sopenharmony_ci			break;
218662306a36Sopenharmony_ci		}
218762306a36Sopenharmony_ci		seq_printf(s, ch_idx & 0x01 ? " tx" : " rx");
218862306a36Sopenharmony_ci		seq_printf(s, " [ep%d]:\n",
218962306a36Sopenharmony_ci			   max_t(int, iudma_defaults[ch_idx].ep_num, 0));
219062306a36Sopenharmony_ci		seq_printf(s, "  cfg: %08x; irqstat: %08x; irqmask: %08x; maxburst: %08x\n",
219162306a36Sopenharmony_ci			   usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG, ch_idx),
219262306a36Sopenharmony_ci			   usb_dmac_readl(udc, ENETDMAC_IR_REG, ch_idx),
219362306a36Sopenharmony_ci			   usb_dmac_readl(udc, ENETDMAC_IRMASK_REG, ch_idx),
219462306a36Sopenharmony_ci			   usb_dmac_readl(udc, ENETDMAC_MAXBURST_REG, ch_idx));
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci		sram2 = usb_dmas_readl(udc, ENETDMAS_SRAM2_REG, ch_idx);
219762306a36Sopenharmony_ci		sram3 = usb_dmas_readl(udc, ENETDMAS_SRAM3_REG, ch_idx);
219862306a36Sopenharmony_ci		seq_printf(s, "  base: %08x; index: %04x_%04x; desc: %04x_%04x %08x\n",
219962306a36Sopenharmony_ci			   usb_dmas_readl(udc, ENETDMAS_RSTART_REG, ch_idx),
220062306a36Sopenharmony_ci			   sram2 >> 16, sram2 & 0xffff,
220162306a36Sopenharmony_ci			   sram3 >> 16, sram3 & 0xffff,
220262306a36Sopenharmony_ci			   usb_dmas_readl(udc, ENETDMAS_SRAM4_REG, ch_idx));
220362306a36Sopenharmony_ci		seq_printf(s, "  desc: %d/%d used", iudma->n_bds_used,
220462306a36Sopenharmony_ci			   iudma->n_bds);
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci		if (iudma->bep)
220762306a36Sopenharmony_ci			seq_printf(s, "; %zu queued\n", list_count_nodes(&iudma->bep->queue));
220862306a36Sopenharmony_ci		else
220962306a36Sopenharmony_ci			seq_printf(s, "\n");
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci		for (i = 0; i < iudma->n_bds; i++) {
221262306a36Sopenharmony_ci			struct bcm_enet_desc *d = &iudma->bd_ring[i];
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci			seq_printf(s, "  %03x (%02x): len_stat: %04x_%04x; pa %08x",
221562306a36Sopenharmony_ci				   i * sizeof(*d), i,
221662306a36Sopenharmony_ci				   d->len_stat >> 16, d->len_stat & 0xffff,
221762306a36Sopenharmony_ci				   d->address);
221862306a36Sopenharmony_ci			if (d == iudma->read_bd)
221962306a36Sopenharmony_ci				seq_printf(s, "   <<RD");
222062306a36Sopenharmony_ci			if (d == iudma->write_bd)
222162306a36Sopenharmony_ci				seq_printf(s, "   <<WR");
222262306a36Sopenharmony_ci			seq_printf(s, "\n");
222362306a36Sopenharmony_ci		}
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci		seq_printf(s, "\n");
222662306a36Sopenharmony_ci	}
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	return 0;
222962306a36Sopenharmony_ci}
223062306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(bcm63xx_iudma_dbg);
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci/**
223362306a36Sopenharmony_ci * bcm63xx_udc_init_debugfs - Create debugfs entries.
223462306a36Sopenharmony_ci * @udc: Reference to the device controller.
223562306a36Sopenharmony_ci */
223662306a36Sopenharmony_cistatic void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
223762306a36Sopenharmony_ci{
223862306a36Sopenharmony_ci	struct dentry *root;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS))
224162306a36Sopenharmony_ci		return;
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci	root = debugfs_create_dir(udc->gadget.name, usb_debug_root);
224462306a36Sopenharmony_ci	debugfs_create_file("usbd", 0400, root, udc, &bcm63xx_usbd_dbg_fops);
224562306a36Sopenharmony_ci	debugfs_create_file("iudma", 0400, root, udc, &bcm63xx_iudma_dbg_fops);
224662306a36Sopenharmony_ci}
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci/**
224962306a36Sopenharmony_ci * bcm63xx_udc_cleanup_debugfs - Remove debugfs entries.
225062306a36Sopenharmony_ci * @udc: Reference to the device controller.
225162306a36Sopenharmony_ci *
225262306a36Sopenharmony_ci * debugfs_remove() is safe to call with a NULL argument.
225362306a36Sopenharmony_ci */
225462306a36Sopenharmony_cistatic void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc)
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	debugfs_lookup_and_remove(udc->gadget.name, usb_debug_root);
225762306a36Sopenharmony_ci}
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci/***********************************************************************
226062306a36Sopenharmony_ci * Driver init/exit
226162306a36Sopenharmony_ci ***********************************************************************/
226262306a36Sopenharmony_ci
226362306a36Sopenharmony_ci/**
226462306a36Sopenharmony_ci * bcm63xx_udc_probe - Initialize a new instance of the UDC.
226562306a36Sopenharmony_ci * @pdev: Platform device struct from the bcm63xx BSP code.
226662306a36Sopenharmony_ci *
226762306a36Sopenharmony_ci * Note that platform data is required, because pd.port_no varies from chip
226862306a36Sopenharmony_ci * to chip and is used to switch the correct USB port to device mode.
226962306a36Sopenharmony_ci */
227062306a36Sopenharmony_cistatic int bcm63xx_udc_probe(struct platform_device *pdev)
227162306a36Sopenharmony_ci{
227262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
227362306a36Sopenharmony_ci	struct bcm63xx_usbd_platform_data *pd = dev_get_platdata(dev);
227462306a36Sopenharmony_ci	struct bcm63xx_udc *udc;
227562306a36Sopenharmony_ci	int rc = -ENOMEM, i, irq;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
227862306a36Sopenharmony_ci	if (!udc)
227962306a36Sopenharmony_ci		return -ENOMEM;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	platform_set_drvdata(pdev, udc);
228262306a36Sopenharmony_ci	udc->dev = dev;
228362306a36Sopenharmony_ci	udc->pd = pd;
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	if (!pd) {
228662306a36Sopenharmony_ci		dev_err(dev, "missing platform data\n");
228762306a36Sopenharmony_ci		return -EINVAL;
228862306a36Sopenharmony_ci	}
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	udc->usbd_regs = devm_platform_ioremap_resource(pdev, 0);
229162306a36Sopenharmony_ci	if (IS_ERR(udc->usbd_regs))
229262306a36Sopenharmony_ci		return PTR_ERR(udc->usbd_regs);
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	udc->iudma_regs = devm_platform_ioremap_resource(pdev, 1);
229562306a36Sopenharmony_ci	if (IS_ERR(udc->iudma_regs))
229662306a36Sopenharmony_ci		return PTR_ERR(udc->iudma_regs);
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	spin_lock_init(&udc->lock);
229962306a36Sopenharmony_ci	INIT_WORK(&udc->ep0_wq, bcm63xx_ep0_process);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	udc->gadget.ops = &bcm63xx_udc_ops;
230262306a36Sopenharmony_ci	udc->gadget.name = dev_name(dev);
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	if (!pd->use_fullspeed && !use_fullspeed)
230562306a36Sopenharmony_ci		udc->gadget.max_speed = USB_SPEED_HIGH;
230662306a36Sopenharmony_ci	else
230762306a36Sopenharmony_ci		udc->gadget.max_speed = USB_SPEED_FULL;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	/* request clocks, allocate buffers, and clear any pending IRQs */
231062306a36Sopenharmony_ci	rc = bcm63xx_init_udc_hw(udc);
231162306a36Sopenharmony_ci	if (rc)
231262306a36Sopenharmony_ci		return rc;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci	rc = -ENXIO;
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	/* IRQ resource #0: control interrupt (VBUS, speed, etc.) */
231762306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
231862306a36Sopenharmony_ci	if (irq < 0) {
231962306a36Sopenharmony_ci		rc = irq;
232062306a36Sopenharmony_ci		goto out_uninit;
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci	if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
232362306a36Sopenharmony_ci			     dev_name(dev), udc) < 0)
232462306a36Sopenharmony_ci		goto report_request_failure;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	/* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
232762306a36Sopenharmony_ci	for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
232862306a36Sopenharmony_ci		irq = platform_get_irq(pdev, i + 1);
232962306a36Sopenharmony_ci		if (irq < 0) {
233062306a36Sopenharmony_ci			rc = irq;
233162306a36Sopenharmony_ci			goto out_uninit;
233262306a36Sopenharmony_ci		}
233362306a36Sopenharmony_ci		if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
233462306a36Sopenharmony_ci				     dev_name(dev), &udc->iudma[i]) < 0)
233562306a36Sopenharmony_ci			goto report_request_failure;
233662306a36Sopenharmony_ci	}
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci	bcm63xx_udc_init_debugfs(udc);
233962306a36Sopenharmony_ci	rc = usb_add_gadget_udc(dev, &udc->gadget);
234062306a36Sopenharmony_ci	if (!rc)
234162306a36Sopenharmony_ci		return 0;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	bcm63xx_udc_cleanup_debugfs(udc);
234462306a36Sopenharmony_ciout_uninit:
234562306a36Sopenharmony_ci	bcm63xx_uninit_udc_hw(udc);
234662306a36Sopenharmony_ci	return rc;
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_cireport_request_failure:
234962306a36Sopenharmony_ci	dev_err(dev, "error requesting IRQ #%d\n", irq);
235062306a36Sopenharmony_ci	goto out_uninit;
235162306a36Sopenharmony_ci}
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci/**
235462306a36Sopenharmony_ci * bcm63xx_udc_remove - Remove the device from the system.
235562306a36Sopenharmony_ci * @pdev: Platform device struct from the bcm63xx BSP code.
235662306a36Sopenharmony_ci */
235762306a36Sopenharmony_cistatic void bcm63xx_udc_remove(struct platform_device *pdev)
235862306a36Sopenharmony_ci{
235962306a36Sopenharmony_ci	struct bcm63xx_udc *udc = platform_get_drvdata(pdev);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	bcm63xx_udc_cleanup_debugfs(udc);
236262306a36Sopenharmony_ci	usb_del_gadget_udc(&udc->gadget);
236362306a36Sopenharmony_ci	BUG_ON(udc->driver);
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	bcm63xx_uninit_udc_hw(udc);
236662306a36Sopenharmony_ci}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_cistatic struct platform_driver bcm63xx_udc_driver = {
236962306a36Sopenharmony_ci	.probe		= bcm63xx_udc_probe,
237062306a36Sopenharmony_ci	.remove_new	= bcm63xx_udc_remove,
237162306a36Sopenharmony_ci	.driver		= {
237262306a36Sopenharmony_ci		.name	= DRV_MODULE_NAME,
237362306a36Sopenharmony_ci	},
237462306a36Sopenharmony_ci};
237562306a36Sopenharmony_cimodule_platform_driver(bcm63xx_udc_driver);
237662306a36Sopenharmony_ci
237762306a36Sopenharmony_ciMODULE_DESCRIPTION("BCM63xx USB Peripheral Controller");
237862306a36Sopenharmony_ciMODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
237962306a36Sopenharmony_ciMODULE_LICENSE("GPL");
238062306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRV_MODULE_NAME);
2381