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