18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Xilinx USB peripheral controller driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 by Thomas Rathbone 68c2ecf20Sopenharmony_ci * Copyright (C) 2005 by HP Labs 78c2ecf20Sopenharmony_ci * Copyright (C) 2005 by David Brownell 88c2ecf20Sopenharmony_ci * Copyright (C) 2010 - 2014 Xilinx, Inc. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Some parts of this driver code is based on the driver for at91-series 118c2ecf20Sopenharmony_ci * USB peripheral controller (at91_udc.c). 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 238c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 248c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 258c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 268c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Register offsets for the USB device.*/ 298c2ecf20Sopenharmony_ci#define XUSB_EP0_CONFIG_OFFSET 0x0000 /* EP0 Config Reg Offset */ 308c2ecf20Sopenharmony_ci#define XUSB_SETUP_PKT_ADDR_OFFSET 0x0080 /* Setup Packet Address */ 318c2ecf20Sopenharmony_ci#define XUSB_ADDRESS_OFFSET 0x0100 /* Address Register */ 328c2ecf20Sopenharmony_ci#define XUSB_CONTROL_OFFSET 0x0104 /* Control Register */ 338c2ecf20Sopenharmony_ci#define XUSB_STATUS_OFFSET 0x0108 /* Status Register */ 348c2ecf20Sopenharmony_ci#define XUSB_FRAMENUM_OFFSET 0x010C /* Frame Number Register */ 358c2ecf20Sopenharmony_ci#define XUSB_IER_OFFSET 0x0110 /* Interrupt Enable Register */ 368c2ecf20Sopenharmony_ci#define XUSB_BUFFREADY_OFFSET 0x0114 /* Buffer Ready Register */ 378c2ecf20Sopenharmony_ci#define XUSB_TESTMODE_OFFSET 0x0118 /* Test Mode Register */ 388c2ecf20Sopenharmony_ci#define XUSB_DMA_RESET_OFFSET 0x0200 /* DMA Soft Reset Register */ 398c2ecf20Sopenharmony_ci#define XUSB_DMA_CONTROL_OFFSET 0x0204 /* DMA Control Register */ 408c2ecf20Sopenharmony_ci#define XUSB_DMA_DSAR_ADDR_OFFSET 0x0208 /* DMA source Address Reg */ 418c2ecf20Sopenharmony_ci#define XUSB_DMA_DDAR_ADDR_OFFSET 0x020C /* DMA destination Addr Reg */ 428c2ecf20Sopenharmony_ci#define XUSB_DMA_LENGTH_OFFSET 0x0210 /* DMA Length Register */ 438c2ecf20Sopenharmony_ci#define XUSB_DMA_STATUS_OFFSET 0x0214 /* DMA Status Register */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Endpoint Configuration Space offsets */ 468c2ecf20Sopenharmony_ci#define XUSB_EP_CFGSTATUS_OFFSET 0x00 /* Endpoint Config Status */ 478c2ecf20Sopenharmony_ci#define XUSB_EP_BUF0COUNT_OFFSET 0x08 /* Buffer 0 Count */ 488c2ecf20Sopenharmony_ci#define XUSB_EP_BUF1COUNT_OFFSET 0x0C /* Buffer 1 Count */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define XUSB_CONTROL_USB_READY_MASK 0x80000000 /* USB ready Mask */ 518c2ecf20Sopenharmony_ci#define XUSB_CONTROL_USB_RMTWAKE_MASK 0x40000000 /* Remote wake up mask */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Interrupt register related masks.*/ 548c2ecf20Sopenharmony_ci#define XUSB_STATUS_GLOBAL_INTR_MASK 0x80000000 /* Global Intr Enable */ 558c2ecf20Sopenharmony_ci#define XUSB_STATUS_DMADONE_MASK 0x04000000 /* DMA done Mask */ 568c2ecf20Sopenharmony_ci#define XUSB_STATUS_DMAERR_MASK 0x02000000 /* DMA Error Mask */ 578c2ecf20Sopenharmony_ci#define XUSB_STATUS_DMABUSY_MASK 0x80000000 /* DMA Error Mask */ 588c2ecf20Sopenharmony_ci#define XUSB_STATUS_RESUME_MASK 0x01000000 /* USB Resume Mask */ 598c2ecf20Sopenharmony_ci#define XUSB_STATUS_RESET_MASK 0x00800000 /* USB Reset Mask */ 608c2ecf20Sopenharmony_ci#define XUSB_STATUS_SUSPEND_MASK 0x00400000 /* USB Suspend Mask */ 618c2ecf20Sopenharmony_ci#define XUSB_STATUS_DISCONNECT_MASK 0x00200000 /* USB Disconnect Mask */ 628c2ecf20Sopenharmony_ci#define XUSB_STATUS_FIFO_BUFF_RDY_MASK 0x00100000 /* FIFO Buff Ready Mask */ 638c2ecf20Sopenharmony_ci#define XUSB_STATUS_FIFO_BUFF_FREE_MASK 0x00080000 /* FIFO Buff Free Mask */ 648c2ecf20Sopenharmony_ci#define XUSB_STATUS_SETUP_PACKET_MASK 0x00040000 /* Setup packet received */ 658c2ecf20Sopenharmony_ci#define XUSB_STATUS_EP1_BUFF2_COMP_MASK 0x00000200 /* EP 1 Buff 2 Processed */ 668c2ecf20Sopenharmony_ci#define XUSB_STATUS_EP1_BUFF1_COMP_MASK 0x00000002 /* EP 1 Buff 1 Processed */ 678c2ecf20Sopenharmony_ci#define XUSB_STATUS_EP0_BUFF2_COMP_MASK 0x00000100 /* EP 0 Buff 2 Processed */ 688c2ecf20Sopenharmony_ci#define XUSB_STATUS_EP0_BUFF1_COMP_MASK 0x00000001 /* EP 0 Buff 1 Processed */ 698c2ecf20Sopenharmony_ci#define XUSB_STATUS_HIGH_SPEED_MASK 0x00010000 /* USB Speed Mask */ 708c2ecf20Sopenharmony_ci/* Suspend,Reset,Suspend and Disconnect Mask */ 718c2ecf20Sopenharmony_ci#define XUSB_STATUS_INTR_EVENT_MASK 0x01E00000 728c2ecf20Sopenharmony_ci/* Buffers completion Mask */ 738c2ecf20Sopenharmony_ci#define XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK 0x0000FEFF 748c2ecf20Sopenharmony_ci/* Mask for buffer 0 and buffer 1 completion for all Endpoints */ 758c2ecf20Sopenharmony_ci#define XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK 0x00000101 768c2ecf20Sopenharmony_ci#define XUSB_STATUS_EP_BUFF2_SHIFT 8 /* EP buffer offset */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Endpoint Configuration Status Register */ 798c2ecf20Sopenharmony_ci#define XUSB_EP_CFG_VALID_MASK 0x80000000 /* Endpoint Valid bit */ 808c2ecf20Sopenharmony_ci#define XUSB_EP_CFG_STALL_MASK 0x40000000 /* Endpoint Stall bit */ 818c2ecf20Sopenharmony_ci#define XUSB_EP_CFG_DATA_TOGGLE_MASK 0x08000000 /* Endpoint Data toggle */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* USB device specific global configuration constants.*/ 848c2ecf20Sopenharmony_ci#define XUSB_MAX_ENDPOINTS 8 /* Maximum End Points */ 858c2ecf20Sopenharmony_ci#define XUSB_EP_NUMBER_ZERO 0 /* End point Zero */ 868c2ecf20Sopenharmony_ci/* DPRAM is the source address for DMA transfer */ 878c2ecf20Sopenharmony_ci#define XUSB_DMA_READ_FROM_DPRAM 0x80000000 888c2ecf20Sopenharmony_ci#define XUSB_DMA_DMASR_BUSY 0x80000000 /* DMA busy */ 898c2ecf20Sopenharmony_ci#define XUSB_DMA_DMASR_ERROR 0x40000000 /* DMA Error */ 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * When this bit is set, the DMA buffer ready bit is set by hardware upon 928c2ecf20Sopenharmony_ci * DMA transfer completion. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci#define XUSB_DMA_BRR_CTRL 0x40000000 /* DMA bufready ctrl bit */ 958c2ecf20Sopenharmony_ci/* Phase States */ 968c2ecf20Sopenharmony_ci#define SETUP_PHASE 0x0000 /* Setup Phase */ 978c2ecf20Sopenharmony_ci#define DATA_PHASE 0x0001 /* Data Phase */ 988c2ecf20Sopenharmony_ci#define STATUS_PHASE 0x0002 /* Status Phase */ 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define EP0_MAX_PACKET 64 /* Endpoint 0 maximum packet length */ 1018c2ecf20Sopenharmony_ci#define STATUSBUFF_SIZE 2 /* Buffer size for GET_STATUS command */ 1028c2ecf20Sopenharmony_ci#define EPNAME_SIZE 4 /* Buffer size for endpoint name */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* container_of helper macros */ 1058c2ecf20Sopenharmony_ci#define to_udc(g) container_of((g), struct xusb_udc, gadget) 1068c2ecf20Sopenharmony_ci#define to_xusb_ep(ep) container_of((ep), struct xusb_ep, ep_usb) 1078c2ecf20Sopenharmony_ci#define to_xusb_req(req) container_of((req), struct xusb_req, usb_req) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * struct xusb_req - Xilinx USB device request structure 1118c2ecf20Sopenharmony_ci * @usb_req: Linux usb request structure 1128c2ecf20Sopenharmony_ci * @queue: usb device request queue 1138c2ecf20Sopenharmony_ci * @ep: pointer to xusb_endpoint structure 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistruct xusb_req { 1168c2ecf20Sopenharmony_ci struct usb_request usb_req; 1178c2ecf20Sopenharmony_ci struct list_head queue; 1188c2ecf20Sopenharmony_ci struct xusb_ep *ep; 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * struct xusb_ep - USB end point structure. 1238c2ecf20Sopenharmony_ci * @ep_usb: usb endpoint instance 1248c2ecf20Sopenharmony_ci * @queue: endpoint message queue 1258c2ecf20Sopenharmony_ci * @udc: xilinx usb peripheral driver instance pointer 1268c2ecf20Sopenharmony_ci * @desc: pointer to the usb endpoint descriptor 1278c2ecf20Sopenharmony_ci * @rambase: the endpoint buffer address 1288c2ecf20Sopenharmony_ci * @offset: the endpoint register offset value 1298c2ecf20Sopenharmony_ci * @name: name of the endpoint 1308c2ecf20Sopenharmony_ci * @epnumber: endpoint number 1318c2ecf20Sopenharmony_ci * @maxpacket: maximum packet size the endpoint can store 1328c2ecf20Sopenharmony_ci * @buffer0count: the size of the packet recieved in the first buffer 1338c2ecf20Sopenharmony_ci * @buffer1count: the size of the packet received in the second buffer 1348c2ecf20Sopenharmony_ci * @curbufnum: current buffer of endpoint that will be processed next 1358c2ecf20Sopenharmony_ci * @buffer0ready: the busy state of first buffer 1368c2ecf20Sopenharmony_ci * @buffer1ready: the busy state of second buffer 1378c2ecf20Sopenharmony_ci * @is_in: endpoint direction (IN or OUT) 1388c2ecf20Sopenharmony_ci * @is_iso: endpoint type(isochronous or non isochronous) 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistruct xusb_ep { 1418c2ecf20Sopenharmony_ci struct usb_ep ep_usb; 1428c2ecf20Sopenharmony_ci struct list_head queue; 1438c2ecf20Sopenharmony_ci struct xusb_udc *udc; 1448c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc; 1458c2ecf20Sopenharmony_ci u32 rambase; 1468c2ecf20Sopenharmony_ci u32 offset; 1478c2ecf20Sopenharmony_ci char name[4]; 1488c2ecf20Sopenharmony_ci u16 epnumber; 1498c2ecf20Sopenharmony_ci u16 maxpacket; 1508c2ecf20Sopenharmony_ci u16 buffer0count; 1518c2ecf20Sopenharmony_ci u16 buffer1count; 1528c2ecf20Sopenharmony_ci u8 curbufnum; 1538c2ecf20Sopenharmony_ci bool buffer0ready; 1548c2ecf20Sopenharmony_ci bool buffer1ready; 1558c2ecf20Sopenharmony_ci bool is_in; 1568c2ecf20Sopenharmony_ci bool is_iso; 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * struct xusb_udc - USB peripheral driver structure 1618c2ecf20Sopenharmony_ci * @gadget: USB gadget driver instance 1628c2ecf20Sopenharmony_ci * @ep: an array of endpoint structures 1638c2ecf20Sopenharmony_ci * @driver: pointer to the usb gadget driver instance 1648c2ecf20Sopenharmony_ci * @setup: usb_ctrlrequest structure for control requests 1658c2ecf20Sopenharmony_ci * @req: pointer to dummy request for get status command 1668c2ecf20Sopenharmony_ci * @dev: pointer to device structure in gadget 1678c2ecf20Sopenharmony_ci * @usb_state: device in suspended state or not 1688c2ecf20Sopenharmony_ci * @remote_wkp: remote wakeup enabled by host 1698c2ecf20Sopenharmony_ci * @setupseqtx: tx status 1708c2ecf20Sopenharmony_ci * @setupseqrx: rx status 1718c2ecf20Sopenharmony_ci * @addr: the usb device base address 1728c2ecf20Sopenharmony_ci * @lock: instance of spinlock 1738c2ecf20Sopenharmony_ci * @dma_enabled: flag indicating whether the dma is included in the system 1748c2ecf20Sopenharmony_ci * @read_fn: function pointer to read device registers 1758c2ecf20Sopenharmony_ci * @write_fn: function pointer to write to device registers 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_cistruct xusb_udc { 1788c2ecf20Sopenharmony_ci struct usb_gadget gadget; 1798c2ecf20Sopenharmony_ci struct xusb_ep ep[8]; 1808c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver; 1818c2ecf20Sopenharmony_ci struct usb_ctrlrequest setup; 1828c2ecf20Sopenharmony_ci struct xusb_req *req; 1838c2ecf20Sopenharmony_ci struct device *dev; 1848c2ecf20Sopenharmony_ci u32 usb_state; 1858c2ecf20Sopenharmony_ci u32 remote_wkp; 1868c2ecf20Sopenharmony_ci u32 setupseqtx; 1878c2ecf20Sopenharmony_ci u32 setupseqrx; 1888c2ecf20Sopenharmony_ci void __iomem *addr; 1898c2ecf20Sopenharmony_ci spinlock_t lock; 1908c2ecf20Sopenharmony_ci bool dma_enabled; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci unsigned int (*read_fn)(void __iomem *); 1938c2ecf20Sopenharmony_ci void (*write_fn)(void __iomem *, u32, u32); 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* Endpoint buffer start addresses in the core */ 1978c2ecf20Sopenharmony_cistatic u32 rambase[8] = { 0x22, 0x1000, 0x1100, 0x1200, 0x1300, 0x1400, 0x1500, 1988c2ecf20Sopenharmony_ci 0x1600 }; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic const char driver_name[] = "xilinx-udc"; 2018c2ecf20Sopenharmony_cistatic const char ep0name[] = "ep0"; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* Control endpoint configuration.*/ 2048c2ecf20Sopenharmony_cistatic const struct usb_endpoint_descriptor config_bulk_out_desc = { 2058c2ecf20Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 2068c2ecf20Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 2078c2ecf20Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 2088c2ecf20Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 2098c2ecf20Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(EP0_MAX_PACKET), 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/** 2138c2ecf20Sopenharmony_ci * xudc_write32 - little endian write to device registers 2148c2ecf20Sopenharmony_ci * @addr: base addr of device registers 2158c2ecf20Sopenharmony_ci * @offset: register offset 2168c2ecf20Sopenharmony_ci * @val: data to be written 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_cistatic void xudc_write32(void __iomem *addr, u32 offset, u32 val) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci iowrite32(val, addr + offset); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/** 2248c2ecf20Sopenharmony_ci * xudc_read32 - little endian read from device registers 2258c2ecf20Sopenharmony_ci * @addr: addr of device register 2268c2ecf20Sopenharmony_ci * Return: value at addr 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_cistatic unsigned int xudc_read32(void __iomem *addr) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci return ioread32(addr); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/** 2348c2ecf20Sopenharmony_ci * xudc_write32_be - big endian write to device registers 2358c2ecf20Sopenharmony_ci * @addr: base addr of device registers 2368c2ecf20Sopenharmony_ci * @offset: register offset 2378c2ecf20Sopenharmony_ci * @val: data to be written 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_cistatic void xudc_write32_be(void __iomem *addr, u32 offset, u32 val) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci iowrite32be(val, addr + offset); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/** 2458c2ecf20Sopenharmony_ci * xudc_read32_be - big endian read from device registers 2468c2ecf20Sopenharmony_ci * @addr: addr of device register 2478c2ecf20Sopenharmony_ci * Return: value at addr 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_cistatic unsigned int xudc_read32_be(void __iomem *addr) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci return ioread32be(addr); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/** 2558c2ecf20Sopenharmony_ci * xudc_wrstatus - Sets up the usb device status stages. 2568c2ecf20Sopenharmony_ci * @udc: pointer to the usb device controller structure. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_cistatic void xudc_wrstatus(struct xusb_udc *udc) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; 2618c2ecf20Sopenharmony_ci u32 epcfgreg; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + ep0->offset)| 2648c2ecf20Sopenharmony_ci XUSB_EP_CFG_DATA_TOGGLE_MASK; 2658c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep0->offset, epcfgreg); 2668c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep0->offset + XUSB_EP_BUF0COUNT_OFFSET, 0); 2678c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/** 2718c2ecf20Sopenharmony_ci * xudc_epconfig - Configures the given endpoint. 2728c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 2738c2ecf20Sopenharmony_ci * @udc: pointer to the usb peripheral controller structure. 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * This function configures a specific endpoint with the given configuration 2768c2ecf20Sopenharmony_ci * data. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_cistatic void xudc_epconfig(struct xusb_ep *ep, struct xusb_udc *udc) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci u32 epcfgreg; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* 2838c2ecf20Sopenharmony_ci * Configure the end point direction, type, Max Packet Size and the 2848c2ecf20Sopenharmony_ci * EP buffer location. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci epcfgreg = ((ep->is_in << 29) | (ep->is_iso << 28) | 2878c2ecf20Sopenharmony_ci (ep->ep_usb.maxpacket << 15) | (ep->rambase)); 2888c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfgreg); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* Set the Buffer count and the Buffer ready bits.*/ 2918c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF0COUNT_OFFSET, 2928c2ecf20Sopenharmony_ci ep->buffer0count); 2938c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset + XUSB_EP_BUF1COUNT_OFFSET, 2948c2ecf20Sopenharmony_ci ep->buffer1count); 2958c2ecf20Sopenharmony_ci if (ep->buffer0ready) 2968c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 2978c2ecf20Sopenharmony_ci 1 << ep->epnumber); 2988c2ecf20Sopenharmony_ci if (ep->buffer1ready) 2998c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 3008c2ecf20Sopenharmony_ci 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT)); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/** 3048c2ecf20Sopenharmony_ci * xudc_start_dma - Starts DMA transfer. 3058c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 3068c2ecf20Sopenharmony_ci * @src: DMA source address. 3078c2ecf20Sopenharmony_ci * @dst: DMA destination address. 3088c2ecf20Sopenharmony_ci * @length: number of bytes to transfer. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * Return: 0 on success, error code on failure 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * This function starts DMA transfer by writing to DMA source, 3138c2ecf20Sopenharmony_ci * destination and lenth registers. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cistatic int xudc_start_dma(struct xusb_ep *ep, dma_addr_t src, 3168c2ecf20Sopenharmony_ci dma_addr_t dst, u32 length) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 3198c2ecf20Sopenharmony_ci int rc = 0; 3208c2ecf20Sopenharmony_ci u32 timeout = 500; 3218c2ecf20Sopenharmony_ci u32 reg; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * Set the addresses in the DMA source and 3258c2ecf20Sopenharmony_ci * destination registers and then set the length 3268c2ecf20Sopenharmony_ci * into the DMA length register. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_DMA_DSAR_ADDR_OFFSET, src); 3298c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_DMA_DDAR_ADDR_OFFSET, dst); 3308c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_DMA_LENGTH_OFFSET, length); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* 3338c2ecf20Sopenharmony_ci * Wait till DMA transaction is complete and 3348c2ecf20Sopenharmony_ci * check whether the DMA transaction was 3358c2ecf20Sopenharmony_ci * successful. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci do { 3388c2ecf20Sopenharmony_ci reg = udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET); 3398c2ecf20Sopenharmony_ci if (!(reg & XUSB_DMA_DMASR_BUSY)) 3408c2ecf20Sopenharmony_ci break; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* 3438c2ecf20Sopenharmony_ci * We can't sleep here, because it's also called from 3448c2ecf20Sopenharmony_ci * interrupt context. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci timeout--; 3478c2ecf20Sopenharmony_ci if (!timeout) { 3488c2ecf20Sopenharmony_ci dev_err(udc->dev, "DMA timeout\n"); 3498c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci udelay(1); 3528c2ecf20Sopenharmony_ci } while (1); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if ((udc->read_fn(udc->addr + XUSB_DMA_STATUS_OFFSET) & 3558c2ecf20Sopenharmony_ci XUSB_DMA_DMASR_ERROR) == XUSB_DMA_DMASR_ERROR){ 3568c2ecf20Sopenharmony_ci dev_err(udc->dev, "DMA Error\n"); 3578c2ecf20Sopenharmony_ci rc = -EINVAL; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return rc; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/** 3648c2ecf20Sopenharmony_ci * xudc_dma_send - Sends IN data using DMA. 3658c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 3668c2ecf20Sopenharmony_ci * @req: pointer to the usb request structure. 3678c2ecf20Sopenharmony_ci * @buffer: pointer to data to be sent. 3688c2ecf20Sopenharmony_ci * @length: number of bytes to send. 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * Return: 0 on success, -EAGAIN if no buffer is free and error 3718c2ecf20Sopenharmony_ci * code on failure. 3728c2ecf20Sopenharmony_ci * 3738c2ecf20Sopenharmony_ci * This function sends data using DMA. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_cistatic int xudc_dma_send(struct xusb_ep *ep, struct xusb_req *req, 3768c2ecf20Sopenharmony_ci u8 *buffer, u32 length) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci u32 *eprambase; 3798c2ecf20Sopenharmony_ci dma_addr_t src; 3808c2ecf20Sopenharmony_ci dma_addr_t dst; 3818c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci src = req->usb_req.dma + req->usb_req.actual; 3848c2ecf20Sopenharmony_ci if (req->usb_req.length) 3858c2ecf20Sopenharmony_ci dma_sync_single_for_device(udc->dev, src, 3868c2ecf20Sopenharmony_ci length, DMA_TO_DEVICE); 3878c2ecf20Sopenharmony_ci if (!ep->curbufnum && !ep->buffer0ready) { 3888c2ecf20Sopenharmony_ci /* Get the Buffer address and copy the transmit data.*/ 3898c2ecf20Sopenharmony_ci eprambase = (u32 __force *)(udc->addr + ep->rambase); 3908c2ecf20Sopenharmony_ci dst = virt_to_phys(eprambase); 3918c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset + 3928c2ecf20Sopenharmony_ci XUSB_EP_BUF0COUNT_OFFSET, length); 3938c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, 3948c2ecf20Sopenharmony_ci XUSB_DMA_BRR_CTRL | (1 << ep->epnumber)); 3958c2ecf20Sopenharmony_ci ep->buffer0ready = 1; 3968c2ecf20Sopenharmony_ci ep->curbufnum = 1; 3978c2ecf20Sopenharmony_ci } else if (ep->curbufnum && !ep->buffer1ready) { 3988c2ecf20Sopenharmony_ci /* Get the Buffer address and copy the transmit data.*/ 3998c2ecf20Sopenharmony_ci eprambase = (u32 __force *)(udc->addr + ep->rambase + 4008c2ecf20Sopenharmony_ci ep->ep_usb.maxpacket); 4018c2ecf20Sopenharmony_ci dst = virt_to_phys(eprambase); 4028c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset + 4038c2ecf20Sopenharmony_ci XUSB_EP_BUF1COUNT_OFFSET, length); 4048c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, 4058c2ecf20Sopenharmony_ci XUSB_DMA_BRR_CTRL | (1 << (ep->epnumber + 4068c2ecf20Sopenharmony_ci XUSB_STATUS_EP_BUFF2_SHIFT))); 4078c2ecf20Sopenharmony_ci ep->buffer1ready = 1; 4088c2ecf20Sopenharmony_ci ep->curbufnum = 0; 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci /* None of ping pong buffers are ready currently .*/ 4118c2ecf20Sopenharmony_ci return -EAGAIN; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return xudc_start_dma(ep, src, dst, length); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci/** 4188c2ecf20Sopenharmony_ci * xudc_dma_receive - Receives OUT data using DMA. 4198c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 4208c2ecf20Sopenharmony_ci * @req: pointer to the usb request structure. 4218c2ecf20Sopenharmony_ci * @buffer: pointer to storage buffer of received data. 4228c2ecf20Sopenharmony_ci * @length: number of bytes to receive. 4238c2ecf20Sopenharmony_ci * 4248c2ecf20Sopenharmony_ci * Return: 0 on success, -EAGAIN if no buffer is free and error 4258c2ecf20Sopenharmony_ci * code on failure. 4268c2ecf20Sopenharmony_ci * 4278c2ecf20Sopenharmony_ci * This function receives data using DMA. 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_cistatic int xudc_dma_receive(struct xusb_ep *ep, struct xusb_req *req, 4308c2ecf20Sopenharmony_ci u8 *buffer, u32 length) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci u32 *eprambase; 4338c2ecf20Sopenharmony_ci dma_addr_t src; 4348c2ecf20Sopenharmony_ci dma_addr_t dst; 4358c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci dst = req->usb_req.dma + req->usb_req.actual; 4388c2ecf20Sopenharmony_ci if (!ep->curbufnum && !ep->buffer0ready) { 4398c2ecf20Sopenharmony_ci /* Get the Buffer address and copy the transmit data */ 4408c2ecf20Sopenharmony_ci eprambase = (u32 __force *)(udc->addr + ep->rambase); 4418c2ecf20Sopenharmony_ci src = virt_to_phys(eprambase); 4428c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, 4438c2ecf20Sopenharmony_ci XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM | 4448c2ecf20Sopenharmony_ci (1 << ep->epnumber)); 4458c2ecf20Sopenharmony_ci ep->buffer0ready = 1; 4468c2ecf20Sopenharmony_ci ep->curbufnum = 1; 4478c2ecf20Sopenharmony_ci } else if (ep->curbufnum && !ep->buffer1ready) { 4488c2ecf20Sopenharmony_ci /* Get the Buffer address and copy the transmit data */ 4498c2ecf20Sopenharmony_ci eprambase = (u32 __force *)(udc->addr + 4508c2ecf20Sopenharmony_ci ep->rambase + ep->ep_usb.maxpacket); 4518c2ecf20Sopenharmony_ci src = virt_to_phys(eprambase); 4528c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_DMA_CONTROL_OFFSET, 4538c2ecf20Sopenharmony_ci XUSB_DMA_BRR_CTRL | XUSB_DMA_READ_FROM_DPRAM | 4548c2ecf20Sopenharmony_ci (1 << (ep->epnumber + 4558c2ecf20Sopenharmony_ci XUSB_STATUS_EP_BUFF2_SHIFT))); 4568c2ecf20Sopenharmony_ci ep->buffer1ready = 1; 4578c2ecf20Sopenharmony_ci ep->curbufnum = 0; 4588c2ecf20Sopenharmony_ci } else { 4598c2ecf20Sopenharmony_ci /* None of the ping-pong buffers are ready currently */ 4608c2ecf20Sopenharmony_ci return -EAGAIN; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return xudc_start_dma(ep, src, dst, length); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/** 4678c2ecf20Sopenharmony_ci * xudc_eptxrx - Transmits or receives data to or from an endpoint. 4688c2ecf20Sopenharmony_ci * @ep: pointer to the usb endpoint configuration structure. 4698c2ecf20Sopenharmony_ci * @req: pointer to the usb request structure. 4708c2ecf20Sopenharmony_ci * @bufferptr: pointer to buffer containing the data to be sent. 4718c2ecf20Sopenharmony_ci * @bufferlen: The number of data bytes to be sent. 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * Return: 0 on success, -EAGAIN if no buffer is free. 4748c2ecf20Sopenharmony_ci * 4758c2ecf20Sopenharmony_ci * This function copies the transmit/receive data to/from the end point buffer 4768c2ecf20Sopenharmony_ci * and enables the buffer for transmission/reception. 4778c2ecf20Sopenharmony_ci */ 4788c2ecf20Sopenharmony_cistatic int xudc_eptxrx(struct xusb_ep *ep, struct xusb_req *req, 4798c2ecf20Sopenharmony_ci u8 *bufferptr, u32 bufferlen) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci u32 *eprambase; 4828c2ecf20Sopenharmony_ci u32 bytestosend; 4838c2ecf20Sopenharmony_ci int rc = 0; 4848c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci bytestosend = bufferlen; 4878c2ecf20Sopenharmony_ci if (udc->dma_enabled) { 4888c2ecf20Sopenharmony_ci if (ep->is_in) 4898c2ecf20Sopenharmony_ci rc = xudc_dma_send(ep, req, bufferptr, bufferlen); 4908c2ecf20Sopenharmony_ci else 4918c2ecf20Sopenharmony_ci rc = xudc_dma_receive(ep, req, bufferptr, bufferlen); 4928c2ecf20Sopenharmony_ci return rc; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci /* Put the transmit buffer into the correct ping-pong buffer.*/ 4958c2ecf20Sopenharmony_ci if (!ep->curbufnum && !ep->buffer0ready) { 4968c2ecf20Sopenharmony_ci /* Get the Buffer address and copy the transmit data.*/ 4978c2ecf20Sopenharmony_ci eprambase = (u32 __force *)(udc->addr + ep->rambase); 4988c2ecf20Sopenharmony_ci if (ep->is_in) { 4998c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)eprambase, bufferptr, 5008c2ecf20Sopenharmony_ci bytestosend); 5018c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset + 5028c2ecf20Sopenharmony_ci XUSB_EP_BUF0COUNT_OFFSET, bufferlen); 5038c2ecf20Sopenharmony_ci } else { 5048c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)bufferptr, eprambase, 5058c2ecf20Sopenharmony_ci bytestosend); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci /* 5088c2ecf20Sopenharmony_ci * Enable the buffer for transmission. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 5118c2ecf20Sopenharmony_ci 1 << ep->epnumber); 5128c2ecf20Sopenharmony_ci ep->buffer0ready = 1; 5138c2ecf20Sopenharmony_ci ep->curbufnum = 1; 5148c2ecf20Sopenharmony_ci } else if (ep->curbufnum && !ep->buffer1ready) { 5158c2ecf20Sopenharmony_ci /* Get the Buffer address and copy the transmit data.*/ 5168c2ecf20Sopenharmony_ci eprambase = (u32 __force *)(udc->addr + ep->rambase + 5178c2ecf20Sopenharmony_ci ep->ep_usb.maxpacket); 5188c2ecf20Sopenharmony_ci if (ep->is_in) { 5198c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)eprambase, bufferptr, 5208c2ecf20Sopenharmony_ci bytestosend); 5218c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset + 5228c2ecf20Sopenharmony_ci XUSB_EP_BUF1COUNT_OFFSET, bufferlen); 5238c2ecf20Sopenharmony_ci } else { 5248c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)bufferptr, eprambase, 5258c2ecf20Sopenharmony_ci bytestosend); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci /* 5288c2ecf20Sopenharmony_ci * Enable the buffer for transmission. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 5318c2ecf20Sopenharmony_ci 1 << (ep->epnumber + XUSB_STATUS_EP_BUFF2_SHIFT)); 5328c2ecf20Sopenharmony_ci ep->buffer1ready = 1; 5338c2ecf20Sopenharmony_ci ep->curbufnum = 0; 5348c2ecf20Sopenharmony_ci } else { 5358c2ecf20Sopenharmony_ci /* None of the ping-pong buffers are ready currently */ 5368c2ecf20Sopenharmony_ci return -EAGAIN; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci return rc; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci/** 5428c2ecf20Sopenharmony_ci * xudc_done - Exeutes the endpoint data transfer completion tasks. 5438c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 5448c2ecf20Sopenharmony_ci * @req: pointer to the usb request structure. 5458c2ecf20Sopenharmony_ci * @status: Status of the data transfer. 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * Deletes the message from the queue and updates data transfer completion 5488c2ecf20Sopenharmony_ci * status. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_cistatic void xudc_done(struct xusb_ep *ep, struct xusb_req *req, int status) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci list_del_init(&req->queue); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (req->usb_req.status == -EINPROGRESS) 5578c2ecf20Sopenharmony_ci req->usb_req.status = status; 5588c2ecf20Sopenharmony_ci else 5598c2ecf20Sopenharmony_ci status = req->usb_req.status; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (status && status != -ESHUTDOWN) 5628c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "%s done %p, status %d\n", 5638c2ecf20Sopenharmony_ci ep->ep_usb.name, req, status); 5648c2ecf20Sopenharmony_ci /* unmap request if DMA is present*/ 5658c2ecf20Sopenharmony_ci if (udc->dma_enabled && ep->epnumber && req->usb_req.length) 5668c2ecf20Sopenharmony_ci usb_gadget_unmap_request(&udc->gadget, &req->usb_req, 5678c2ecf20Sopenharmony_ci ep->is_in); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (req->usb_req.complete) { 5708c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 5718c2ecf20Sopenharmony_ci req->usb_req.complete(&ep->ep_usb, &req->usb_req); 5728c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci/** 5778c2ecf20Sopenharmony_ci * xudc_read_fifo - Reads the data from the given endpoint buffer. 5788c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 5798c2ecf20Sopenharmony_ci * @req: pointer to the usb request structure. 5808c2ecf20Sopenharmony_ci * 5818c2ecf20Sopenharmony_ci * Return: 0 if request is completed and -EAGAIN if not completed. 5828c2ecf20Sopenharmony_ci * 5838c2ecf20Sopenharmony_ci * Pulls OUT packet data from the endpoint buffer. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_cistatic int xudc_read_fifo(struct xusb_ep *ep, struct xusb_req *req) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci u8 *buf; 5888c2ecf20Sopenharmony_ci u32 is_short, count, bufferspace; 5898c2ecf20Sopenharmony_ci u8 bufoffset; 5908c2ecf20Sopenharmony_ci u8 two_pkts = 0; 5918c2ecf20Sopenharmony_ci int ret; 5928c2ecf20Sopenharmony_ci int retval = -EAGAIN; 5938c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (ep->buffer0ready && ep->buffer1ready) { 5968c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Packet NOT ready!\n"); 5978c2ecf20Sopenharmony_ci return retval; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_citop: 6008c2ecf20Sopenharmony_ci if (ep->curbufnum) 6018c2ecf20Sopenharmony_ci bufoffset = XUSB_EP_BUF1COUNT_OFFSET; 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci bufoffset = XUSB_EP_BUF0COUNT_OFFSET; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci count = udc->read_fn(udc->addr + ep->offset + bufoffset); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (!ep->buffer0ready && !ep->buffer1ready) 6088c2ecf20Sopenharmony_ci two_pkts = 1; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci buf = req->usb_req.buf + req->usb_req.actual; 6118c2ecf20Sopenharmony_ci prefetchw(buf); 6128c2ecf20Sopenharmony_ci bufferspace = req->usb_req.length - req->usb_req.actual; 6138c2ecf20Sopenharmony_ci is_short = count < ep->ep_usb.maxpacket; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (unlikely(!bufferspace)) { 6168c2ecf20Sopenharmony_ci /* 6178c2ecf20Sopenharmony_ci * This happens when the driver's buffer 6188c2ecf20Sopenharmony_ci * is smaller than what the host sent. 6198c2ecf20Sopenharmony_ci * discard the extra data. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci if (req->usb_req.status != -EOVERFLOW) 6228c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "%s overflow %d\n", 6238c2ecf20Sopenharmony_ci ep->ep_usb.name, count); 6248c2ecf20Sopenharmony_ci req->usb_req.status = -EOVERFLOW; 6258c2ecf20Sopenharmony_ci xudc_done(ep, req, -EOVERFLOW); 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci ret = xudc_eptxrx(ep, req, buf, count); 6308c2ecf20Sopenharmony_ci switch (ret) { 6318c2ecf20Sopenharmony_ci case 0: 6328c2ecf20Sopenharmony_ci req->usb_req.actual += min(count, bufferspace); 6338c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n", 6348c2ecf20Sopenharmony_ci ep->ep_usb.name, count, is_short ? "/S" : "", req, 6358c2ecf20Sopenharmony_ci req->usb_req.actual, req->usb_req.length); 6368c2ecf20Sopenharmony_ci bufferspace -= count; 6378c2ecf20Sopenharmony_ci /* Completion */ 6388c2ecf20Sopenharmony_ci if ((req->usb_req.actual == req->usb_req.length) || is_short) { 6398c2ecf20Sopenharmony_ci if (udc->dma_enabled && req->usb_req.length) 6408c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(udc->dev, 6418c2ecf20Sopenharmony_ci req->usb_req.dma, 6428c2ecf20Sopenharmony_ci req->usb_req.actual, 6438c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6448c2ecf20Sopenharmony_ci xudc_done(ep, req, 0); 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci if (two_pkts) { 6488c2ecf20Sopenharmony_ci two_pkts = 0; 6498c2ecf20Sopenharmony_ci goto top; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci case -EAGAIN: 6538c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "receive busy\n"); 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci case -EINVAL: 6568c2ecf20Sopenharmony_ci case -ETIMEDOUT: 6578c2ecf20Sopenharmony_ci /* DMA error, dequeue the request */ 6588c2ecf20Sopenharmony_ci xudc_done(ep, req, -ECONNRESET); 6598c2ecf20Sopenharmony_ci retval = 0; 6608c2ecf20Sopenharmony_ci break; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return retval; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/** 6678c2ecf20Sopenharmony_ci * xudc_write_fifo - Writes data into the given endpoint buffer. 6688c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 6698c2ecf20Sopenharmony_ci * @req: pointer to the usb request structure. 6708c2ecf20Sopenharmony_ci * 6718c2ecf20Sopenharmony_ci * Return: 0 if request is completed and -EAGAIN if not completed. 6728c2ecf20Sopenharmony_ci * 6738c2ecf20Sopenharmony_ci * Loads endpoint buffer for an IN packet. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_cistatic int xudc_write_fifo(struct xusb_ep *ep, struct xusb_req *req) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci u32 max; 6788c2ecf20Sopenharmony_ci u32 length; 6798c2ecf20Sopenharmony_ci int ret; 6808c2ecf20Sopenharmony_ci int retval = -EAGAIN; 6818c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 6828c2ecf20Sopenharmony_ci int is_last, is_short = 0; 6838c2ecf20Sopenharmony_ci u8 *buf; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci max = le16_to_cpu(ep->desc->wMaxPacketSize); 6868c2ecf20Sopenharmony_ci buf = req->usb_req.buf + req->usb_req.actual; 6878c2ecf20Sopenharmony_ci prefetch(buf); 6888c2ecf20Sopenharmony_ci length = req->usb_req.length - req->usb_req.actual; 6898c2ecf20Sopenharmony_ci length = min(length, max); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci ret = xudc_eptxrx(ep, req, buf, length); 6928c2ecf20Sopenharmony_ci switch (ret) { 6938c2ecf20Sopenharmony_ci case 0: 6948c2ecf20Sopenharmony_ci req->usb_req.actual += length; 6958c2ecf20Sopenharmony_ci if (unlikely(length != max)) { 6968c2ecf20Sopenharmony_ci is_last = is_short = 1; 6978c2ecf20Sopenharmony_ci } else { 6988c2ecf20Sopenharmony_ci if (likely(req->usb_req.length != 6998c2ecf20Sopenharmony_ci req->usb_req.actual) || req->usb_req.zero) 7008c2ecf20Sopenharmony_ci is_last = 0; 7018c2ecf20Sopenharmony_ci else 7028c2ecf20Sopenharmony_ci is_last = 1; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "%s: wrote %s %d bytes%s%s %d left %p\n", 7058c2ecf20Sopenharmony_ci __func__, ep->ep_usb.name, length, is_last ? "/L" : "", 7068c2ecf20Sopenharmony_ci is_short ? "/S" : "", 7078c2ecf20Sopenharmony_ci req->usb_req.length - req->usb_req.actual, req); 7088c2ecf20Sopenharmony_ci /* completion */ 7098c2ecf20Sopenharmony_ci if (is_last) { 7108c2ecf20Sopenharmony_ci xudc_done(ep, req, 0); 7118c2ecf20Sopenharmony_ci retval = 0; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci break; 7148c2ecf20Sopenharmony_ci case -EAGAIN: 7158c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Send busy\n"); 7168c2ecf20Sopenharmony_ci break; 7178c2ecf20Sopenharmony_ci case -EINVAL: 7188c2ecf20Sopenharmony_ci case -ETIMEDOUT: 7198c2ecf20Sopenharmony_ci /* DMA error, dequeue the request */ 7208c2ecf20Sopenharmony_ci xudc_done(ep, req, -ECONNRESET); 7218c2ecf20Sopenharmony_ci retval = 0; 7228c2ecf20Sopenharmony_ci break; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci return retval; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/** 7298c2ecf20Sopenharmony_ci * xudc_nuke - Cleans up the data transfer message list. 7308c2ecf20Sopenharmony_ci * @ep: pointer to the usb device endpoint structure. 7318c2ecf20Sopenharmony_ci * @status: Status of the data transfer. 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_cistatic void xudc_nuke(struct xusb_ep *ep, int status) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct xusb_req *req; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci while (!list_empty(&ep->queue)) { 7388c2ecf20Sopenharmony_ci req = list_first_entry(&ep->queue, struct xusb_req, queue); 7398c2ecf20Sopenharmony_ci xudc_done(ep, req, status); 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/** 7448c2ecf20Sopenharmony_ci * xudc_ep_set_halt - Stalls/unstalls the given endpoint. 7458c2ecf20Sopenharmony_ci * @_ep: pointer to the usb device endpoint structure. 7468c2ecf20Sopenharmony_ci * @value: value to indicate stall/unstall. 7478c2ecf20Sopenharmony_ci * 7488c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 7498c2ecf20Sopenharmony_ci */ 7508c2ecf20Sopenharmony_cistatic int xudc_ep_set_halt(struct usb_ep *_ep, int value) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct xusb_ep *ep = to_xusb_ep(_ep); 7538c2ecf20Sopenharmony_ci struct xusb_udc *udc; 7548c2ecf20Sopenharmony_ci unsigned long flags; 7558c2ecf20Sopenharmony_ci u32 epcfgreg; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (!_ep || (!ep->desc && ep->epnumber)) { 7588c2ecf20Sopenharmony_ci pr_debug("%s: bad ep or descriptor\n", __func__); 7598c2ecf20Sopenharmony_ci return -EINVAL; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci udc = ep->udc; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (ep->is_in && (!list_empty(&ep->queue)) && value) { 7648c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "requests pending can't halt\n"); 7658c2ecf20Sopenharmony_ci return -EAGAIN; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (ep->buffer0ready || ep->buffer1ready) { 7698c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "HW buffers busy can't halt\n"); 7708c2ecf20Sopenharmony_ci return -EAGAIN; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (value) { 7768c2ecf20Sopenharmony_ci /* Stall the device.*/ 7778c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + ep->offset); 7788c2ecf20Sopenharmony_ci epcfgreg |= XUSB_EP_CFG_STALL_MASK; 7798c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfgreg); 7808c2ecf20Sopenharmony_ci } else { 7818c2ecf20Sopenharmony_ci /* Unstall the device.*/ 7828c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + ep->offset); 7838c2ecf20Sopenharmony_ci epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; 7848c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfgreg); 7858c2ecf20Sopenharmony_ci if (ep->epnumber) { 7868c2ecf20Sopenharmony_ci /* Reset the toggle bit.*/ 7878c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(ep->udc->addr + ep->offset); 7888c2ecf20Sopenharmony_ci epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK; 7898c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfgreg); 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_ci} 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci/** 7988c2ecf20Sopenharmony_ci * xudc_ep_enable - Enables the given endpoint. 7998c2ecf20Sopenharmony_ci * @ep: pointer to the xusb endpoint structure. 8008c2ecf20Sopenharmony_ci * @desc: pointer to usb endpoint descriptor. 8018c2ecf20Sopenharmony_ci * 8028c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 8038c2ecf20Sopenharmony_ci */ 8048c2ecf20Sopenharmony_cistatic int __xudc_ep_enable(struct xusb_ep *ep, 8058c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 8088c2ecf20Sopenharmony_ci u32 tmp; 8098c2ecf20Sopenharmony_ci u32 epcfg; 8108c2ecf20Sopenharmony_ci u32 ier; 8118c2ecf20Sopenharmony_ci u16 maxpacket; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci ep->is_in = ((desc->bEndpointAddress & USB_DIR_IN) != 0); 8148c2ecf20Sopenharmony_ci /* Bit 3...0:endpoint number */ 8158c2ecf20Sopenharmony_ci ep->epnumber = (desc->bEndpointAddress & 0x0f); 8168c2ecf20Sopenharmony_ci ep->desc = desc; 8178c2ecf20Sopenharmony_ci ep->ep_usb.desc = desc; 8188c2ecf20Sopenharmony_ci tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; 8198c2ecf20Sopenharmony_ci ep->ep_usb.maxpacket = maxpacket = le16_to_cpu(desc->wMaxPacketSize); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci switch (tmp) { 8228c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_CONTROL: 8238c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "only one control endpoint\n"); 8248c2ecf20Sopenharmony_ci /* NON- ISO */ 8258c2ecf20Sopenharmony_ci ep->is_iso = 0; 8268c2ecf20Sopenharmony_ci return -EINVAL; 8278c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_INT: 8288c2ecf20Sopenharmony_ci /* NON- ISO */ 8298c2ecf20Sopenharmony_ci ep->is_iso = 0; 8308c2ecf20Sopenharmony_ci if (maxpacket > 64) { 8318c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket); 8328c2ecf20Sopenharmony_ci return -EINVAL; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: 8368c2ecf20Sopenharmony_ci /* NON- ISO */ 8378c2ecf20Sopenharmony_ci ep->is_iso = 0; 8388c2ecf20Sopenharmony_ci if (!(is_power_of_2(maxpacket) && maxpacket >= 8 && 8398c2ecf20Sopenharmony_ci maxpacket <= 512)) { 8408c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "bogus maxpacket %d\n", maxpacket); 8418c2ecf20Sopenharmony_ci return -EINVAL; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci break; 8448c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: 8458c2ecf20Sopenharmony_ci /* ISO */ 8468c2ecf20Sopenharmony_ci ep->is_iso = 1; 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci ep->buffer0ready = 0; 8518c2ecf20Sopenharmony_ci ep->buffer1ready = 0; 8528c2ecf20Sopenharmony_ci ep->curbufnum = 0; 8538c2ecf20Sopenharmony_ci ep->rambase = rambase[ep->epnumber]; 8548c2ecf20Sopenharmony_ci xudc_epconfig(ep, udc); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Enable Endpoint %d max pkt is %d\n", 8578c2ecf20Sopenharmony_ci ep->epnumber, maxpacket); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* Enable the End point.*/ 8608c2ecf20Sopenharmony_ci epcfg = udc->read_fn(udc->addr + ep->offset); 8618c2ecf20Sopenharmony_ci epcfg |= XUSB_EP_CFG_VALID_MASK; 8628c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfg); 8638c2ecf20Sopenharmony_ci if (ep->epnumber) 8648c2ecf20Sopenharmony_ci ep->rambase <<= 2; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Enable buffer completion interrupts for endpoint */ 8678c2ecf20Sopenharmony_ci ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); 8688c2ecf20Sopenharmony_ci ier |= (XUSB_STATUS_INTR_BUFF_COMP_SHIFT_MASK << ep->epnumber); 8698c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci /* for OUT endpoint set buffers ready to receive */ 8728c2ecf20Sopenharmony_ci if (ep->epnumber && !ep->is_in) { 8738c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 8748c2ecf20Sopenharmony_ci 1 << ep->epnumber); 8758c2ecf20Sopenharmony_ci ep->buffer0ready = 1; 8768c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 8778c2ecf20Sopenharmony_ci (1 << (ep->epnumber + 8788c2ecf20Sopenharmony_ci XUSB_STATUS_EP_BUFF2_SHIFT))); 8798c2ecf20Sopenharmony_ci ep->buffer1ready = 1; 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci/** 8868c2ecf20Sopenharmony_ci * xudc_ep_enable - Enables the given endpoint. 8878c2ecf20Sopenharmony_ci * @_ep: pointer to the usb endpoint structure. 8888c2ecf20Sopenharmony_ci * @desc: pointer to usb endpoint descriptor. 8898c2ecf20Sopenharmony_ci * 8908c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 8918c2ecf20Sopenharmony_ci */ 8928c2ecf20Sopenharmony_cistatic int xudc_ep_enable(struct usb_ep *_ep, 8938c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 8948c2ecf20Sopenharmony_ci{ 8958c2ecf20Sopenharmony_ci struct xusb_ep *ep; 8968c2ecf20Sopenharmony_ci struct xusb_udc *udc; 8978c2ecf20Sopenharmony_ci unsigned long flags; 8988c2ecf20Sopenharmony_ci int ret; 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { 9018c2ecf20Sopenharmony_ci pr_debug("%s: bad ep or descriptor\n", __func__); 9028c2ecf20Sopenharmony_ci return -EINVAL; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci ep = to_xusb_ep(_ep); 9068c2ecf20Sopenharmony_ci udc = ep->udc; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { 9098c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "bogus device state\n"); 9108c2ecf20Sopenharmony_ci return -ESHUTDOWN; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 9148c2ecf20Sopenharmony_ci ret = __xudc_ep_enable(ep, desc); 9158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci return ret; 9188c2ecf20Sopenharmony_ci} 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci/** 9218c2ecf20Sopenharmony_ci * xudc_ep_disable - Disables the given endpoint. 9228c2ecf20Sopenharmony_ci * @_ep: pointer to the usb endpoint structure. 9238c2ecf20Sopenharmony_ci * 9248c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_cistatic int xudc_ep_disable(struct usb_ep *_ep) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct xusb_ep *ep; 9298c2ecf20Sopenharmony_ci unsigned long flags; 9308c2ecf20Sopenharmony_ci u32 epcfg; 9318c2ecf20Sopenharmony_ci struct xusb_udc *udc; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (!_ep) { 9348c2ecf20Sopenharmony_ci pr_debug("%s: invalid ep\n", __func__); 9358c2ecf20Sopenharmony_ci return -EINVAL; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci ep = to_xusb_ep(_ep); 9398c2ecf20Sopenharmony_ci udc = ep->udc; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci xudc_nuke(ep, -ESHUTDOWN); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Restore the endpoint's pristine config */ 9468c2ecf20Sopenharmony_ci ep->desc = NULL; 9478c2ecf20Sopenharmony_ci ep->ep_usb.desc = NULL; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "USB Ep %d disable\n ", ep->epnumber); 9508c2ecf20Sopenharmony_ci /* Disable the endpoint.*/ 9518c2ecf20Sopenharmony_ci epcfg = udc->read_fn(udc->addr + ep->offset); 9528c2ecf20Sopenharmony_ci epcfg &= ~XUSB_EP_CFG_VALID_MASK; 9538c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfg); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 9568c2ecf20Sopenharmony_ci return 0; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci/** 9608c2ecf20Sopenharmony_ci * xudc_ep_alloc_request - Initializes the request queue. 9618c2ecf20Sopenharmony_ci * @_ep: pointer to the usb endpoint structure. 9628c2ecf20Sopenharmony_ci * @gfp_flags: Flags related to the request call. 9638c2ecf20Sopenharmony_ci * 9648c2ecf20Sopenharmony_ci * Return: pointer to request structure on success and a NULL on failure. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_cistatic struct usb_request *xudc_ep_alloc_request(struct usb_ep *_ep, 9678c2ecf20Sopenharmony_ci gfp_t gfp_flags) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct xusb_ep *ep = to_xusb_ep(_ep); 9708c2ecf20Sopenharmony_ci struct xusb_req *req; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci req = kzalloc(sizeof(*req), gfp_flags); 9738c2ecf20Sopenharmony_ci if (!req) 9748c2ecf20Sopenharmony_ci return NULL; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci req->ep = ep; 9778c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&req->queue); 9788c2ecf20Sopenharmony_ci return &req->usb_req; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/** 9828c2ecf20Sopenharmony_ci * xudc_free_request - Releases the request from queue. 9838c2ecf20Sopenharmony_ci * @_ep: pointer to the usb device endpoint structure. 9848c2ecf20Sopenharmony_ci * @_req: pointer to the usb request structure. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_cistatic void xudc_free_request(struct usb_ep *_ep, struct usb_request *_req) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct xusb_req *req = to_xusb_req(_req); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci kfree(req); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/** 9948c2ecf20Sopenharmony_ci * xudc_ep0_queue - Adds the request to endpoint 0 queue. 9958c2ecf20Sopenharmony_ci * @ep0: pointer to the xusb endpoint 0 structure. 9968c2ecf20Sopenharmony_ci * @req: pointer to the xusb request structure. 9978c2ecf20Sopenharmony_ci * 9988c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_cistatic int __xudc_ep0_queue(struct xusb_ep *ep0, struct xusb_req *req) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep0->udc; 10038c2ecf20Sopenharmony_ci u32 length; 10048c2ecf20Sopenharmony_ci u8 *corebuf; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { 10078c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "%s, bogus device state\n", __func__); 10088c2ecf20Sopenharmony_ci return -EINVAL; 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci if (!list_empty(&ep0->queue)) { 10118c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "%s:ep0 busy\n", __func__); 10128c2ecf20Sopenharmony_ci return -EBUSY; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci req->usb_req.status = -EINPROGRESS; 10168c2ecf20Sopenharmony_ci req->usb_req.actual = 0; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci list_add_tail(&req->queue, &ep0->queue); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (udc->setup.bRequestType & USB_DIR_IN) { 10218c2ecf20Sopenharmony_ci prefetch(req->usb_req.buf); 10228c2ecf20Sopenharmony_ci length = req->usb_req.length; 10238c2ecf20Sopenharmony_ci corebuf = (void __force *) ((ep0->rambase << 2) + 10248c2ecf20Sopenharmony_ci udc->addr); 10258c2ecf20Sopenharmony_ci length = req->usb_req.actual = min_t(u32, length, 10268c2ecf20Sopenharmony_ci EP0_MAX_PACKET); 10278c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)corebuf, req->usb_req.buf, length); 10288c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, length); 10298c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); 10308c2ecf20Sopenharmony_ci } else { 10318c2ecf20Sopenharmony_ci if (udc->setup.wLength) { 10328c2ecf20Sopenharmony_ci /* Enable EP0 buffer to receive data */ 10338c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0); 10348c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); 10358c2ecf20Sopenharmony_ci } else { 10368c2ecf20Sopenharmony_ci xudc_wrstatus(udc); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci return 0; 10418c2ecf20Sopenharmony_ci} 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci/** 10448c2ecf20Sopenharmony_ci * xudc_ep0_queue - Adds the request to endpoint 0 queue. 10458c2ecf20Sopenharmony_ci * @_ep: pointer to the usb endpoint 0 structure. 10468c2ecf20Sopenharmony_ci * @_req: pointer to the usb request structure. 10478c2ecf20Sopenharmony_ci * @gfp_flags: Flags related to the request call. 10488c2ecf20Sopenharmony_ci * 10498c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 10508c2ecf20Sopenharmony_ci */ 10518c2ecf20Sopenharmony_cistatic int xudc_ep0_queue(struct usb_ep *_ep, struct usb_request *_req, 10528c2ecf20Sopenharmony_ci gfp_t gfp_flags) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct xusb_req *req = to_xusb_req(_req); 10558c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = to_xusb_ep(_ep); 10568c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep0->udc; 10578c2ecf20Sopenharmony_ci unsigned long flags; 10588c2ecf20Sopenharmony_ci int ret; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 10618c2ecf20Sopenharmony_ci ret = __xudc_ep0_queue(ep0, req); 10628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci return ret; 10658c2ecf20Sopenharmony_ci} 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci/** 10688c2ecf20Sopenharmony_ci * xudc_ep_queue - Adds the request to endpoint queue. 10698c2ecf20Sopenharmony_ci * @_ep: pointer to the usb endpoint structure. 10708c2ecf20Sopenharmony_ci * @_req: pointer to the usb request structure. 10718c2ecf20Sopenharmony_ci * @gfp_flags: Flags related to the request call. 10728c2ecf20Sopenharmony_ci * 10738c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 10748c2ecf20Sopenharmony_ci */ 10758c2ecf20Sopenharmony_cistatic int xudc_ep_queue(struct usb_ep *_ep, struct usb_request *_req, 10768c2ecf20Sopenharmony_ci gfp_t gfp_flags) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct xusb_req *req = to_xusb_req(_req); 10798c2ecf20Sopenharmony_ci struct xusb_ep *ep = to_xusb_ep(_ep); 10808c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 10818c2ecf20Sopenharmony_ci int ret; 10828c2ecf20Sopenharmony_ci unsigned long flags; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci if (!ep->desc) { 10858c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "%s: queuing request to disabled %s\n", 10868c2ecf20Sopenharmony_ci __func__, ep->name); 10878c2ecf20Sopenharmony_ci return -ESHUTDOWN; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) { 10918c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "%s, bogus device state\n", __func__); 10928c2ecf20Sopenharmony_ci return -EINVAL; 10938c2ecf20Sopenharmony_ci } 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci _req->status = -EINPROGRESS; 10988c2ecf20Sopenharmony_ci _req->actual = 0; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (udc->dma_enabled) { 11018c2ecf20Sopenharmony_ci ret = usb_gadget_map_request(&udc->gadget, &req->usb_req, 11028c2ecf20Sopenharmony_ci ep->is_in); 11038c2ecf20Sopenharmony_ci if (ret) { 11048c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "gadget_map failed ep%d\n", 11058c2ecf20Sopenharmony_ci ep->epnumber); 11068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 11078c2ecf20Sopenharmony_ci return -EAGAIN; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) { 11128c2ecf20Sopenharmony_ci if (ep->is_in) { 11138c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "xudc_write_fifo from ep_queue\n"); 11148c2ecf20Sopenharmony_ci if (!xudc_write_fifo(ep, req)) 11158c2ecf20Sopenharmony_ci req = NULL; 11168c2ecf20Sopenharmony_ci } else { 11178c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "xudc_read_fifo from ep_queue\n"); 11188c2ecf20Sopenharmony_ci if (!xudc_read_fifo(ep, req)) 11198c2ecf20Sopenharmony_ci req = NULL; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (req != NULL) 11248c2ecf20Sopenharmony_ci list_add_tail(&req->queue, &ep->queue); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 11278c2ecf20Sopenharmony_ci return 0; 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci/** 11318c2ecf20Sopenharmony_ci * xudc_ep_dequeue - Removes the request from the queue. 11328c2ecf20Sopenharmony_ci * @_ep: pointer to the usb device endpoint structure. 11338c2ecf20Sopenharmony_ci * @_req: pointer to the usb request structure. 11348c2ecf20Sopenharmony_ci * 11358c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_cistatic int xudc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct xusb_ep *ep = to_xusb_ep(_ep); 11408c2ecf20Sopenharmony_ci struct xusb_req *req = to_xusb_req(_req); 11418c2ecf20Sopenharmony_ci struct xusb_udc *udc = ep->udc; 11428c2ecf20Sopenharmony_ci unsigned long flags; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 11458c2ecf20Sopenharmony_ci /* Make sure it's actually queued on this endpoint */ 11468c2ecf20Sopenharmony_ci list_for_each_entry(req, &ep->queue, queue) { 11478c2ecf20Sopenharmony_ci if (&req->usb_req == _req) 11488c2ecf20Sopenharmony_ci break; 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci if (&req->usb_req != _req) { 11518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 11528c2ecf20Sopenharmony_ci return -EINVAL; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci xudc_done(ep, req, -ECONNRESET); 11558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci return 0; 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci/** 11618c2ecf20Sopenharmony_ci * xudc_ep0_enable - Enables the given endpoint. 11628c2ecf20Sopenharmony_ci * @ep: pointer to the usb endpoint structure. 11638c2ecf20Sopenharmony_ci * @desc: pointer to usb endpoint descriptor. 11648c2ecf20Sopenharmony_ci * 11658c2ecf20Sopenharmony_ci * Return: error always. 11668c2ecf20Sopenharmony_ci * 11678c2ecf20Sopenharmony_ci * endpoint 0 enable should not be called by gadget layer. 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_cistatic int xudc_ep0_enable(struct usb_ep *ep, 11708c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci return -EINVAL; 11738c2ecf20Sopenharmony_ci} 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci/** 11768c2ecf20Sopenharmony_ci * xudc_ep0_disable - Disables the given endpoint. 11778c2ecf20Sopenharmony_ci * @ep: pointer to the usb endpoint structure. 11788c2ecf20Sopenharmony_ci * 11798c2ecf20Sopenharmony_ci * Return: error always. 11808c2ecf20Sopenharmony_ci * 11818c2ecf20Sopenharmony_ci * endpoint 0 disable should not be called by gadget layer. 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_cistatic int xudc_ep0_disable(struct usb_ep *ep) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci return -EINVAL; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic const struct usb_ep_ops xusb_ep0_ops = { 11898c2ecf20Sopenharmony_ci .enable = xudc_ep0_enable, 11908c2ecf20Sopenharmony_ci .disable = xudc_ep0_disable, 11918c2ecf20Sopenharmony_ci .alloc_request = xudc_ep_alloc_request, 11928c2ecf20Sopenharmony_ci .free_request = xudc_free_request, 11938c2ecf20Sopenharmony_ci .queue = xudc_ep0_queue, 11948c2ecf20Sopenharmony_ci .dequeue = xudc_ep_dequeue, 11958c2ecf20Sopenharmony_ci .set_halt = xudc_ep_set_halt, 11968c2ecf20Sopenharmony_ci}; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic const struct usb_ep_ops xusb_ep_ops = { 11998c2ecf20Sopenharmony_ci .enable = xudc_ep_enable, 12008c2ecf20Sopenharmony_ci .disable = xudc_ep_disable, 12018c2ecf20Sopenharmony_ci .alloc_request = xudc_ep_alloc_request, 12028c2ecf20Sopenharmony_ci .free_request = xudc_free_request, 12038c2ecf20Sopenharmony_ci .queue = xudc_ep_queue, 12048c2ecf20Sopenharmony_ci .dequeue = xudc_ep_dequeue, 12058c2ecf20Sopenharmony_ci .set_halt = xudc_ep_set_halt, 12068c2ecf20Sopenharmony_ci}; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci/** 12098c2ecf20Sopenharmony_ci * xudc_get_frame - Reads the current usb frame number. 12108c2ecf20Sopenharmony_ci * @gadget: pointer to the usb gadget structure. 12118c2ecf20Sopenharmony_ci * 12128c2ecf20Sopenharmony_ci * Return: current frame number for success and error value on failure. 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_cistatic int xudc_get_frame(struct usb_gadget *gadget) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct xusb_udc *udc; 12178c2ecf20Sopenharmony_ci int frame; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (!gadget) 12208c2ecf20Sopenharmony_ci return -ENODEV; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci udc = to_udc(gadget); 12238c2ecf20Sopenharmony_ci frame = udc->read_fn(udc->addr + XUSB_FRAMENUM_OFFSET); 12248c2ecf20Sopenharmony_ci return frame; 12258c2ecf20Sopenharmony_ci} 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci/** 12288c2ecf20Sopenharmony_ci * xudc_wakeup - Send remote wakeup signal to host 12298c2ecf20Sopenharmony_ci * @gadget: pointer to the usb gadget structure. 12308c2ecf20Sopenharmony_ci * 12318c2ecf20Sopenharmony_ci * Return: 0 on success and error on failure 12328c2ecf20Sopenharmony_ci */ 12338c2ecf20Sopenharmony_cistatic int xudc_wakeup(struct usb_gadget *gadget) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct xusb_udc *udc = to_udc(gadget); 12368c2ecf20Sopenharmony_ci u32 crtlreg; 12378c2ecf20Sopenharmony_ci int status = -EINVAL; 12388c2ecf20Sopenharmony_ci unsigned long flags; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci /* Remote wake up not enabled by host */ 12438c2ecf20Sopenharmony_ci if (!udc->remote_wkp) 12448c2ecf20Sopenharmony_ci goto done; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET); 12478c2ecf20Sopenharmony_ci crtlreg |= XUSB_CONTROL_USB_RMTWAKE_MASK; 12488c2ecf20Sopenharmony_ci /* set remote wake up bit */ 12498c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); 12508c2ecf20Sopenharmony_ci /* 12518c2ecf20Sopenharmony_ci * wait for a while and reset remote wake up bit since this bit 12528c2ecf20Sopenharmony_ci * is not cleared by HW after sending remote wakeup to host. 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_ci mdelay(2); 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci crtlreg &= ~XUSB_CONTROL_USB_RMTWAKE_MASK; 12578c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); 12588c2ecf20Sopenharmony_ci status = 0; 12598c2ecf20Sopenharmony_cidone: 12608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 12618c2ecf20Sopenharmony_ci return status; 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci/** 12658c2ecf20Sopenharmony_ci * xudc_pullup - start/stop USB traffic 12668c2ecf20Sopenharmony_ci * @gadget: pointer to the usb gadget structure. 12678c2ecf20Sopenharmony_ci * @is_on: flag to start or stop 12688c2ecf20Sopenharmony_ci * 12698c2ecf20Sopenharmony_ci * Return: 0 always 12708c2ecf20Sopenharmony_ci * 12718c2ecf20Sopenharmony_ci * This function starts/stops SIE engine of IP based on is_on. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_cistatic int xudc_pullup(struct usb_gadget *gadget, int is_on) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct xusb_udc *udc = to_udc(gadget); 12768c2ecf20Sopenharmony_ci unsigned long flags; 12778c2ecf20Sopenharmony_ci u32 crtlreg; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci crtlreg = udc->read_fn(udc->addr + XUSB_CONTROL_OFFSET); 12828c2ecf20Sopenharmony_ci if (is_on) 12838c2ecf20Sopenharmony_ci crtlreg |= XUSB_CONTROL_USB_READY_MASK; 12848c2ecf20Sopenharmony_ci else 12858c2ecf20Sopenharmony_ci crtlreg &= ~XUSB_CONTROL_USB_READY_MASK; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_CONTROL_OFFSET, crtlreg); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci return 0; 12928c2ecf20Sopenharmony_ci} 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci/** 12958c2ecf20Sopenharmony_ci * xudc_eps_init - initialize endpoints. 12968c2ecf20Sopenharmony_ci * @udc: pointer to the usb device controller structure. 12978c2ecf20Sopenharmony_ci */ 12988c2ecf20Sopenharmony_cistatic void xudc_eps_init(struct xusb_udc *udc) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci u32 ep_number; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&udc->gadget.ep_list); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci for (ep_number = 0; ep_number < XUSB_MAX_ENDPOINTS; ep_number++) { 13058c2ecf20Sopenharmony_ci struct xusb_ep *ep = &udc->ep[ep_number]; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci if (ep_number) { 13088c2ecf20Sopenharmony_ci list_add_tail(&ep->ep_usb.ep_list, 13098c2ecf20Sopenharmony_ci &udc->gadget.ep_list); 13108c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep_usb, 13118c2ecf20Sopenharmony_ci (unsigned short) ~0); 13128c2ecf20Sopenharmony_ci snprintf(ep->name, EPNAME_SIZE, "ep%d", ep_number); 13138c2ecf20Sopenharmony_ci ep->ep_usb.name = ep->name; 13148c2ecf20Sopenharmony_ci ep->ep_usb.ops = &xusb_ep_ops; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci ep->ep_usb.caps.type_iso = true; 13178c2ecf20Sopenharmony_ci ep->ep_usb.caps.type_bulk = true; 13188c2ecf20Sopenharmony_ci ep->ep_usb.caps.type_int = true; 13198c2ecf20Sopenharmony_ci } else { 13208c2ecf20Sopenharmony_ci ep->ep_usb.name = ep0name; 13218c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep_usb, EP0_MAX_PACKET); 13228c2ecf20Sopenharmony_ci ep->ep_usb.ops = &xusb_ep0_ops; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci ep->ep_usb.caps.type_control = true; 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci ep->ep_usb.caps.dir_in = true; 13288c2ecf20Sopenharmony_ci ep->ep_usb.caps.dir_out = true; 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci ep->udc = udc; 13318c2ecf20Sopenharmony_ci ep->epnumber = ep_number; 13328c2ecf20Sopenharmony_ci ep->desc = NULL; 13338c2ecf20Sopenharmony_ci /* 13348c2ecf20Sopenharmony_ci * The configuration register address offset between 13358c2ecf20Sopenharmony_ci * each endpoint is 0x10. 13368c2ecf20Sopenharmony_ci */ 13378c2ecf20Sopenharmony_ci ep->offset = XUSB_EP0_CONFIG_OFFSET + (ep_number * 0x10); 13388c2ecf20Sopenharmony_ci ep->is_in = 0; 13398c2ecf20Sopenharmony_ci ep->is_iso = 0; 13408c2ecf20Sopenharmony_ci ep->maxpacket = 0; 13418c2ecf20Sopenharmony_ci xudc_epconfig(ep, udc); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* Initialize one queue per endpoint */ 13448c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep->queue); 13458c2ecf20Sopenharmony_ci } 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci/** 13498c2ecf20Sopenharmony_ci * xudc_stop_activity - Stops any further activity on the device. 13508c2ecf20Sopenharmony_ci * @udc: pointer to the usb device controller structure. 13518c2ecf20Sopenharmony_ci */ 13528c2ecf20Sopenharmony_cistatic void xudc_stop_activity(struct xusb_udc *udc) 13538c2ecf20Sopenharmony_ci{ 13548c2ecf20Sopenharmony_ci int i; 13558c2ecf20Sopenharmony_ci struct xusb_ep *ep; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) { 13588c2ecf20Sopenharmony_ci ep = &udc->ep[i]; 13598c2ecf20Sopenharmony_ci xudc_nuke(ep, -ESHUTDOWN); 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci/** 13648c2ecf20Sopenharmony_ci * xudc_start - Starts the device. 13658c2ecf20Sopenharmony_ci * @gadget: pointer to the usb gadget structure 13668c2ecf20Sopenharmony_ci * @driver: pointer to gadget driver structure 13678c2ecf20Sopenharmony_ci * 13688c2ecf20Sopenharmony_ci * Return: zero on success and error on failure 13698c2ecf20Sopenharmony_ci */ 13708c2ecf20Sopenharmony_cistatic int xudc_start(struct usb_gadget *gadget, 13718c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci struct xusb_udc *udc = to_udc(gadget); 13748c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; 13758c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc = &config_bulk_out_desc; 13768c2ecf20Sopenharmony_ci unsigned long flags; 13778c2ecf20Sopenharmony_ci int ret = 0; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (udc->driver) { 13828c2ecf20Sopenharmony_ci dev_err(udc->dev, "%s is already bound to %s\n", 13838c2ecf20Sopenharmony_ci udc->gadget.name, udc->driver->driver.name); 13848c2ecf20Sopenharmony_ci ret = -EBUSY; 13858c2ecf20Sopenharmony_ci goto err; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci /* hook up the driver */ 13898c2ecf20Sopenharmony_ci udc->driver = driver; 13908c2ecf20Sopenharmony_ci udc->gadget.speed = driver->max_speed; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci /* Enable the control endpoint. */ 13938c2ecf20Sopenharmony_ci ret = __xudc_ep_enable(ep0, desc); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci /* Set device address and remote wakeup to 0 */ 13968c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); 13978c2ecf20Sopenharmony_ci udc->remote_wkp = 0; 13988c2ecf20Sopenharmony_cierr: 13998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 14008c2ecf20Sopenharmony_ci return ret; 14018c2ecf20Sopenharmony_ci} 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci/** 14048c2ecf20Sopenharmony_ci * xudc_stop - stops the device. 14058c2ecf20Sopenharmony_ci * @gadget: pointer to the usb gadget structure 14068c2ecf20Sopenharmony_ci * 14078c2ecf20Sopenharmony_ci * Return: zero always 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_cistatic int xudc_stop(struct usb_gadget *gadget) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci struct xusb_udc *udc = to_udc(gadget); 14128c2ecf20Sopenharmony_ci unsigned long flags; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci udc->gadget.speed = USB_SPEED_UNKNOWN; 14178c2ecf20Sopenharmony_ci udc->driver = NULL; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci /* Set device address and remote wakeup to 0 */ 14208c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); 14218c2ecf20Sopenharmony_ci udc->remote_wkp = 0; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci xudc_stop_activity(udc); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci return 0; 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops xusb_udc_ops = { 14318c2ecf20Sopenharmony_ci .get_frame = xudc_get_frame, 14328c2ecf20Sopenharmony_ci .wakeup = xudc_wakeup, 14338c2ecf20Sopenharmony_ci .pullup = xudc_pullup, 14348c2ecf20Sopenharmony_ci .udc_start = xudc_start, 14358c2ecf20Sopenharmony_ci .udc_stop = xudc_stop, 14368c2ecf20Sopenharmony_ci}; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci/** 14398c2ecf20Sopenharmony_ci * xudc_clear_stall_all_ep - clears stall of every endpoint. 14408c2ecf20Sopenharmony_ci * @udc: pointer to the udc structure. 14418c2ecf20Sopenharmony_ci */ 14428c2ecf20Sopenharmony_cistatic void xudc_clear_stall_all_ep(struct xusb_udc *udc) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci struct xusb_ep *ep; 14458c2ecf20Sopenharmony_ci u32 epcfgreg; 14468c2ecf20Sopenharmony_ci int i; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci for (i = 0; i < XUSB_MAX_ENDPOINTS; i++) { 14498c2ecf20Sopenharmony_ci ep = &udc->ep[i]; 14508c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + ep->offset); 14518c2ecf20Sopenharmony_ci epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; 14528c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfgreg); 14538c2ecf20Sopenharmony_ci if (ep->epnumber) { 14548c2ecf20Sopenharmony_ci /* Reset the toggle bit.*/ 14558c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + ep->offset); 14568c2ecf20Sopenharmony_ci epcfgreg &= ~XUSB_EP_CFG_DATA_TOGGLE_MASK; 14578c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep->offset, epcfgreg); 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci} 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci/** 14638c2ecf20Sopenharmony_ci * xudc_startup_handler - The usb device controller interrupt handler. 14648c2ecf20Sopenharmony_ci * @udc: pointer to the udc structure. 14658c2ecf20Sopenharmony_ci * @intrstatus: The mask value containing the interrupt sources. 14668c2ecf20Sopenharmony_ci * 14678c2ecf20Sopenharmony_ci * This function handles the RESET,SUSPEND,RESUME and DISCONNECT interrupts. 14688c2ecf20Sopenharmony_ci */ 14698c2ecf20Sopenharmony_cistatic void xudc_startup_handler(struct xusb_udc *udc, u32 intrstatus) 14708c2ecf20Sopenharmony_ci{ 14718c2ecf20Sopenharmony_ci u32 intrreg; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_RESET_MASK) { 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Reset\n"); 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_HIGH_SPEED_MASK) 14788c2ecf20Sopenharmony_ci udc->gadget.speed = USB_SPEED_HIGH; 14798c2ecf20Sopenharmony_ci else 14808c2ecf20Sopenharmony_ci udc->gadget.speed = USB_SPEED_FULL; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci xudc_stop_activity(udc); 14838c2ecf20Sopenharmony_ci xudc_clear_stall_all_ep(udc); 14848c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci /* Set device address and remote wakeup to 0 */ 14878c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); 14888c2ecf20Sopenharmony_ci udc->remote_wkp = 0; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* Enable the suspend, resume and disconnect */ 14918c2ecf20Sopenharmony_ci intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); 14928c2ecf20Sopenharmony_ci intrreg |= XUSB_STATUS_SUSPEND_MASK | XUSB_STATUS_RESUME_MASK | 14938c2ecf20Sopenharmony_ci XUSB_STATUS_DISCONNECT_MASK; 14948c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_SUSPEND_MASK) { 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Suspend\n"); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci /* Enable the reset, resume and disconnect */ 15018c2ecf20Sopenharmony_ci intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); 15028c2ecf20Sopenharmony_ci intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK | 15038c2ecf20Sopenharmony_ci XUSB_STATUS_DISCONNECT_MASK; 15048c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci udc->usb_state = USB_STATE_SUSPENDED; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (udc->driver->suspend) { 15098c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 15108c2ecf20Sopenharmony_ci udc->driver->suspend(&udc->gadget); 15118c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_RESUME_MASK) { 15158c2ecf20Sopenharmony_ci bool condition = (udc->usb_state != USB_STATE_SUSPENDED); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci dev_WARN_ONCE(udc->dev, condition, 15188c2ecf20Sopenharmony_ci "Resume IRQ while not suspended\n"); 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Resume\n"); 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci /* Enable the reset, suspend and disconnect */ 15238c2ecf20Sopenharmony_ci intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); 15248c2ecf20Sopenharmony_ci intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_SUSPEND_MASK | 15258c2ecf20Sopenharmony_ci XUSB_STATUS_DISCONNECT_MASK; 15268c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci udc->usb_state = 0; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci if (udc->driver->resume) { 15318c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 15328c2ecf20Sopenharmony_ci udc->driver->resume(&udc->gadget); 15338c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_DISCONNECT_MASK) { 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Disconnect\n"); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci /* Enable the reset, resume and suspend */ 15418c2ecf20Sopenharmony_ci intrreg = udc->read_fn(udc->addr + XUSB_IER_OFFSET); 15428c2ecf20Sopenharmony_ci intrreg |= XUSB_STATUS_RESET_MASK | XUSB_STATUS_RESUME_MASK | 15438c2ecf20Sopenharmony_ci XUSB_STATUS_SUSPEND_MASK; 15448c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, intrreg); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci if (udc->driver && udc->driver->disconnect) { 15478c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 15488c2ecf20Sopenharmony_ci udc->driver->disconnect(&udc->gadget); 15498c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci/** 15558c2ecf20Sopenharmony_ci * xudc_ep0_stall - Stall endpoint zero. 15568c2ecf20Sopenharmony_ci * @udc: pointer to the udc structure. 15578c2ecf20Sopenharmony_ci * 15588c2ecf20Sopenharmony_ci * This function stalls endpoint zero. 15598c2ecf20Sopenharmony_ci */ 15608c2ecf20Sopenharmony_cistatic void xudc_ep0_stall(struct xusb_udc *udc) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci u32 epcfgreg; 15638c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO]; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + ep0->offset); 15668c2ecf20Sopenharmony_ci epcfgreg |= XUSB_EP_CFG_STALL_MASK; 15678c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep0->offset, epcfgreg); 15688c2ecf20Sopenharmony_ci} 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci/** 15718c2ecf20Sopenharmony_ci * xudc_setaddress - executes SET_ADDRESS command 15728c2ecf20Sopenharmony_ci * @udc: pointer to the udc structure. 15738c2ecf20Sopenharmony_ci * 15748c2ecf20Sopenharmony_ci * This function executes USB SET_ADDRESS command 15758c2ecf20Sopenharmony_ci */ 15768c2ecf20Sopenharmony_cistatic void xudc_setaddress(struct xusb_udc *udc) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[0]; 15798c2ecf20Sopenharmony_ci struct xusb_req *req = udc->req; 15808c2ecf20Sopenharmony_ci int ret; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci req->usb_req.length = 0; 15838c2ecf20Sopenharmony_ci ret = __xudc_ep0_queue(ep0, req); 15848c2ecf20Sopenharmony_ci if (ret == 0) 15858c2ecf20Sopenharmony_ci return; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci dev_err(udc->dev, "Can't respond to SET ADDRESS request\n"); 15888c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci/** 15928c2ecf20Sopenharmony_ci * xudc_getstatus - executes GET_STATUS command 15938c2ecf20Sopenharmony_ci * @udc: pointer to the udc structure. 15948c2ecf20Sopenharmony_ci * 15958c2ecf20Sopenharmony_ci * This function executes USB GET_STATUS command 15968c2ecf20Sopenharmony_ci */ 15978c2ecf20Sopenharmony_cistatic void xudc_getstatus(struct xusb_udc *udc) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[0]; 16008c2ecf20Sopenharmony_ci struct xusb_req *req = udc->req; 16018c2ecf20Sopenharmony_ci struct xusb_ep *target_ep; 16028c2ecf20Sopenharmony_ci u16 status = 0; 16038c2ecf20Sopenharmony_ci u32 epcfgreg; 16048c2ecf20Sopenharmony_ci int epnum; 16058c2ecf20Sopenharmony_ci u32 halt; 16068c2ecf20Sopenharmony_ci int ret; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci switch (udc->setup.bRequestType & USB_RECIP_MASK) { 16098c2ecf20Sopenharmony_ci case USB_RECIP_DEVICE: 16108c2ecf20Sopenharmony_ci /* Get device status */ 16118c2ecf20Sopenharmony_ci status = 1 << USB_DEVICE_SELF_POWERED; 16128c2ecf20Sopenharmony_ci if (udc->remote_wkp) 16138c2ecf20Sopenharmony_ci status |= (1 << USB_DEVICE_REMOTE_WAKEUP); 16148c2ecf20Sopenharmony_ci break; 16158c2ecf20Sopenharmony_ci case USB_RECIP_INTERFACE: 16168c2ecf20Sopenharmony_ci break; 16178c2ecf20Sopenharmony_ci case USB_RECIP_ENDPOINT: 16188c2ecf20Sopenharmony_ci epnum = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; 16198c2ecf20Sopenharmony_ci if (epnum >= XUSB_MAX_ENDPOINTS) 16208c2ecf20Sopenharmony_ci goto stall; 16218c2ecf20Sopenharmony_ci target_ep = &udc->ep[epnum]; 16228c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + target_ep->offset); 16238c2ecf20Sopenharmony_ci halt = epcfgreg & XUSB_EP_CFG_STALL_MASK; 16248c2ecf20Sopenharmony_ci if (udc->setup.wIndex & USB_DIR_IN) { 16258c2ecf20Sopenharmony_ci if (!target_ep->is_in) 16268c2ecf20Sopenharmony_ci goto stall; 16278c2ecf20Sopenharmony_ci } else { 16288c2ecf20Sopenharmony_ci if (target_ep->is_in) 16298c2ecf20Sopenharmony_ci goto stall; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci if (halt) 16328c2ecf20Sopenharmony_ci status = 1 << USB_ENDPOINT_HALT; 16338c2ecf20Sopenharmony_ci break; 16348c2ecf20Sopenharmony_ci default: 16358c2ecf20Sopenharmony_ci goto stall; 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci req->usb_req.length = 2; 16398c2ecf20Sopenharmony_ci *(u16 *)req->usb_req.buf = cpu_to_le16(status); 16408c2ecf20Sopenharmony_ci ret = __xudc_ep0_queue(ep0, req); 16418c2ecf20Sopenharmony_ci if (ret == 0) 16428c2ecf20Sopenharmony_ci return; 16438c2ecf20Sopenharmony_cistall: 16448c2ecf20Sopenharmony_ci dev_err(udc->dev, "Can't respond to getstatus request\n"); 16458c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 16468c2ecf20Sopenharmony_ci} 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci/** 16498c2ecf20Sopenharmony_ci * xudc_set_clear_feature - Executes the set feature and clear feature commands. 16508c2ecf20Sopenharmony_ci * @udc: pointer to the usb device controller structure. 16518c2ecf20Sopenharmony_ci * 16528c2ecf20Sopenharmony_ci * Processes the SET_FEATURE and CLEAR_FEATURE commands. 16538c2ecf20Sopenharmony_ci */ 16548c2ecf20Sopenharmony_cistatic void xudc_set_clear_feature(struct xusb_udc *udc) 16558c2ecf20Sopenharmony_ci{ 16568c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[0]; 16578c2ecf20Sopenharmony_ci struct xusb_req *req = udc->req; 16588c2ecf20Sopenharmony_ci struct xusb_ep *target_ep; 16598c2ecf20Sopenharmony_ci u8 endpoint; 16608c2ecf20Sopenharmony_ci u8 outinbit; 16618c2ecf20Sopenharmony_ci u32 epcfgreg; 16628c2ecf20Sopenharmony_ci int flag = (udc->setup.bRequest == USB_REQ_SET_FEATURE ? 1 : 0); 16638c2ecf20Sopenharmony_ci int ret; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ci switch (udc->setup.bRequestType) { 16668c2ecf20Sopenharmony_ci case USB_RECIP_DEVICE: 16678c2ecf20Sopenharmony_ci switch (udc->setup.wValue) { 16688c2ecf20Sopenharmony_ci case USB_DEVICE_TEST_MODE: 16698c2ecf20Sopenharmony_ci /* 16708c2ecf20Sopenharmony_ci * The Test Mode will be executed 16718c2ecf20Sopenharmony_ci * after the status phase. 16728c2ecf20Sopenharmony_ci */ 16738c2ecf20Sopenharmony_ci break; 16748c2ecf20Sopenharmony_ci case USB_DEVICE_REMOTE_WAKEUP: 16758c2ecf20Sopenharmony_ci if (flag) 16768c2ecf20Sopenharmony_ci udc->remote_wkp = 1; 16778c2ecf20Sopenharmony_ci else 16788c2ecf20Sopenharmony_ci udc->remote_wkp = 0; 16798c2ecf20Sopenharmony_ci break; 16808c2ecf20Sopenharmony_ci default: 16818c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 16828c2ecf20Sopenharmony_ci break; 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci break; 16858c2ecf20Sopenharmony_ci case USB_RECIP_ENDPOINT: 16868c2ecf20Sopenharmony_ci if (!udc->setup.wValue) { 16878c2ecf20Sopenharmony_ci endpoint = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK; 16888c2ecf20Sopenharmony_ci if (endpoint >= XUSB_MAX_ENDPOINTS) { 16898c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 16908c2ecf20Sopenharmony_ci return; 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci target_ep = &udc->ep[endpoint]; 16938c2ecf20Sopenharmony_ci outinbit = udc->setup.wIndex & USB_ENDPOINT_DIR_MASK; 16948c2ecf20Sopenharmony_ci outinbit = outinbit >> 7; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci /* Make sure direction matches.*/ 16978c2ecf20Sopenharmony_ci if (outinbit != target_ep->is_in) { 16988c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 16998c2ecf20Sopenharmony_ci return; 17008c2ecf20Sopenharmony_ci } 17018c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + target_ep->offset); 17028c2ecf20Sopenharmony_ci if (!endpoint) { 17038c2ecf20Sopenharmony_ci /* Clear the stall.*/ 17048c2ecf20Sopenharmony_ci epcfgreg &= ~XUSB_EP_CFG_STALL_MASK; 17058c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, 17068c2ecf20Sopenharmony_ci target_ep->offset, epcfgreg); 17078c2ecf20Sopenharmony_ci } else { 17088c2ecf20Sopenharmony_ci if (flag) { 17098c2ecf20Sopenharmony_ci epcfgreg |= XUSB_EP_CFG_STALL_MASK; 17108c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, 17118c2ecf20Sopenharmony_ci target_ep->offset, 17128c2ecf20Sopenharmony_ci epcfgreg); 17138c2ecf20Sopenharmony_ci } else { 17148c2ecf20Sopenharmony_ci /* Unstall the endpoint.*/ 17158c2ecf20Sopenharmony_ci epcfgreg &= ~(XUSB_EP_CFG_STALL_MASK | 17168c2ecf20Sopenharmony_ci XUSB_EP_CFG_DATA_TOGGLE_MASK); 17178c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, 17188c2ecf20Sopenharmony_ci target_ep->offset, 17198c2ecf20Sopenharmony_ci epcfgreg); 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci break; 17248c2ecf20Sopenharmony_ci default: 17258c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 17268c2ecf20Sopenharmony_ci return; 17278c2ecf20Sopenharmony_ci } 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci req->usb_req.length = 0; 17308c2ecf20Sopenharmony_ci ret = __xudc_ep0_queue(ep0, req); 17318c2ecf20Sopenharmony_ci if (ret == 0) 17328c2ecf20Sopenharmony_ci return; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n"); 17358c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 17368c2ecf20Sopenharmony_ci} 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci/** 17398c2ecf20Sopenharmony_ci * xudc_handle_setup - Processes the setup packet. 17408c2ecf20Sopenharmony_ci * @udc: pointer to the usb device controller structure. 17418c2ecf20Sopenharmony_ci * 17428c2ecf20Sopenharmony_ci * Process setup packet and delegate to gadget layer. 17438c2ecf20Sopenharmony_ci */ 17448c2ecf20Sopenharmony_cistatic void xudc_handle_setup(struct xusb_udc *udc) 17458c2ecf20Sopenharmony_ci __must_hold(&udc->lock) 17468c2ecf20Sopenharmony_ci{ 17478c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[0]; 17488c2ecf20Sopenharmony_ci struct usb_ctrlrequest setup; 17498c2ecf20Sopenharmony_ci u32 *ep0rambase; 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci /* Load up the chapter 9 command buffer.*/ 17528c2ecf20Sopenharmony_ci ep0rambase = (u32 __force *) (udc->addr + XUSB_SETUP_PKT_ADDR_OFFSET); 17538c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)&setup, ep0rambase, 8); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci udc->setup = setup; 17568c2ecf20Sopenharmony_ci udc->setup.wValue = cpu_to_le16(setup.wValue); 17578c2ecf20Sopenharmony_ci udc->setup.wIndex = cpu_to_le16(setup.wIndex); 17588c2ecf20Sopenharmony_ci udc->setup.wLength = cpu_to_le16(setup.wLength); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci /* Clear previous requests */ 17618c2ecf20Sopenharmony_ci xudc_nuke(ep0, -ECONNRESET); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci if (udc->setup.bRequestType & USB_DIR_IN) { 17648c2ecf20Sopenharmony_ci /* Execute the get command.*/ 17658c2ecf20Sopenharmony_ci udc->setupseqrx = STATUS_PHASE; 17668c2ecf20Sopenharmony_ci udc->setupseqtx = DATA_PHASE; 17678c2ecf20Sopenharmony_ci } else { 17688c2ecf20Sopenharmony_ci /* Execute the put command.*/ 17698c2ecf20Sopenharmony_ci udc->setupseqrx = DATA_PHASE; 17708c2ecf20Sopenharmony_ci udc->setupseqtx = STATUS_PHASE; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci switch (udc->setup.bRequest) { 17748c2ecf20Sopenharmony_ci case USB_REQ_GET_STATUS: 17758c2ecf20Sopenharmony_ci /* Data+Status phase form udc */ 17768c2ecf20Sopenharmony_ci if ((udc->setup.bRequestType & 17778c2ecf20Sopenharmony_ci (USB_DIR_IN | USB_TYPE_MASK)) != 17788c2ecf20Sopenharmony_ci (USB_DIR_IN | USB_TYPE_STANDARD)) 17798c2ecf20Sopenharmony_ci break; 17808c2ecf20Sopenharmony_ci xudc_getstatus(udc); 17818c2ecf20Sopenharmony_ci return; 17828c2ecf20Sopenharmony_ci case USB_REQ_SET_ADDRESS: 17838c2ecf20Sopenharmony_ci /* Status phase from udc */ 17848c2ecf20Sopenharmony_ci if (udc->setup.bRequestType != (USB_DIR_OUT | 17858c2ecf20Sopenharmony_ci USB_TYPE_STANDARD | USB_RECIP_DEVICE)) 17868c2ecf20Sopenharmony_ci break; 17878c2ecf20Sopenharmony_ci xudc_setaddress(udc); 17888c2ecf20Sopenharmony_ci return; 17898c2ecf20Sopenharmony_ci case USB_REQ_CLEAR_FEATURE: 17908c2ecf20Sopenharmony_ci case USB_REQ_SET_FEATURE: 17918c2ecf20Sopenharmony_ci /* Requests with no data phase, status phase from udc */ 17928c2ecf20Sopenharmony_ci if ((udc->setup.bRequestType & USB_TYPE_MASK) 17938c2ecf20Sopenharmony_ci != USB_TYPE_STANDARD) 17948c2ecf20Sopenharmony_ci break; 17958c2ecf20Sopenharmony_ci xudc_set_clear_feature(udc); 17968c2ecf20Sopenharmony_ci return; 17978c2ecf20Sopenharmony_ci default: 17988c2ecf20Sopenharmony_ci break; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 18028c2ecf20Sopenharmony_ci if (udc->driver->setup(&udc->gadget, &setup) < 0) 18038c2ecf20Sopenharmony_ci xudc_ep0_stall(udc); 18048c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci/** 18088c2ecf20Sopenharmony_ci * xudc_ep0_out - Processes the endpoint 0 OUT token. 18098c2ecf20Sopenharmony_ci * @udc: pointer to the usb device controller structure. 18108c2ecf20Sopenharmony_ci */ 18118c2ecf20Sopenharmony_cistatic void xudc_ep0_out(struct xusb_udc *udc) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[0]; 18148c2ecf20Sopenharmony_ci struct xusb_req *req; 18158c2ecf20Sopenharmony_ci u8 *ep0rambase; 18168c2ecf20Sopenharmony_ci unsigned int bytes_to_rx; 18178c2ecf20Sopenharmony_ci void *buffer; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci req = list_first_entry(&ep0->queue, struct xusb_req, queue); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci switch (udc->setupseqrx) { 18228c2ecf20Sopenharmony_ci case STATUS_PHASE: 18238c2ecf20Sopenharmony_ci /* 18248c2ecf20Sopenharmony_ci * This resets both state machines for the next 18258c2ecf20Sopenharmony_ci * Setup packet. 18268c2ecf20Sopenharmony_ci */ 18278c2ecf20Sopenharmony_ci udc->setupseqrx = SETUP_PHASE; 18288c2ecf20Sopenharmony_ci udc->setupseqtx = SETUP_PHASE; 18298c2ecf20Sopenharmony_ci req->usb_req.actual = req->usb_req.length; 18308c2ecf20Sopenharmony_ci xudc_done(ep0, req, 0); 18318c2ecf20Sopenharmony_ci break; 18328c2ecf20Sopenharmony_ci case DATA_PHASE: 18338c2ecf20Sopenharmony_ci bytes_to_rx = udc->read_fn(udc->addr + 18348c2ecf20Sopenharmony_ci XUSB_EP_BUF0COUNT_OFFSET); 18358c2ecf20Sopenharmony_ci /* Copy the data to be received from the DPRAM. */ 18368c2ecf20Sopenharmony_ci ep0rambase = (u8 __force *) (udc->addr + 18378c2ecf20Sopenharmony_ci (ep0->rambase << 2)); 18388c2ecf20Sopenharmony_ci buffer = req->usb_req.buf + req->usb_req.actual; 18398c2ecf20Sopenharmony_ci req->usb_req.actual = req->usb_req.actual + bytes_to_rx; 18408c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)buffer, ep0rambase, bytes_to_rx); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (req->usb_req.length == req->usb_req.actual) { 18438c2ecf20Sopenharmony_ci /* Data transfer completed get ready for Status stage */ 18448c2ecf20Sopenharmony_ci xudc_wrstatus(udc); 18458c2ecf20Sopenharmony_ci } else { 18468c2ecf20Sopenharmony_ci /* Enable EP0 buffer to receive data */ 18478c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, 0); 18488c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci break; 18518c2ecf20Sopenharmony_ci default: 18528c2ecf20Sopenharmony_ci break; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci/** 18578c2ecf20Sopenharmony_ci * xudc_ep0_in - Processes the endpoint 0 IN token. 18588c2ecf20Sopenharmony_ci * @udc: pointer to the usb device controller structure. 18598c2ecf20Sopenharmony_ci */ 18608c2ecf20Sopenharmony_cistatic void xudc_ep0_in(struct xusb_udc *udc) 18618c2ecf20Sopenharmony_ci{ 18628c2ecf20Sopenharmony_ci struct xusb_ep *ep0 = &udc->ep[0]; 18638c2ecf20Sopenharmony_ci struct xusb_req *req; 18648c2ecf20Sopenharmony_ci unsigned int bytes_to_tx; 18658c2ecf20Sopenharmony_ci void *buffer; 18668c2ecf20Sopenharmony_ci u32 epcfgreg; 18678c2ecf20Sopenharmony_ci u16 count = 0; 18688c2ecf20Sopenharmony_ci u16 length; 18698c2ecf20Sopenharmony_ci u8 *ep0rambase; 18708c2ecf20Sopenharmony_ci u8 test_mode = udc->setup.wIndex >> 8; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci req = list_first_entry(&ep0->queue, struct xusb_req, queue); 18738c2ecf20Sopenharmony_ci bytes_to_tx = req->usb_req.length - req->usb_req.actual; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci switch (udc->setupseqtx) { 18768c2ecf20Sopenharmony_ci case STATUS_PHASE: 18778c2ecf20Sopenharmony_ci switch (udc->setup.bRequest) { 18788c2ecf20Sopenharmony_ci case USB_REQ_SET_ADDRESS: 18798c2ecf20Sopenharmony_ci /* Set the address of the device.*/ 18808c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 18818c2ecf20Sopenharmony_ci udc->setup.wValue); 18828c2ecf20Sopenharmony_ci break; 18838c2ecf20Sopenharmony_ci case USB_REQ_SET_FEATURE: 18848c2ecf20Sopenharmony_ci if (udc->setup.bRequestType == 18858c2ecf20Sopenharmony_ci USB_RECIP_DEVICE) { 18868c2ecf20Sopenharmony_ci if (udc->setup.wValue == 18878c2ecf20Sopenharmony_ci USB_DEVICE_TEST_MODE) 18888c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, 18898c2ecf20Sopenharmony_ci XUSB_TESTMODE_OFFSET, 18908c2ecf20Sopenharmony_ci test_mode); 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci break; 18938c2ecf20Sopenharmony_ci } 18948c2ecf20Sopenharmony_ci req->usb_req.actual = req->usb_req.length; 18958c2ecf20Sopenharmony_ci xudc_done(ep0, req, 0); 18968c2ecf20Sopenharmony_ci break; 18978c2ecf20Sopenharmony_ci case DATA_PHASE: 18988c2ecf20Sopenharmony_ci if (!bytes_to_tx) { 18998c2ecf20Sopenharmony_ci /* 19008c2ecf20Sopenharmony_ci * We're done with data transfer, next 19018c2ecf20Sopenharmony_ci * will be zero length OUT with data toggle of 19028c2ecf20Sopenharmony_ci * 1. Setup data_toggle. 19038c2ecf20Sopenharmony_ci */ 19048c2ecf20Sopenharmony_ci epcfgreg = udc->read_fn(udc->addr + ep0->offset); 19058c2ecf20Sopenharmony_ci epcfgreg |= XUSB_EP_CFG_DATA_TOGGLE_MASK; 19068c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, ep0->offset, epcfgreg); 19078c2ecf20Sopenharmony_ci udc->setupseqtx = STATUS_PHASE; 19088c2ecf20Sopenharmony_ci } else { 19098c2ecf20Sopenharmony_ci length = count = min_t(u32, bytes_to_tx, 19108c2ecf20Sopenharmony_ci EP0_MAX_PACKET); 19118c2ecf20Sopenharmony_ci /* Copy the data to be transmitted into the DPRAM. */ 19128c2ecf20Sopenharmony_ci ep0rambase = (u8 __force *) (udc->addr + 19138c2ecf20Sopenharmony_ci (ep0->rambase << 2)); 19148c2ecf20Sopenharmony_ci buffer = req->usb_req.buf + req->usb_req.actual; 19158c2ecf20Sopenharmony_ci req->usb_req.actual = req->usb_req.actual + length; 19168c2ecf20Sopenharmony_ci memcpy_toio((void __iomem *)ep0rambase, buffer, length); 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_EP_BUF0COUNT_OFFSET, count); 19198c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_BUFFREADY_OFFSET, 1); 19208c2ecf20Sopenharmony_ci break; 19218c2ecf20Sopenharmony_ci default: 19228c2ecf20Sopenharmony_ci break; 19238c2ecf20Sopenharmony_ci } 19248c2ecf20Sopenharmony_ci} 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci/** 19278c2ecf20Sopenharmony_ci * xudc_ctrl_ep_handler - Endpoint 0 interrupt handler. 19288c2ecf20Sopenharmony_ci * @udc: pointer to the udc structure. 19298c2ecf20Sopenharmony_ci * @intrstatus: It's the mask value for the interrupt sources on endpoint 0. 19308c2ecf20Sopenharmony_ci * 19318c2ecf20Sopenharmony_ci * Processes the commands received during enumeration phase. 19328c2ecf20Sopenharmony_ci */ 19338c2ecf20Sopenharmony_cistatic void xudc_ctrl_ep_handler(struct xusb_udc *udc, u32 intrstatus) 19348c2ecf20Sopenharmony_ci{ 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_SETUP_PACKET_MASK) { 19378c2ecf20Sopenharmony_ci xudc_handle_setup(udc); 19388c2ecf20Sopenharmony_ci } else { 19398c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_FIFO_BUFF_RDY_MASK) 19408c2ecf20Sopenharmony_ci xudc_ep0_out(udc); 19418c2ecf20Sopenharmony_ci else if (intrstatus & XUSB_STATUS_FIFO_BUFF_FREE_MASK) 19428c2ecf20Sopenharmony_ci xudc_ep0_in(udc); 19438c2ecf20Sopenharmony_ci } 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci/** 19478c2ecf20Sopenharmony_ci * xudc_nonctrl_ep_handler - Non control endpoint interrupt handler. 19488c2ecf20Sopenharmony_ci * @udc: pointer to the udc structure. 19498c2ecf20Sopenharmony_ci * @epnum: End point number for which the interrupt is to be processed 19508c2ecf20Sopenharmony_ci * @intrstatus: mask value for interrupt sources of endpoints other 19518c2ecf20Sopenharmony_ci * than endpoint 0. 19528c2ecf20Sopenharmony_ci * 19538c2ecf20Sopenharmony_ci * Processes the buffer completion interrupts. 19548c2ecf20Sopenharmony_ci */ 19558c2ecf20Sopenharmony_cistatic void xudc_nonctrl_ep_handler(struct xusb_udc *udc, u8 epnum, 19568c2ecf20Sopenharmony_ci u32 intrstatus) 19578c2ecf20Sopenharmony_ci{ 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci struct xusb_req *req; 19608c2ecf20Sopenharmony_ci struct xusb_ep *ep; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci ep = &udc->ep[epnum]; 19638c2ecf20Sopenharmony_ci /* Process the End point interrupts.*/ 19648c2ecf20Sopenharmony_ci if (intrstatus & (XUSB_STATUS_EP0_BUFF1_COMP_MASK << epnum)) 19658c2ecf20Sopenharmony_ci ep->buffer0ready = 0; 19668c2ecf20Sopenharmony_ci if (intrstatus & (XUSB_STATUS_EP0_BUFF2_COMP_MASK << epnum)) 19678c2ecf20Sopenharmony_ci ep->buffer1ready = 0; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) 19708c2ecf20Sopenharmony_ci return; 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci req = list_first_entry(&ep->queue, struct xusb_req, queue); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci if (ep->is_in) 19758c2ecf20Sopenharmony_ci xudc_write_fifo(ep, req); 19768c2ecf20Sopenharmony_ci else 19778c2ecf20Sopenharmony_ci xudc_read_fifo(ep, req); 19788c2ecf20Sopenharmony_ci} 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci/** 19818c2ecf20Sopenharmony_ci * xudc_irq - The main interrupt handler. 19828c2ecf20Sopenharmony_ci * @irq: The interrupt number. 19838c2ecf20Sopenharmony_ci * @_udc: pointer to the usb device controller structure. 19848c2ecf20Sopenharmony_ci * 19858c2ecf20Sopenharmony_ci * Return: IRQ_HANDLED after the interrupt is handled. 19868c2ecf20Sopenharmony_ci */ 19878c2ecf20Sopenharmony_cistatic irqreturn_t xudc_irq(int irq, void *_udc) 19888c2ecf20Sopenharmony_ci{ 19898c2ecf20Sopenharmony_ci struct xusb_udc *udc = _udc; 19908c2ecf20Sopenharmony_ci u32 intrstatus; 19918c2ecf20Sopenharmony_ci u32 ier; 19928c2ecf20Sopenharmony_ci u8 index; 19938c2ecf20Sopenharmony_ci u32 bufintr; 19948c2ecf20Sopenharmony_ci unsigned long flags; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci /* 19998c2ecf20Sopenharmony_ci * Event interrupts are level sensitive hence first disable 20008c2ecf20Sopenharmony_ci * IER, read ISR and figure out active interrupts. 20018c2ecf20Sopenharmony_ci */ 20028c2ecf20Sopenharmony_ci ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); 20038c2ecf20Sopenharmony_ci ier &= ~XUSB_STATUS_INTR_EVENT_MASK; 20048c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci /* Read the Interrupt Status Register.*/ 20078c2ecf20Sopenharmony_ci intrstatus = udc->read_fn(udc->addr + XUSB_STATUS_OFFSET); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci /* Call the handler for the event interrupt.*/ 20108c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_INTR_EVENT_MASK) { 20118c2ecf20Sopenharmony_ci /* 20128c2ecf20Sopenharmony_ci * Check if there is any action to be done for : 20138c2ecf20Sopenharmony_ci * - USB Reset received {XUSB_STATUS_RESET_MASK} 20148c2ecf20Sopenharmony_ci * - USB Suspend received {XUSB_STATUS_SUSPEND_MASK} 20158c2ecf20Sopenharmony_ci * - USB Resume received {XUSB_STATUS_RESUME_MASK} 20168c2ecf20Sopenharmony_ci * - USB Disconnect received {XUSB_STATUS_DISCONNECT_MASK} 20178c2ecf20Sopenharmony_ci */ 20188c2ecf20Sopenharmony_ci xudc_startup_handler(udc, intrstatus); 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* Check the buffer completion interrupts */ 20228c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK) { 20238c2ecf20Sopenharmony_ci /* Enable Reset, Suspend, Resume and Disconnect */ 20248c2ecf20Sopenharmony_ci ier = udc->read_fn(udc->addr + XUSB_IER_OFFSET); 20258c2ecf20Sopenharmony_ci ier |= XUSB_STATUS_INTR_EVENT_MASK; 20268c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci if (intrstatus & XUSB_STATUS_EP0_BUFF1_COMP_MASK) 20298c2ecf20Sopenharmony_ci xudc_ctrl_ep_handler(udc, intrstatus); 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci for (index = 1; index < 8; index++) { 20328c2ecf20Sopenharmony_ci bufintr = ((intrstatus & 20338c2ecf20Sopenharmony_ci (XUSB_STATUS_EP1_BUFF1_COMP_MASK << 20348c2ecf20Sopenharmony_ci (index - 1))) || (intrstatus & 20358c2ecf20Sopenharmony_ci (XUSB_STATUS_EP1_BUFF2_COMP_MASK << 20368c2ecf20Sopenharmony_ci (index - 1)))); 20378c2ecf20Sopenharmony_ci if (bufintr) { 20388c2ecf20Sopenharmony_ci xudc_nonctrl_ep_handler(udc, index, 20398c2ecf20Sopenharmony_ci intrstatus); 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci } 20428c2ecf20Sopenharmony_ci } 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 20458c2ecf20Sopenharmony_ci return IRQ_HANDLED; 20468c2ecf20Sopenharmony_ci} 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci/** 20498c2ecf20Sopenharmony_ci * xudc_probe - The device probe function for driver initialization. 20508c2ecf20Sopenharmony_ci * @pdev: pointer to the platform device structure. 20518c2ecf20Sopenharmony_ci * 20528c2ecf20Sopenharmony_ci * Return: 0 for success and error value on failure 20538c2ecf20Sopenharmony_ci */ 20548c2ecf20Sopenharmony_cistatic int xudc_probe(struct platform_device *pdev) 20558c2ecf20Sopenharmony_ci{ 20568c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 20578c2ecf20Sopenharmony_ci struct resource *res; 20588c2ecf20Sopenharmony_ci struct xusb_udc *udc; 20598c2ecf20Sopenharmony_ci int irq; 20608c2ecf20Sopenharmony_ci int ret; 20618c2ecf20Sopenharmony_ci u32 ier; 20628c2ecf20Sopenharmony_ci u8 *buff; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL); 20658c2ecf20Sopenharmony_ci if (!udc) 20668c2ecf20Sopenharmony_ci return -ENOMEM; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci /* Create a dummy request for GET_STATUS, SET_ADDRESS */ 20698c2ecf20Sopenharmony_ci udc->req = devm_kzalloc(&pdev->dev, sizeof(struct xusb_req), 20708c2ecf20Sopenharmony_ci GFP_KERNEL); 20718c2ecf20Sopenharmony_ci if (!udc->req) 20728c2ecf20Sopenharmony_ci return -ENOMEM; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci buff = devm_kzalloc(&pdev->dev, STATUSBUFF_SIZE, GFP_KERNEL); 20758c2ecf20Sopenharmony_ci if (!buff) 20768c2ecf20Sopenharmony_ci return -ENOMEM; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci udc->req->usb_req.buf = buff; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci /* Map the registers */ 20818c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 20828c2ecf20Sopenharmony_ci udc->addr = devm_ioremap_resource(&pdev->dev, res); 20838c2ecf20Sopenharmony_ci if (IS_ERR(udc->addr)) 20848c2ecf20Sopenharmony_ci return PTR_ERR(udc->addr); 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 20878c2ecf20Sopenharmony_ci if (irq < 0) 20888c2ecf20Sopenharmony_ci return irq; 20898c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, xudc_irq, 0, 20908c2ecf20Sopenharmony_ci dev_name(&pdev->dev), udc); 20918c2ecf20Sopenharmony_ci if (ret < 0) { 20928c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "unable to request irq %d", irq); 20938c2ecf20Sopenharmony_ci goto fail; 20948c2ecf20Sopenharmony_ci } 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci udc->dma_enabled = of_property_read_bool(np, "xlnx,has-builtin-dma"); 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci /* Setup gadget structure */ 20998c2ecf20Sopenharmony_ci udc->gadget.ops = &xusb_udc_ops; 21008c2ecf20Sopenharmony_ci udc->gadget.max_speed = USB_SPEED_HIGH; 21018c2ecf20Sopenharmony_ci udc->gadget.speed = USB_SPEED_UNKNOWN; 21028c2ecf20Sopenharmony_ci udc->gadget.ep0 = &udc->ep[XUSB_EP_NUMBER_ZERO].ep_usb; 21038c2ecf20Sopenharmony_ci udc->gadget.name = driver_name; 21048c2ecf20Sopenharmony_ci 21058c2ecf20Sopenharmony_ci spin_lock_init(&udc->lock); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci /* Check for IP endianness */ 21088c2ecf20Sopenharmony_ci udc->write_fn = xudc_write32_be; 21098c2ecf20Sopenharmony_ci udc->read_fn = xudc_read32_be; 21108c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, USB_TEST_J); 21118c2ecf20Sopenharmony_ci if ((udc->read_fn(udc->addr + XUSB_TESTMODE_OFFSET)) 21128c2ecf20Sopenharmony_ci != USB_TEST_J) { 21138c2ecf20Sopenharmony_ci udc->write_fn = xudc_write32; 21148c2ecf20Sopenharmony_ci udc->read_fn = xudc_read32; 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_TESTMODE_OFFSET, 0); 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci xudc_eps_init(udc); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci /* Set device address to 0.*/ 21218c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); 21228c2ecf20Sopenharmony_ci 21238c2ecf20Sopenharmony_ci ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); 21248c2ecf20Sopenharmony_ci if (ret) 21258c2ecf20Sopenharmony_ci goto fail; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci udc->dev = &udc->gadget.dev; 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci /* Enable the interrupts.*/ 21308c2ecf20Sopenharmony_ci ier = XUSB_STATUS_GLOBAL_INTR_MASK | XUSB_STATUS_INTR_EVENT_MASK | 21318c2ecf20Sopenharmony_ci XUSB_STATUS_FIFO_BUFF_RDY_MASK | XUSB_STATUS_FIFO_BUFF_FREE_MASK | 21328c2ecf20Sopenharmony_ci XUSB_STATUS_SETUP_PACKET_MASK | 21338c2ecf20Sopenharmony_ci XUSB_STATUS_INTR_BUFF_COMP_ALL_MASK; 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci udc->write_fn(udc->addr, XUSB_IER_OFFSET, ier); 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, udc); 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci dev_vdbg(&pdev->dev, "%s at 0x%08X mapped to %p %s\n", 21408c2ecf20Sopenharmony_ci driver_name, (u32)res->start, udc->addr, 21418c2ecf20Sopenharmony_ci udc->dma_enabled ? "with DMA" : "without DMA"); 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci return 0; 21448c2ecf20Sopenharmony_cifail: 21458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "probe failed, %d\n", ret); 21468c2ecf20Sopenharmony_ci return ret; 21478c2ecf20Sopenharmony_ci} 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci/** 21508c2ecf20Sopenharmony_ci * xudc_remove - Releases the resources allocated during the initialization. 21518c2ecf20Sopenharmony_ci * @pdev: pointer to the platform device structure. 21528c2ecf20Sopenharmony_ci * 21538c2ecf20Sopenharmony_ci * Return: 0 always 21548c2ecf20Sopenharmony_ci */ 21558c2ecf20Sopenharmony_cistatic int xudc_remove(struct platform_device *pdev) 21568c2ecf20Sopenharmony_ci{ 21578c2ecf20Sopenharmony_ci struct xusb_udc *udc = platform_get_drvdata(pdev); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci usb_del_gadget_udc(&udc->gadget); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci return 0; 21628c2ecf20Sopenharmony_ci} 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci/* Match table for of_platform binding */ 21658c2ecf20Sopenharmony_cistatic const struct of_device_id usb_of_match[] = { 21668c2ecf20Sopenharmony_ci { .compatible = "xlnx,usb2-device-4.00.a", }, 21678c2ecf20Sopenharmony_ci { /* end of list */ }, 21688c2ecf20Sopenharmony_ci}; 21698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, usb_of_match); 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_cistatic struct platform_driver xudc_driver = { 21728c2ecf20Sopenharmony_ci .driver = { 21738c2ecf20Sopenharmony_ci .name = driver_name, 21748c2ecf20Sopenharmony_ci .of_match_table = usb_of_match, 21758c2ecf20Sopenharmony_ci }, 21768c2ecf20Sopenharmony_ci .probe = xudc_probe, 21778c2ecf20Sopenharmony_ci .remove = xudc_remove, 21788c2ecf20Sopenharmony_ci}; 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_cimodule_platform_driver(xudc_driver); 21818c2ecf20Sopenharmony_ci 21828c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx udc driver"); 21838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx, Inc"); 21848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2185