18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * USB Gadget driver for LPC32xx 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Kevin Wells <kevin.wells@nxp.com> 78c2ecf20Sopenharmony_ci * Mike James 88c2ecf20Sopenharmony_ci * Roland Stigge <stigge@antcom.de> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2006 Philips Semiconductors 118c2ecf20Sopenharmony_ci * Copyright (C) 2009 NXP Semiconductors 128c2ecf20Sopenharmony_ci * Copyright (C) 2012 Roland Stigge 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Note: This driver is based on original work done by Mike James for 158c2ecf20Sopenharmony_ci * the LPC3180. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/clk.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 218c2ecf20Sopenharmony_ci#include <linux/dmapool.h> 228c2ecf20Sopenharmony_ci#include <linux/i2c.h> 238c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/of.h> 268c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 278c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 288c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 318c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 328c2ecf20Sopenharmony_ci#include <linux/usb/isp1301.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FILES 358c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 368c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * USB device configuration structure 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_citypedef void (*usc_chg_event)(int); 438c2ecf20Sopenharmony_cistruct lpc32xx_usbd_cfg { 448c2ecf20Sopenharmony_ci int vbus_drv_pol; /* 0=active low drive for VBUS via ISP1301 */ 458c2ecf20Sopenharmony_ci usc_chg_event conn_chgb; /* Connection change event (optional) */ 468c2ecf20Sopenharmony_ci usc_chg_event susp_chgb; /* Suspend/resume event (optional) */ 478c2ecf20Sopenharmony_ci usc_chg_event rmwk_chgb; /* Enable/disable remote wakeup */ 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* 518c2ecf20Sopenharmony_ci * controller driver data structures 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* 16 endpoints (not to be confused with 32 hardware endpoints) */ 558c2ecf20Sopenharmony_ci#define NUM_ENDPOINTS 16 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* 588c2ecf20Sopenharmony_ci * IRQ indices make reading the code a little easier 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci#define IRQ_USB_LP 0 618c2ecf20Sopenharmony_ci#define IRQ_USB_HP 1 628c2ecf20Sopenharmony_ci#define IRQ_USB_DEVDMA 2 638c2ecf20Sopenharmony_ci#define IRQ_USB_ATX 3 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define EP_OUT 0 /* RX (from host) */ 668c2ecf20Sopenharmony_ci#define EP_IN 1 /* TX (to host) */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Returns the interrupt mask for the selected hardware endpoint */ 698c2ecf20Sopenharmony_ci#define EP_MASK_SEL(ep, dir) (1 << (((ep) * 2) + dir)) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define EP_INT_TYPE 0 728c2ecf20Sopenharmony_ci#define EP_ISO_TYPE 1 738c2ecf20Sopenharmony_ci#define EP_BLK_TYPE 2 748c2ecf20Sopenharmony_ci#define EP_CTL_TYPE 3 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* EP0 states */ 778c2ecf20Sopenharmony_ci#define WAIT_FOR_SETUP 0 /* Wait for setup packet */ 788c2ecf20Sopenharmony_ci#define DATA_IN 1 /* Expect dev->host transfer */ 798c2ecf20Sopenharmony_ci#define DATA_OUT 2 /* Expect host->dev transfer */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* DD (DMA Descriptor) structure, requires word alignment, this is already 828c2ecf20Sopenharmony_ci * defined in the LPC32XX USB device header file, but this version is slightly 838c2ecf20Sopenharmony_ci * modified to tag some work data with each DMA descriptor. */ 848c2ecf20Sopenharmony_cistruct lpc32xx_usbd_dd_gad { 858c2ecf20Sopenharmony_ci u32 dd_next_phy; 868c2ecf20Sopenharmony_ci u32 dd_setup; 878c2ecf20Sopenharmony_ci u32 dd_buffer_addr; 888c2ecf20Sopenharmony_ci u32 dd_status; 898c2ecf20Sopenharmony_ci u32 dd_iso_ps_mem_addr; 908c2ecf20Sopenharmony_ci u32 this_dma; 918c2ecf20Sopenharmony_ci u32 iso_status[6]; /* 5 spare */ 928c2ecf20Sopenharmony_ci u32 dd_next_v; 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* 968c2ecf20Sopenharmony_ci * Logical endpoint structure 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_cistruct lpc32xx_ep { 998c2ecf20Sopenharmony_ci struct usb_ep ep; 1008c2ecf20Sopenharmony_ci struct list_head queue; 1018c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci u32 hwep_num_base; /* Physical hardware EP */ 1048c2ecf20Sopenharmony_ci u32 hwep_num; /* Maps to hardware endpoint */ 1058c2ecf20Sopenharmony_ci u32 maxpacket; 1068c2ecf20Sopenharmony_ci u32 lep; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci bool is_in; 1098c2ecf20Sopenharmony_ci bool req_pending; 1108c2ecf20Sopenharmony_ci u32 eptype; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci u32 totalints; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci bool wedge; 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cienum atx_type { 1188c2ecf20Sopenharmony_ci ISP1301, 1198c2ecf20Sopenharmony_ci STOTG04, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* 1238c2ecf20Sopenharmony_ci * Common UDC structure 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistruct lpc32xx_udc { 1268c2ecf20Sopenharmony_ci struct usb_gadget gadget; 1278c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver; 1288c2ecf20Sopenharmony_ci struct platform_device *pdev; 1298c2ecf20Sopenharmony_ci struct device *dev; 1308c2ecf20Sopenharmony_ci struct dentry *pde; 1318c2ecf20Sopenharmony_ci spinlock_t lock; 1328c2ecf20Sopenharmony_ci struct i2c_client *isp1301_i2c_client; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Board and device specific */ 1358c2ecf20Sopenharmony_ci struct lpc32xx_usbd_cfg *board; 1368c2ecf20Sopenharmony_ci void __iomem *udp_baseaddr; 1378c2ecf20Sopenharmony_ci int udp_irq[4]; 1388c2ecf20Sopenharmony_ci struct clk *usb_slv_clk; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* DMA support */ 1418c2ecf20Sopenharmony_ci u32 *udca_v_base; 1428c2ecf20Sopenharmony_ci u32 udca_p_base; 1438c2ecf20Sopenharmony_ci struct dma_pool *dd_cache; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Common EP and control data */ 1468c2ecf20Sopenharmony_ci u32 enabled_devints; 1478c2ecf20Sopenharmony_ci u32 enabled_hwepints; 1488c2ecf20Sopenharmony_ci u32 dev_status; 1498c2ecf20Sopenharmony_ci u32 realized_eps; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* VBUS detection, pullup, and power flags */ 1528c2ecf20Sopenharmony_ci u8 vbus; 1538c2ecf20Sopenharmony_ci u8 last_vbus; 1548c2ecf20Sopenharmony_ci int pullup; 1558c2ecf20Sopenharmony_ci int poweron; 1568c2ecf20Sopenharmony_ci enum atx_type atx; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* Work queues related to I2C support */ 1598c2ecf20Sopenharmony_ci struct work_struct pullup_job; 1608c2ecf20Sopenharmony_ci struct work_struct power_job; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* USB device peripheral - various */ 1638c2ecf20Sopenharmony_ci struct lpc32xx_ep ep[NUM_ENDPOINTS]; 1648c2ecf20Sopenharmony_ci bool enabled; 1658c2ecf20Sopenharmony_ci bool clocked; 1668c2ecf20Sopenharmony_ci bool suspended; 1678c2ecf20Sopenharmony_ci int ep0state; 1688c2ecf20Sopenharmony_ci atomic_t enabled_ep_cnt; 1698c2ecf20Sopenharmony_ci wait_queue_head_t ep_disable_wait_queue; 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/* 1738c2ecf20Sopenharmony_ci * Endpoint request 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_cistruct lpc32xx_request { 1768c2ecf20Sopenharmony_ci struct usb_request req; 1778c2ecf20Sopenharmony_ci struct list_head queue; 1788c2ecf20Sopenharmony_ci struct lpc32xx_usbd_dd_gad *dd_desc_ptr; 1798c2ecf20Sopenharmony_ci bool mapped; 1808c2ecf20Sopenharmony_ci bool send_zlp; 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic inline struct lpc32xx_udc *to_udc(struct usb_gadget *g) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci return container_of(g, struct lpc32xx_udc, gadget); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#define ep_dbg(epp, fmt, arg...) \ 1898c2ecf20Sopenharmony_ci dev_dbg(epp->udc->dev, "%s: " fmt, __func__, ## arg) 1908c2ecf20Sopenharmony_ci#define ep_err(epp, fmt, arg...) \ 1918c2ecf20Sopenharmony_ci dev_err(epp->udc->dev, "%s: " fmt, __func__, ## arg) 1928c2ecf20Sopenharmony_ci#define ep_info(epp, fmt, arg...) \ 1938c2ecf20Sopenharmony_ci dev_info(epp->udc->dev, "%s: " fmt, __func__, ## arg) 1948c2ecf20Sopenharmony_ci#define ep_warn(epp, fmt, arg...) \ 1958c2ecf20Sopenharmony_ci dev_warn(epp->udc->dev, "%s:" fmt, __func__, ## arg) 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci#define UDCA_BUFF_SIZE (128) 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/********************************************************************** 2008c2ecf20Sopenharmony_ci * USB device controller register offsets 2018c2ecf20Sopenharmony_ci **********************************************************************/ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci#define USBD_DEVINTST(x) ((x) + 0x200) 2048c2ecf20Sopenharmony_ci#define USBD_DEVINTEN(x) ((x) + 0x204) 2058c2ecf20Sopenharmony_ci#define USBD_DEVINTCLR(x) ((x) + 0x208) 2068c2ecf20Sopenharmony_ci#define USBD_DEVINTSET(x) ((x) + 0x20C) 2078c2ecf20Sopenharmony_ci#define USBD_CMDCODE(x) ((x) + 0x210) 2088c2ecf20Sopenharmony_ci#define USBD_CMDDATA(x) ((x) + 0x214) 2098c2ecf20Sopenharmony_ci#define USBD_RXDATA(x) ((x) + 0x218) 2108c2ecf20Sopenharmony_ci#define USBD_TXDATA(x) ((x) + 0x21C) 2118c2ecf20Sopenharmony_ci#define USBD_RXPLEN(x) ((x) + 0x220) 2128c2ecf20Sopenharmony_ci#define USBD_TXPLEN(x) ((x) + 0x224) 2138c2ecf20Sopenharmony_ci#define USBD_CTRL(x) ((x) + 0x228) 2148c2ecf20Sopenharmony_ci#define USBD_DEVINTPRI(x) ((x) + 0x22C) 2158c2ecf20Sopenharmony_ci#define USBD_EPINTST(x) ((x) + 0x230) 2168c2ecf20Sopenharmony_ci#define USBD_EPINTEN(x) ((x) + 0x234) 2178c2ecf20Sopenharmony_ci#define USBD_EPINTCLR(x) ((x) + 0x238) 2188c2ecf20Sopenharmony_ci#define USBD_EPINTSET(x) ((x) + 0x23C) 2198c2ecf20Sopenharmony_ci#define USBD_EPINTPRI(x) ((x) + 0x240) 2208c2ecf20Sopenharmony_ci#define USBD_REEP(x) ((x) + 0x244) 2218c2ecf20Sopenharmony_ci#define USBD_EPIND(x) ((x) + 0x248) 2228c2ecf20Sopenharmony_ci#define USBD_EPMAXPSIZE(x) ((x) + 0x24C) 2238c2ecf20Sopenharmony_ci/* DMA support registers only below */ 2248c2ecf20Sopenharmony_ci/* Set, clear, or get enabled state of the DMA request status. If 2258c2ecf20Sopenharmony_ci * enabled, an IN or OUT token will start a DMA transfer for the EP */ 2268c2ecf20Sopenharmony_ci#define USBD_DMARST(x) ((x) + 0x250) 2278c2ecf20Sopenharmony_ci#define USBD_DMARCLR(x) ((x) + 0x254) 2288c2ecf20Sopenharmony_ci#define USBD_DMARSET(x) ((x) + 0x258) 2298c2ecf20Sopenharmony_ci/* DMA UDCA head pointer */ 2308c2ecf20Sopenharmony_ci#define USBD_UDCAH(x) ((x) + 0x280) 2318c2ecf20Sopenharmony_ci/* EP DMA status, enable, and disable. This is used to specifically 2328c2ecf20Sopenharmony_ci * enabled or disable DMA for a specific EP */ 2338c2ecf20Sopenharmony_ci#define USBD_EPDMAST(x) ((x) + 0x284) 2348c2ecf20Sopenharmony_ci#define USBD_EPDMAEN(x) ((x) + 0x288) 2358c2ecf20Sopenharmony_ci#define USBD_EPDMADIS(x) ((x) + 0x28C) 2368c2ecf20Sopenharmony_ci/* DMA master interrupts enable and pending interrupts */ 2378c2ecf20Sopenharmony_ci#define USBD_DMAINTST(x) ((x) + 0x290) 2388c2ecf20Sopenharmony_ci#define USBD_DMAINTEN(x) ((x) + 0x294) 2398c2ecf20Sopenharmony_ci/* DMA end of transfer interrupt enable, disable, status */ 2408c2ecf20Sopenharmony_ci#define USBD_EOTINTST(x) ((x) + 0x2A0) 2418c2ecf20Sopenharmony_ci#define USBD_EOTINTCLR(x) ((x) + 0x2A4) 2428c2ecf20Sopenharmony_ci#define USBD_EOTINTSET(x) ((x) + 0x2A8) 2438c2ecf20Sopenharmony_ci/* New DD request interrupt enable, disable, status */ 2448c2ecf20Sopenharmony_ci#define USBD_NDDRTINTST(x) ((x) + 0x2AC) 2458c2ecf20Sopenharmony_ci#define USBD_NDDRTINTCLR(x) ((x) + 0x2B0) 2468c2ecf20Sopenharmony_ci#define USBD_NDDRTINTSET(x) ((x) + 0x2B4) 2478c2ecf20Sopenharmony_ci/* DMA error interrupt enable, disable, status */ 2488c2ecf20Sopenharmony_ci#define USBD_SYSERRTINTST(x) ((x) + 0x2B8) 2498c2ecf20Sopenharmony_ci#define USBD_SYSERRTINTCLR(x) ((x) + 0x2BC) 2508c2ecf20Sopenharmony_ci#define USBD_SYSERRTINTSET(x) ((x) + 0x2C0) 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/********************************************************************** 2538c2ecf20Sopenharmony_ci * USBD_DEVINTST/USBD_DEVINTEN/USBD_DEVINTCLR/USBD_DEVINTSET/ 2548c2ecf20Sopenharmony_ci * USBD_DEVINTPRI register definitions 2558c2ecf20Sopenharmony_ci **********************************************************************/ 2568c2ecf20Sopenharmony_ci#define USBD_ERR_INT (1 << 9) 2578c2ecf20Sopenharmony_ci#define USBD_EP_RLZED (1 << 8) 2588c2ecf20Sopenharmony_ci#define USBD_TXENDPKT (1 << 7) 2598c2ecf20Sopenharmony_ci#define USBD_RXENDPKT (1 << 6) 2608c2ecf20Sopenharmony_ci#define USBD_CDFULL (1 << 5) 2618c2ecf20Sopenharmony_ci#define USBD_CCEMPTY (1 << 4) 2628c2ecf20Sopenharmony_ci#define USBD_DEV_STAT (1 << 3) 2638c2ecf20Sopenharmony_ci#define USBD_EP_SLOW (1 << 2) 2648c2ecf20Sopenharmony_ci#define USBD_EP_FAST (1 << 1) 2658c2ecf20Sopenharmony_ci#define USBD_FRAME (1 << 0) 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/********************************************************************** 2688c2ecf20Sopenharmony_ci * USBD_EPINTST/USBD_EPINTEN/USBD_EPINTCLR/USBD_EPINTSET/ 2698c2ecf20Sopenharmony_ci * USBD_EPINTPRI register definitions 2708c2ecf20Sopenharmony_ci **********************************************************************/ 2718c2ecf20Sopenharmony_ci/* End point selection macro (RX) */ 2728c2ecf20Sopenharmony_ci#define USBD_RX_EP_SEL(e) (1 << ((e) << 1)) 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* End point selection macro (TX) */ 2758c2ecf20Sopenharmony_ci#define USBD_TX_EP_SEL(e) (1 << (((e) << 1) + 1)) 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/********************************************************************** 2788c2ecf20Sopenharmony_ci * USBD_REEP/USBD_DMARST/USBD_DMARCLR/USBD_DMARSET/USBD_EPDMAST/ 2798c2ecf20Sopenharmony_ci * USBD_EPDMAEN/USBD_EPDMADIS/ 2808c2ecf20Sopenharmony_ci * USBD_NDDRTINTST/USBD_NDDRTINTCLR/USBD_NDDRTINTSET/ 2818c2ecf20Sopenharmony_ci * USBD_EOTINTST/USBD_EOTINTCLR/USBD_EOTINTSET/ 2828c2ecf20Sopenharmony_ci * USBD_SYSERRTINTST/USBD_SYSERRTINTCLR/USBD_SYSERRTINTSET 2838c2ecf20Sopenharmony_ci * register definitions 2848c2ecf20Sopenharmony_ci **********************************************************************/ 2858c2ecf20Sopenharmony_ci/* Endpoint selection macro */ 2868c2ecf20Sopenharmony_ci#define USBD_EP_SEL(e) (1 << (e)) 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/********************************************************************** 2898c2ecf20Sopenharmony_ci * SBD_DMAINTST/USBD_DMAINTEN 2908c2ecf20Sopenharmony_ci **********************************************************************/ 2918c2ecf20Sopenharmony_ci#define USBD_SYS_ERR_INT (1 << 2) 2928c2ecf20Sopenharmony_ci#define USBD_NEW_DD_INT (1 << 1) 2938c2ecf20Sopenharmony_ci#define USBD_EOT_INT (1 << 0) 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/********************************************************************** 2968c2ecf20Sopenharmony_ci * USBD_RXPLEN register definitions 2978c2ecf20Sopenharmony_ci **********************************************************************/ 2988c2ecf20Sopenharmony_ci#define USBD_PKT_RDY (1 << 11) 2998c2ecf20Sopenharmony_ci#define USBD_DV (1 << 10) 3008c2ecf20Sopenharmony_ci#define USBD_PK_LEN_MASK 0x3FF 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/********************************************************************** 3038c2ecf20Sopenharmony_ci * USBD_CTRL register definitions 3048c2ecf20Sopenharmony_ci **********************************************************************/ 3058c2ecf20Sopenharmony_ci#define USBD_LOG_ENDPOINT(e) ((e) << 2) 3068c2ecf20Sopenharmony_ci#define USBD_WR_EN (1 << 1) 3078c2ecf20Sopenharmony_ci#define USBD_RD_EN (1 << 0) 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci/********************************************************************** 3108c2ecf20Sopenharmony_ci * USBD_CMDCODE register definitions 3118c2ecf20Sopenharmony_ci **********************************************************************/ 3128c2ecf20Sopenharmony_ci#define USBD_CMD_CODE(c) ((c) << 16) 3138c2ecf20Sopenharmony_ci#define USBD_CMD_PHASE(p) ((p) << 8) 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci/********************************************************************** 3168c2ecf20Sopenharmony_ci * USBD_DMARST/USBD_DMARCLR/USBD_DMARSET register definitions 3178c2ecf20Sopenharmony_ci **********************************************************************/ 3188c2ecf20Sopenharmony_ci#define USBD_DMAEP(e) (1 << (e)) 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci/* DD (DMA Descriptor) structure, requires word alignment */ 3218c2ecf20Sopenharmony_cistruct lpc32xx_usbd_dd { 3228c2ecf20Sopenharmony_ci u32 *dd_next; 3238c2ecf20Sopenharmony_ci u32 dd_setup; 3248c2ecf20Sopenharmony_ci u32 dd_buffer_addr; 3258c2ecf20Sopenharmony_ci u32 dd_status; 3268c2ecf20Sopenharmony_ci u32 dd_iso_ps_mem_addr; 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci/* dd_setup bit defines */ 3308c2ecf20Sopenharmony_ci#define DD_SETUP_ATLE_DMA_MODE 0x01 3318c2ecf20Sopenharmony_ci#define DD_SETUP_NEXT_DD_VALID 0x04 3328c2ecf20Sopenharmony_ci#define DD_SETUP_ISO_EP 0x10 3338c2ecf20Sopenharmony_ci#define DD_SETUP_PACKETLEN(n) (((n) & 0x7FF) << 5) 3348c2ecf20Sopenharmony_ci#define DD_SETUP_DMALENBYTES(n) (((n) & 0xFFFF) << 16) 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci/* dd_status bit defines */ 3378c2ecf20Sopenharmony_ci#define DD_STATUS_DD_RETIRED 0x01 3388c2ecf20Sopenharmony_ci#define DD_STATUS_STS_MASK 0x1E 3398c2ecf20Sopenharmony_ci#define DD_STATUS_STS_NS 0x00 /* Not serviced */ 3408c2ecf20Sopenharmony_ci#define DD_STATUS_STS_BS 0x02 /* Being serviced */ 3418c2ecf20Sopenharmony_ci#define DD_STATUS_STS_NC 0x04 /* Normal completion */ 3428c2ecf20Sopenharmony_ci#define DD_STATUS_STS_DUR 0x06 /* Data underrun (short packet) */ 3438c2ecf20Sopenharmony_ci#define DD_STATUS_STS_DOR 0x08 /* Data overrun */ 3448c2ecf20Sopenharmony_ci#define DD_STATUS_STS_SE 0x12 /* System error */ 3458c2ecf20Sopenharmony_ci#define DD_STATUS_PKT_VAL 0x20 /* Packet valid */ 3468c2ecf20Sopenharmony_ci#define DD_STATUS_LSB_EX 0x40 /* LS byte extracted (ATLE) */ 3478c2ecf20Sopenharmony_ci#define DD_STATUS_MSB_EX 0x80 /* MS byte extracted (ATLE) */ 3488c2ecf20Sopenharmony_ci#define DD_STATUS_MLEN(n) (((n) >> 8) & 0x3F) 3498c2ecf20Sopenharmony_ci#define DD_STATUS_CURDMACNT(n) (((n) >> 16) & 0xFFFF) 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* 3528c2ecf20Sopenharmony_ci * 3538c2ecf20Sopenharmony_ci * Protocol engine bits below 3548c2ecf20Sopenharmony_ci * 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci/* Device Interrupt Bit Definitions */ 3578c2ecf20Sopenharmony_ci#define FRAME_INT 0x00000001 3588c2ecf20Sopenharmony_ci#define EP_FAST_INT 0x00000002 3598c2ecf20Sopenharmony_ci#define EP_SLOW_INT 0x00000004 3608c2ecf20Sopenharmony_ci#define DEV_STAT_INT 0x00000008 3618c2ecf20Sopenharmony_ci#define CCEMTY_INT 0x00000010 3628c2ecf20Sopenharmony_ci#define CDFULL_INT 0x00000020 3638c2ecf20Sopenharmony_ci#define RxENDPKT_INT 0x00000040 3648c2ecf20Sopenharmony_ci#define TxENDPKT_INT 0x00000080 3658c2ecf20Sopenharmony_ci#define EP_RLZED_INT 0x00000100 3668c2ecf20Sopenharmony_ci#define ERR_INT 0x00000200 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* Rx & Tx Packet Length Definitions */ 3698c2ecf20Sopenharmony_ci#define PKT_LNGTH_MASK 0x000003FF 3708c2ecf20Sopenharmony_ci#define PKT_DV 0x00000400 3718c2ecf20Sopenharmony_ci#define PKT_RDY 0x00000800 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/* USB Control Definitions */ 3748c2ecf20Sopenharmony_ci#define CTRL_RD_EN 0x00000001 3758c2ecf20Sopenharmony_ci#define CTRL_WR_EN 0x00000002 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* Command Codes */ 3788c2ecf20Sopenharmony_ci#define CMD_SET_ADDR 0x00D00500 3798c2ecf20Sopenharmony_ci#define CMD_CFG_DEV 0x00D80500 3808c2ecf20Sopenharmony_ci#define CMD_SET_MODE 0x00F30500 3818c2ecf20Sopenharmony_ci#define CMD_RD_FRAME 0x00F50500 3828c2ecf20Sopenharmony_ci#define DAT_RD_FRAME 0x00F50200 3838c2ecf20Sopenharmony_ci#define CMD_RD_TEST 0x00FD0500 3848c2ecf20Sopenharmony_ci#define DAT_RD_TEST 0x00FD0200 3858c2ecf20Sopenharmony_ci#define CMD_SET_DEV_STAT 0x00FE0500 3868c2ecf20Sopenharmony_ci#define CMD_GET_DEV_STAT 0x00FE0500 3878c2ecf20Sopenharmony_ci#define DAT_GET_DEV_STAT 0x00FE0200 3888c2ecf20Sopenharmony_ci#define CMD_GET_ERR_CODE 0x00FF0500 3898c2ecf20Sopenharmony_ci#define DAT_GET_ERR_CODE 0x00FF0200 3908c2ecf20Sopenharmony_ci#define CMD_RD_ERR_STAT 0x00FB0500 3918c2ecf20Sopenharmony_ci#define DAT_RD_ERR_STAT 0x00FB0200 3928c2ecf20Sopenharmony_ci#define DAT_WR_BYTE(x) (0x00000100 | ((x) << 16)) 3938c2ecf20Sopenharmony_ci#define CMD_SEL_EP(x) (0x00000500 | ((x) << 16)) 3948c2ecf20Sopenharmony_ci#define DAT_SEL_EP(x) (0x00000200 | ((x) << 16)) 3958c2ecf20Sopenharmony_ci#define CMD_SEL_EP_CLRI(x) (0x00400500 | ((x) << 16)) 3968c2ecf20Sopenharmony_ci#define DAT_SEL_EP_CLRI(x) (0x00400200 | ((x) << 16)) 3978c2ecf20Sopenharmony_ci#define CMD_SET_EP_STAT(x) (0x00400500 | ((x) << 16)) 3988c2ecf20Sopenharmony_ci#define CMD_CLR_BUF 0x00F20500 3998c2ecf20Sopenharmony_ci#define DAT_CLR_BUF 0x00F20200 4008c2ecf20Sopenharmony_ci#define CMD_VALID_BUF 0x00FA0500 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci/* Device Address Register Definitions */ 4038c2ecf20Sopenharmony_ci#define DEV_ADDR_MASK 0x7F 4048c2ecf20Sopenharmony_ci#define DEV_EN 0x80 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/* Device Configure Register Definitions */ 4078c2ecf20Sopenharmony_ci#define CONF_DVICE 0x01 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* Device Mode Register Definitions */ 4108c2ecf20Sopenharmony_ci#define AP_CLK 0x01 4118c2ecf20Sopenharmony_ci#define INAK_CI 0x02 4128c2ecf20Sopenharmony_ci#define INAK_CO 0x04 4138c2ecf20Sopenharmony_ci#define INAK_II 0x08 4148c2ecf20Sopenharmony_ci#define INAK_IO 0x10 4158c2ecf20Sopenharmony_ci#define INAK_BI 0x20 4168c2ecf20Sopenharmony_ci#define INAK_BO 0x40 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* Device Status Register Definitions */ 4198c2ecf20Sopenharmony_ci#define DEV_CON 0x01 4208c2ecf20Sopenharmony_ci#define DEV_CON_CH 0x02 4218c2ecf20Sopenharmony_ci#define DEV_SUS 0x04 4228c2ecf20Sopenharmony_ci#define DEV_SUS_CH 0x08 4238c2ecf20Sopenharmony_ci#define DEV_RST 0x10 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* Error Code Register Definitions */ 4268c2ecf20Sopenharmony_ci#define ERR_EC_MASK 0x0F 4278c2ecf20Sopenharmony_ci#define ERR_EA 0x10 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci/* Error Status Register Definitions */ 4308c2ecf20Sopenharmony_ci#define ERR_PID 0x01 4318c2ecf20Sopenharmony_ci#define ERR_UEPKT 0x02 4328c2ecf20Sopenharmony_ci#define ERR_DCRC 0x04 4338c2ecf20Sopenharmony_ci#define ERR_TIMOUT 0x08 4348c2ecf20Sopenharmony_ci#define ERR_EOP 0x10 4358c2ecf20Sopenharmony_ci#define ERR_B_OVRN 0x20 4368c2ecf20Sopenharmony_ci#define ERR_BTSTF 0x40 4378c2ecf20Sopenharmony_ci#define ERR_TGL 0x80 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci/* Endpoint Select Register Definitions */ 4408c2ecf20Sopenharmony_ci#define EP_SEL_F 0x01 4418c2ecf20Sopenharmony_ci#define EP_SEL_ST 0x02 4428c2ecf20Sopenharmony_ci#define EP_SEL_STP 0x04 4438c2ecf20Sopenharmony_ci#define EP_SEL_PO 0x08 4448c2ecf20Sopenharmony_ci#define EP_SEL_EPN 0x10 4458c2ecf20Sopenharmony_ci#define EP_SEL_B_1_FULL 0x20 4468c2ecf20Sopenharmony_ci#define EP_SEL_B_2_FULL 0x40 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* Endpoint Status Register Definitions */ 4498c2ecf20Sopenharmony_ci#define EP_STAT_ST 0x01 4508c2ecf20Sopenharmony_ci#define EP_STAT_DA 0x20 4518c2ecf20Sopenharmony_ci#define EP_STAT_RF_MO 0x40 4528c2ecf20Sopenharmony_ci#define EP_STAT_CND_ST 0x80 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci/* Clear Buffer Register Definitions */ 4558c2ecf20Sopenharmony_ci#define CLR_BUF_PO 0x01 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci/* DMA Interrupt Bit Definitions */ 4588c2ecf20Sopenharmony_ci#define EOT_INT 0x01 4598c2ecf20Sopenharmony_ci#define NDD_REQ_INT 0x02 4608c2ecf20Sopenharmony_ci#define SYS_ERR_INT 0x04 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#define DRIVER_VERSION "1.03" 4638c2ecf20Sopenharmony_cistatic const char driver_name[] = "lpc32xx_udc"; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci/* 4668c2ecf20Sopenharmony_ci * 4678c2ecf20Sopenharmony_ci * proc interface support 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FILES 4718c2ecf20Sopenharmony_cistatic char *epnames[] = {"INT", "ISO", "BULK", "CTRL"}; 4728c2ecf20Sopenharmony_cistatic const char debug_filename[] = "driver/udc"; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic void proc_ep_show(struct seq_file *s, struct lpc32xx_ep *ep) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci seq_printf(s, "\n"); 4798c2ecf20Sopenharmony_ci seq_printf(s, "%12s, maxpacket %4d %3s", 4808c2ecf20Sopenharmony_ci ep->ep.name, ep->ep.maxpacket, 4818c2ecf20Sopenharmony_ci ep->is_in ? "in" : "out"); 4828c2ecf20Sopenharmony_ci seq_printf(s, " type %4s", epnames[ep->eptype]); 4838c2ecf20Sopenharmony_ci seq_printf(s, " ints: %12d", ep->totalints); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) 4868c2ecf20Sopenharmony_ci seq_printf(s, "\t(queue empty)\n"); 4878c2ecf20Sopenharmony_ci else { 4888c2ecf20Sopenharmony_ci list_for_each_entry(req, &ep->queue, queue) { 4898c2ecf20Sopenharmony_ci u32 length = req->req.actual; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci seq_printf(s, "\treq %p len %d/%d buf %p\n", 4928c2ecf20Sopenharmony_ci &req->req, length, 4938c2ecf20Sopenharmony_ci req->req.length, req->req.buf); 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int udc_show(struct seq_file *s, void *unused) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = s->private; 5018c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep; 5028c2ecf20Sopenharmony_ci unsigned long flags; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n", 5098c2ecf20Sopenharmony_ci udc->vbus ? "present" : "off", 5108c2ecf20Sopenharmony_ci udc->enabled ? (udc->vbus ? "active" : "enabled") : 5118c2ecf20Sopenharmony_ci "disabled", 5128c2ecf20Sopenharmony_ci udc->gadget.is_selfpowered ? "self" : "VBUS", 5138c2ecf20Sopenharmony_ci udc->suspended ? ", suspended" : "", 5148c2ecf20Sopenharmony_ci udc->driver ? udc->driver->driver.name : "(none)"); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci if (udc->enabled && udc->vbus) { 5178c2ecf20Sopenharmony_ci proc_ep_show(s, &udc->ep[0]); 5188c2ecf20Sopenharmony_ci list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) 5198c2ecf20Sopenharmony_ci proc_ep_show(s, ep); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return 0; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(udc); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic void create_debug_file(struct lpc32xx_udc *udc) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &udc_fops); 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void remove_debug_file(struct lpc32xx_udc *udc) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci debugfs_remove(udc->pde); 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci#else 5408c2ecf20Sopenharmony_cistatic inline void create_debug_file(struct lpc32xx_udc *udc) {} 5418c2ecf20Sopenharmony_cistatic inline void remove_debug_file(struct lpc32xx_udc *udc) {} 5428c2ecf20Sopenharmony_ci#endif 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/* Primary initialization sequence for the ISP1301 transceiver */ 5458c2ecf20Sopenharmony_cistatic void isp1301_udc_configure(struct lpc32xx_udc *udc) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci u8 value; 5488c2ecf20Sopenharmony_ci s32 vendor, product; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci vendor = i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00); 5518c2ecf20Sopenharmony_ci product = i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (vendor == 0x0483 && product == 0xa0c4) 5548c2ecf20Sopenharmony_ci udc->atx = STOTG04; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* LPC32XX only supports DAT_SE0 USB mode */ 5578c2ecf20Sopenharmony_ci /* This sequence is important */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Disable transparent UART mode first */ 5608c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5618c2ecf20Sopenharmony_ci (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), 5628c2ecf20Sopenharmony_ci MC1_UART_EN); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* Set full speed and SE0 mode */ 5658c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5668c2ecf20Sopenharmony_ci (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); 5678c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5688c2ecf20Sopenharmony_ci ISP1301_I2C_MODE_CONTROL_1, (MC1_SPEED_REG | MC1_DAT_SE0)); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci /* 5718c2ecf20Sopenharmony_ci * The PSW_OE enable bit state is reversed in the ISP1301 User's Guide 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5748c2ecf20Sopenharmony_ci (ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci value = MC2_BI_DI; 5778c2ecf20Sopenharmony_ci if (udc->atx != STOTG04) 5788c2ecf20Sopenharmony_ci value |= MC2_SPD_SUSP_CTRL; 5798c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5808c2ecf20Sopenharmony_ci ISP1301_I2C_MODE_CONTROL_2, value); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Driver VBUS_DRV high or low depending on board setup */ 5838c2ecf20Sopenharmony_ci if (udc->board->vbus_drv_pol != 0) 5848c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5858c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV); 5868c2ecf20Sopenharmony_ci else 5878c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5888c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, 5898c2ecf20Sopenharmony_ci OTG1_VBUS_DRV); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* Bi-directional mode with suspend control 5928c2ecf20Sopenharmony_ci * Enable both pulldowns for now - the pullup will be enable when VBUS 5938c2ecf20Sopenharmony_ci * is detected */ 5948c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5958c2ecf20Sopenharmony_ci (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); 5968c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 5978c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1, 5988c2ecf20Sopenharmony_ci (0 | OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* Discharge VBUS (just in case) */ 6018c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6028c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG); 6038c2ecf20Sopenharmony_ci msleep(1); 6048c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6058c2ecf20Sopenharmony_ci (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), 6068c2ecf20Sopenharmony_ci OTG1_VBUS_DISCHRG); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6098c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6128c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 6138c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6148c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n", vendor); 6178c2ecf20Sopenharmony_ci dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n", product); 6188c2ecf20Sopenharmony_ci dev_info(udc->dev, "ISP1301 Version ID : 0x%04x\n", 6198c2ecf20Sopenharmony_ci i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x14)); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci/* Enables or disables the USB device pullup via the ISP1301 transceiver */ 6248c2ecf20Sopenharmony_cistatic void isp1301_pullup_set(struct lpc32xx_udc *udc) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci if (udc->pullup) 6278c2ecf20Sopenharmony_ci /* Enable pullup for bus signalling */ 6288c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6298c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1, OTG1_DP_PULLUP); 6308c2ecf20Sopenharmony_ci else 6318c2ecf20Sopenharmony_ci /* Enable pullup for bus signalling */ 6328c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6338c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, 6348c2ecf20Sopenharmony_ci OTG1_DP_PULLUP); 6358c2ecf20Sopenharmony_ci} 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_cistatic void pullup_work(struct work_struct *work) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = 6408c2ecf20Sopenharmony_ci container_of(work, struct lpc32xx_udc, pullup_job); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci isp1301_pullup_set(udc); 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic void isp1301_pullup_enable(struct lpc32xx_udc *udc, int en_pullup, 6468c2ecf20Sopenharmony_ci int block) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci if (en_pullup == udc->pullup) 6498c2ecf20Sopenharmony_ci return; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci udc->pullup = en_pullup; 6528c2ecf20Sopenharmony_ci if (block) 6538c2ecf20Sopenharmony_ci isp1301_pullup_set(udc); 6548c2ecf20Sopenharmony_ci else 6558c2ecf20Sopenharmony_ci /* defer slow i2c pull up setting */ 6568c2ecf20Sopenharmony_ci schedule_work(&udc->pullup_job); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 6608c2ecf20Sopenharmony_ci/* Powers up or down the ISP1301 transceiver */ 6618c2ecf20Sopenharmony_cistatic void isp1301_set_powerstate(struct lpc32xx_udc *udc, int enable) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci /* There is no "global power down" register for stotg04 */ 6648c2ecf20Sopenharmony_ci if (udc->atx == STOTG04) 6658c2ecf20Sopenharmony_ci return; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (enable != 0) 6688c2ecf20Sopenharmony_ci /* Power up ISP1301 - this ISP1301 will automatically wakeup 6698c2ecf20Sopenharmony_ci when VBUS is detected */ 6708c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6718c2ecf20Sopenharmony_ci ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR, 6728c2ecf20Sopenharmony_ci MC2_GLOBAL_PWR_DN); 6738c2ecf20Sopenharmony_ci else 6748c2ecf20Sopenharmony_ci /* Power down ISP1301 */ 6758c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 6768c2ecf20Sopenharmony_ci ISP1301_I2C_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void power_work(struct work_struct *work) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = 6828c2ecf20Sopenharmony_ci container_of(work, struct lpc32xx_udc, power_job); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci isp1301_set_powerstate(udc, udc->poweron); 6858c2ecf20Sopenharmony_ci} 6868c2ecf20Sopenharmony_ci#endif 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/* 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * USB protocol engine command/data read/write helper functions 6918c2ecf20Sopenharmony_ci * 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ci/* Issues a single command to the USB device state machine */ 6948c2ecf20Sopenharmony_cistatic void udc_protocol_cmd_w(struct lpc32xx_udc *udc, u32 cmd) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci u32 pass = 0; 6978c2ecf20Sopenharmony_ci int to; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* EP may lock on CLRI if this read isn't done */ 7008c2ecf20Sopenharmony_ci u32 tmp = readl(USBD_DEVINTST(udc->udp_baseaddr)); 7018c2ecf20Sopenharmony_ci (void) tmp; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci while (pass == 0) { 7048c2ecf20Sopenharmony_ci writel(USBD_CCEMPTY, USBD_DEVINTCLR(udc->udp_baseaddr)); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci /* Write command code */ 7078c2ecf20Sopenharmony_ci writel(cmd, USBD_CMDCODE(udc->udp_baseaddr)); 7088c2ecf20Sopenharmony_ci to = 10000; 7098c2ecf20Sopenharmony_ci while (((readl(USBD_DEVINTST(udc->udp_baseaddr)) & 7108c2ecf20Sopenharmony_ci USBD_CCEMPTY) == 0) && (to > 0)) { 7118c2ecf20Sopenharmony_ci to--; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (to > 0) 7158c2ecf20Sopenharmony_ci pass = 1; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci cpu_relax(); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/* Issues 2 commands (or command and data) to the USB device state machine */ 7228c2ecf20Sopenharmony_cistatic inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd, 7238c2ecf20Sopenharmony_ci u32 data) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, cmd); 7268c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, data); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/* Issues a single command to the USB device state machine and reads 7308c2ecf20Sopenharmony_ci * response data */ 7318c2ecf20Sopenharmony_cistatic u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci int to = 1000; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* Write a command and read data from the protocol engine */ 7368c2ecf20Sopenharmony_ci writel((USBD_CDFULL | USBD_CCEMPTY), 7378c2ecf20Sopenharmony_ci USBD_DEVINTCLR(udc->udp_baseaddr)); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Write command code */ 7408c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, cmd); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL)) 7438c2ecf20Sopenharmony_ci && (to > 0)) 7448c2ecf20Sopenharmony_ci to--; 7458c2ecf20Sopenharmony_ci if (!to) 7468c2ecf20Sopenharmony_ci dev_dbg(udc->dev, 7478c2ecf20Sopenharmony_ci "Protocol engine didn't receive response (CDFULL)\n"); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci return readl(USBD_CMDDATA(udc->udp_baseaddr)); 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci/* 7538c2ecf20Sopenharmony_ci * 7548c2ecf20Sopenharmony_ci * USB device interrupt mask support functions 7558c2ecf20Sopenharmony_ci * 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_ci/* Enable one or more USB device interrupts */ 7588c2ecf20Sopenharmony_cistatic inline void uda_enable_devint(struct lpc32xx_udc *udc, u32 devmask) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci udc->enabled_devints |= devmask; 7618c2ecf20Sopenharmony_ci writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr)); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci/* Disable one or more USB device interrupts */ 7658c2ecf20Sopenharmony_cistatic inline void uda_disable_devint(struct lpc32xx_udc *udc, u32 mask) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci udc->enabled_devints &= ~mask; 7688c2ecf20Sopenharmony_ci writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr)); 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci/* Clear one or more USB device interrupts */ 7728c2ecf20Sopenharmony_cistatic inline void uda_clear_devint(struct lpc32xx_udc *udc, u32 mask) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci writel(mask, USBD_DEVINTCLR(udc->udp_baseaddr)); 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci/* 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * Endpoint interrupt disable/enable functions 7808c2ecf20Sopenharmony_ci * 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci/* Enable one or more USB endpoint interrupts */ 7838c2ecf20Sopenharmony_cistatic void uda_enable_hwepint(struct lpc32xx_udc *udc, u32 hwep) 7848c2ecf20Sopenharmony_ci{ 7858c2ecf20Sopenharmony_ci udc->enabled_hwepints |= (1 << hwep); 7868c2ecf20Sopenharmony_ci writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr)); 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/* Disable one or more USB endpoint interrupts */ 7908c2ecf20Sopenharmony_cistatic void uda_disable_hwepint(struct lpc32xx_udc *udc, u32 hwep) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci udc->enabled_hwepints &= ~(1 << hwep); 7938c2ecf20Sopenharmony_ci writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr)); 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci/* Clear one or more USB endpoint interrupts */ 7978c2ecf20Sopenharmony_cistatic inline void uda_clear_hwepint(struct lpc32xx_udc *udc, u32 hwep) 7988c2ecf20Sopenharmony_ci{ 7998c2ecf20Sopenharmony_ci writel((1 << hwep), USBD_EPINTCLR(udc->udp_baseaddr)); 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci/* Enable DMA for the HW channel */ 8038c2ecf20Sopenharmony_cistatic inline void udc_ep_dma_enable(struct lpc32xx_udc *udc, u32 hwep) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci writel((1 << hwep), USBD_EPDMAEN(udc->udp_baseaddr)); 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci/* Disable DMA for the HW channel */ 8098c2ecf20Sopenharmony_cistatic inline void udc_ep_dma_disable(struct lpc32xx_udc *udc, u32 hwep) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci writel((1 << hwep), USBD_EPDMADIS(udc->udp_baseaddr)); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci/* 8158c2ecf20Sopenharmony_ci * 8168c2ecf20Sopenharmony_ci * Endpoint realize/unrealize functions 8178c2ecf20Sopenharmony_ci * 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_ci/* Before an endpoint can be used, it needs to be realized 8208c2ecf20Sopenharmony_ci * in the USB protocol engine - this realizes the endpoint. 8218c2ecf20Sopenharmony_ci * The interrupt (FIFO or DMA) is not enabled with this function */ 8228c2ecf20Sopenharmony_cistatic void udc_realize_hwep(struct lpc32xx_udc *udc, u32 hwep, 8238c2ecf20Sopenharmony_ci u32 maxpacket) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci int to = 1000; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr)); 8288c2ecf20Sopenharmony_ci writel(hwep, USBD_EPIND(udc->udp_baseaddr)); 8298c2ecf20Sopenharmony_ci udc->realized_eps |= (1 << hwep); 8308c2ecf20Sopenharmony_ci writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr)); 8318c2ecf20Sopenharmony_ci writel(maxpacket, USBD_EPMAXPSIZE(udc->udp_baseaddr)); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* Wait until endpoint is realized in hardware */ 8348c2ecf20Sopenharmony_ci while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & 8358c2ecf20Sopenharmony_ci USBD_EP_RLZED)) && (to > 0)) 8368c2ecf20Sopenharmony_ci to--; 8378c2ecf20Sopenharmony_ci if (!to) 8388c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "EP not correctly realized in hardware\n"); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr)); 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci/* Unrealize an EP */ 8448c2ecf20Sopenharmony_cistatic void udc_unrealize_hwep(struct lpc32xx_udc *udc, u32 hwep) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci udc->realized_eps &= ~(1 << hwep); 8478c2ecf20Sopenharmony_ci writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr)); 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci/* 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * Endpoint support functions 8538c2ecf20Sopenharmony_ci * 8548c2ecf20Sopenharmony_ci */ 8558c2ecf20Sopenharmony_ci/* Select and clear endpoint interrupt */ 8568c2ecf20Sopenharmony_cistatic u32 udc_selep_clrint(struct lpc32xx_udc *udc, u32 hwep) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_SEL_EP_CLRI(hwep)); 8598c2ecf20Sopenharmony_ci return udc_protocol_cmd_r(udc, DAT_SEL_EP_CLRI(hwep)); 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci/* Disables the endpoint in the USB protocol engine */ 8638c2ecf20Sopenharmony_cistatic void udc_disable_hwep(struct lpc32xx_udc *udc, u32 hwep) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), 8668c2ecf20Sopenharmony_ci DAT_WR_BYTE(EP_STAT_DA)); 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci/* Stalls the endpoint - endpoint will return STALL */ 8708c2ecf20Sopenharmony_cistatic void udc_stall_hwep(struct lpc32xx_udc *udc, u32 hwep) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), 8738c2ecf20Sopenharmony_ci DAT_WR_BYTE(EP_STAT_ST)); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci/* Clear stall or reset endpoint */ 8778c2ecf20Sopenharmony_cistatic void udc_clrstall_hwep(struct lpc32xx_udc *udc, u32 hwep) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), 8808c2ecf20Sopenharmony_ci DAT_WR_BYTE(0)); 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci/* Select an endpoint for endpoint status, clear, validate */ 8848c2ecf20Sopenharmony_cistatic void udc_select_hwep(struct lpc32xx_udc *udc, u32 hwep) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_SEL_EP(hwep)); 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci/* 8908c2ecf20Sopenharmony_ci * 8918c2ecf20Sopenharmony_ci * Endpoint buffer management functions 8928c2ecf20Sopenharmony_ci * 8938c2ecf20Sopenharmony_ci */ 8948c2ecf20Sopenharmony_ci/* Clear the current endpoint's buffer */ 8958c2ecf20Sopenharmony_cistatic void udc_clr_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci udc_select_hwep(udc, hwep); 8988c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_CLR_BUF); 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci/* Validate the current endpoint's buffer */ 9028c2ecf20Sopenharmony_cistatic void udc_val_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci udc_select_hwep(udc, hwep); 9058c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_VALID_BUF); 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic inline u32 udc_clearep_getsts(struct lpc32xx_udc *udc, u32 hwep) 9098c2ecf20Sopenharmony_ci{ 9108c2ecf20Sopenharmony_ci /* Clear EP interrupt */ 9118c2ecf20Sopenharmony_ci uda_clear_hwepint(udc, hwep); 9128c2ecf20Sopenharmony_ci return udc_selep_clrint(udc, hwep); 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/* 9168c2ecf20Sopenharmony_ci * 9178c2ecf20Sopenharmony_ci * USB EP DMA support 9188c2ecf20Sopenharmony_ci * 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ci/* Allocate a DMA Descriptor */ 9218c2ecf20Sopenharmony_cistatic struct lpc32xx_usbd_dd_gad *udc_dd_alloc(struct lpc32xx_udc *udc) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci dma_addr_t dma; 9248c2ecf20Sopenharmony_ci struct lpc32xx_usbd_dd_gad *dd; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci dd = dma_pool_alloc(udc->dd_cache, GFP_ATOMIC | GFP_DMA, &dma); 9278c2ecf20Sopenharmony_ci if (dd) 9288c2ecf20Sopenharmony_ci dd->this_dma = dma; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return dd; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci/* Free a DMA Descriptor */ 9348c2ecf20Sopenharmony_cistatic void udc_dd_free(struct lpc32xx_udc *udc, struct lpc32xx_usbd_dd_gad *dd) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci dma_pool_free(udc->dd_cache, dd, dd->this_dma); 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/* 9408c2ecf20Sopenharmony_ci * 9418c2ecf20Sopenharmony_ci * USB setup and shutdown functions 9428c2ecf20Sopenharmony_ci * 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci/* Enables or disables most of the USB system clocks when low power mode is 9458c2ecf20Sopenharmony_ci * needed. Clocks are typically started on a connection event, and disabled 9468c2ecf20Sopenharmony_ci * when a cable is disconnected */ 9478c2ecf20Sopenharmony_cistatic void udc_clk_set(struct lpc32xx_udc *udc, int enable) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci if (enable != 0) { 9508c2ecf20Sopenharmony_ci if (udc->clocked) 9518c2ecf20Sopenharmony_ci return; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci udc->clocked = 1; 9548c2ecf20Sopenharmony_ci clk_prepare_enable(udc->usb_slv_clk); 9558c2ecf20Sopenharmony_ci } else { 9568c2ecf20Sopenharmony_ci if (!udc->clocked) 9578c2ecf20Sopenharmony_ci return; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci udc->clocked = 0; 9608c2ecf20Sopenharmony_ci clk_disable_unprepare(udc->usb_slv_clk); 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci} 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/* Set/reset USB device address */ 9658c2ecf20Sopenharmony_cistatic void udc_set_address(struct lpc32xx_udc *udc, u32 addr) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci /* Address will be latched at the end of the status phase, or 9688c2ecf20Sopenharmony_ci latched immediately if function is called twice */ 9698c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_ADDR, 9708c2ecf20Sopenharmony_ci DAT_WR_BYTE(DEV_EN | addr)); 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci/* Setup up a IN request for DMA transfer - this consists of determining the 9748c2ecf20Sopenharmony_ci * list of DMA addresses for the transfer, allocating DMA Descriptors, 9758c2ecf20Sopenharmony_ci * installing the DD into the UDCA, and then enabling the DMA for that EP */ 9768c2ecf20Sopenharmony_cistatic int udc_ep_in_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 9798c2ecf20Sopenharmony_ci u32 hwep = ep->hwep_num; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci ep->req_pending = 1; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci /* There will always be a request waiting here */ 9848c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci /* Place the DD Descriptor into the UDCA */ 9878c2ecf20Sopenharmony_ci udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* Enable DMA and interrupt for the HW EP */ 9908c2ecf20Sopenharmony_ci udc_ep_dma_enable(udc, hwep); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Clear ZLP if last packet is not of MAXP size */ 9938c2ecf20Sopenharmony_ci if (req->req.length % ep->ep.maxpacket) 9948c2ecf20Sopenharmony_ci req->send_zlp = 0; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci return 0; 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci/* Setup up a OUT request for DMA transfer - this consists of determining the 10008c2ecf20Sopenharmony_ci * list of DMA addresses for the transfer, allocating DMA Descriptors, 10018c2ecf20Sopenharmony_ci * installing the DD into the UDCA, and then enabling the DMA for that EP */ 10028c2ecf20Sopenharmony_cistatic int udc_ep_out_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 10058c2ecf20Sopenharmony_ci u32 hwep = ep->hwep_num; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci ep->req_pending = 1; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* There will always be a request waiting here */ 10108c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* Place the DD Descriptor into the UDCA */ 10138c2ecf20Sopenharmony_ci udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci /* Enable DMA and interrupt for the HW EP */ 10168c2ecf20Sopenharmony_ci udc_ep_dma_enable(udc, hwep); 10178c2ecf20Sopenharmony_ci return 0; 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic void udc_disable(struct lpc32xx_udc *udc) 10218c2ecf20Sopenharmony_ci{ 10228c2ecf20Sopenharmony_ci u32 i; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci /* Disable device */ 10258c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0)); 10268c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(0)); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci /* Disable all device interrupts (including EP0) */ 10298c2ecf20Sopenharmony_ci uda_disable_devint(udc, 0x3FF); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci /* Disable and reset all endpoint interrupts */ 10328c2ecf20Sopenharmony_ci for (i = 0; i < 32; i++) { 10338c2ecf20Sopenharmony_ci uda_disable_hwepint(udc, i); 10348c2ecf20Sopenharmony_ci uda_clear_hwepint(udc, i); 10358c2ecf20Sopenharmony_ci udc_disable_hwep(udc, i); 10368c2ecf20Sopenharmony_ci udc_unrealize_hwep(udc, i); 10378c2ecf20Sopenharmony_ci udc->udca_v_base[i] = 0; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Disable and clear all interrupts and DMA */ 10408c2ecf20Sopenharmony_ci udc_ep_dma_disable(udc, i); 10418c2ecf20Sopenharmony_ci writel((1 << i), USBD_EOTINTCLR(udc->udp_baseaddr)); 10428c2ecf20Sopenharmony_ci writel((1 << i), USBD_NDDRTINTCLR(udc->udp_baseaddr)); 10438c2ecf20Sopenharmony_ci writel((1 << i), USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 10448c2ecf20Sopenharmony_ci writel((1 << i), USBD_DMARCLR(udc->udp_baseaddr)); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci /* Disable DMA interrupts */ 10488c2ecf20Sopenharmony_ci writel(0, USBD_DMAINTEN(udc->udp_baseaddr)); 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci writel(0, USBD_UDCAH(udc->udp_baseaddr)); 10518c2ecf20Sopenharmony_ci} 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_cistatic void udc_enable(struct lpc32xx_udc *udc) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci u32 i; 10568c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = &udc->ep[0]; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* Start with known state */ 10598c2ecf20Sopenharmony_ci udc_disable(udc); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* Enable device */ 10628c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(DEV_CON)); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* EP interrupts on high priority, FRAME interrupt on low priority */ 10658c2ecf20Sopenharmony_ci writel(USBD_EP_FAST, USBD_DEVINTPRI(udc->udp_baseaddr)); 10668c2ecf20Sopenharmony_ci writel(0xFFFF, USBD_EPINTPRI(udc->udp_baseaddr)); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* Clear any pending device interrupts */ 10698c2ecf20Sopenharmony_ci writel(0x3FF, USBD_DEVINTCLR(udc->udp_baseaddr)); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci /* Setup UDCA - not yet used (DMA) */ 10728c2ecf20Sopenharmony_ci writel(udc->udca_p_base, USBD_UDCAH(udc->udp_baseaddr)); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci /* Only enable EP0 in and out for now, EP0 only works in FIFO mode */ 10758c2ecf20Sopenharmony_ci for (i = 0; i <= 1; i++) { 10768c2ecf20Sopenharmony_ci udc_realize_hwep(udc, i, ep->ep.maxpacket); 10778c2ecf20Sopenharmony_ci uda_enable_hwepint(udc, i); 10788c2ecf20Sopenharmony_ci udc_select_hwep(udc, i); 10798c2ecf20Sopenharmony_ci udc_clrstall_hwep(udc, i); 10808c2ecf20Sopenharmony_ci udc_clr_buffer_hwep(udc, i); 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci /* Device interrupt setup */ 10848c2ecf20Sopenharmony_ci uda_clear_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW | 10858c2ecf20Sopenharmony_ci USBD_EP_FAST)); 10868c2ecf20Sopenharmony_ci uda_enable_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW | 10878c2ecf20Sopenharmony_ci USBD_EP_FAST)); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* Set device address to 0 - called twice to force a latch in the USB 10908c2ecf20Sopenharmony_ci engine without the need of a setup packet status closure */ 10918c2ecf20Sopenharmony_ci udc_set_address(udc, 0); 10928c2ecf20Sopenharmony_ci udc_set_address(udc, 0); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci /* Enable master DMA interrupts */ 10958c2ecf20Sopenharmony_ci writel((USBD_SYS_ERR_INT | USBD_EOT_INT), 10968c2ecf20Sopenharmony_ci USBD_DMAINTEN(udc->udp_baseaddr)); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci udc->dev_status = 0; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/* 11028c2ecf20Sopenharmony_ci * 11038c2ecf20Sopenharmony_ci * USB device board specific events handled via callbacks 11048c2ecf20Sopenharmony_ci * 11058c2ecf20Sopenharmony_ci */ 11068c2ecf20Sopenharmony_ci/* Connection change event - notify board function of change */ 11078c2ecf20Sopenharmony_cistatic void uda_power_event(struct lpc32xx_udc *udc, u32 conn) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci /* Just notify of a connection change event (optional) */ 11108c2ecf20Sopenharmony_ci if (udc->board->conn_chgb != NULL) 11118c2ecf20Sopenharmony_ci udc->board->conn_chgb(conn); 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci/* Suspend/resume event - notify board function of change */ 11158c2ecf20Sopenharmony_cistatic void uda_resm_susp_event(struct lpc32xx_udc *udc, u32 conn) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci /* Just notify of a Suspend/resume change event (optional) */ 11188c2ecf20Sopenharmony_ci if (udc->board->susp_chgb != NULL) 11198c2ecf20Sopenharmony_ci udc->board->susp_chgb(conn); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (conn) 11228c2ecf20Sopenharmony_ci udc->suspended = 0; 11238c2ecf20Sopenharmony_ci else 11248c2ecf20Sopenharmony_ci udc->suspended = 1; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci/* Remote wakeup enable/disable - notify board function of change */ 11288c2ecf20Sopenharmony_cistatic void uda_remwkp_cgh(struct lpc32xx_udc *udc) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci if (udc->board->rmwk_chgb != NULL) 11318c2ecf20Sopenharmony_ci udc->board->rmwk_chgb(udc->dev_status & 11328c2ecf20Sopenharmony_ci (1 << USB_DEVICE_REMOTE_WAKEUP)); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci/* Reads data from FIFO, adjusts for alignment and data size */ 11368c2ecf20Sopenharmony_cistatic void udc_pop_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci int n, i, bl; 11398c2ecf20Sopenharmony_ci u16 *p16; 11408c2ecf20Sopenharmony_ci u32 *p32, tmp, cbytes; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci /* Use optimal data transfer method based on source address and size */ 11438c2ecf20Sopenharmony_ci switch (((uintptr_t) data) & 0x3) { 11448c2ecf20Sopenharmony_ci case 0: /* 32-bit aligned */ 11458c2ecf20Sopenharmony_ci p32 = (u32 *) data; 11468c2ecf20Sopenharmony_ci cbytes = (bytes & ~0x3); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci /* Copy 32-bit aligned data first */ 11498c2ecf20Sopenharmony_ci for (n = 0; n < cbytes; n += 4) 11508c2ecf20Sopenharmony_ci *p32++ = readl(USBD_RXDATA(udc->udp_baseaddr)); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci /* Handle any remaining bytes */ 11538c2ecf20Sopenharmony_ci bl = bytes - cbytes; 11548c2ecf20Sopenharmony_ci if (bl) { 11558c2ecf20Sopenharmony_ci tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 11568c2ecf20Sopenharmony_ci for (n = 0; n < bl; n++) 11578c2ecf20Sopenharmony_ci data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci break; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci case 1: /* 8-bit aligned */ 11638c2ecf20Sopenharmony_ci case 3: 11648c2ecf20Sopenharmony_ci /* Each byte has to be handled independently */ 11658c2ecf20Sopenharmony_ci for (n = 0; n < bytes; n += 4) { 11668c2ecf20Sopenharmony_ci tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci bl = bytes - n; 11698c2ecf20Sopenharmony_ci if (bl > 4) 11708c2ecf20Sopenharmony_ci bl = 4; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci for (i = 0; i < bl; i++) 11738c2ecf20Sopenharmony_ci data[n + i] = (u8) ((tmp >> (i * 8)) & 0xFF); 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci break; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci case 2: /* 16-bit aligned */ 11788c2ecf20Sopenharmony_ci p16 = (u16 *) data; 11798c2ecf20Sopenharmony_ci cbytes = (bytes & ~0x3); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* Copy 32-bit sized objects first with 16-bit alignment */ 11828c2ecf20Sopenharmony_ci for (n = 0; n < cbytes; n += 4) { 11838c2ecf20Sopenharmony_ci tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 11848c2ecf20Sopenharmony_ci *p16++ = (u16)(tmp & 0xFFFF); 11858c2ecf20Sopenharmony_ci *p16++ = (u16)((tmp >> 16) & 0xFFFF); 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci /* Handle any remaining bytes */ 11898c2ecf20Sopenharmony_ci bl = bytes - cbytes; 11908c2ecf20Sopenharmony_ci if (bl) { 11918c2ecf20Sopenharmony_ci tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 11928c2ecf20Sopenharmony_ci for (n = 0; n < bl; n++) 11938c2ecf20Sopenharmony_ci data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF); 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci break; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci/* Read data from the FIFO for an endpoint. This function is for endpoints (such 12008c2ecf20Sopenharmony_ci * as EP0) that don't use DMA. This function should only be called if a packet 12018c2ecf20Sopenharmony_ci * is known to be ready to read for the endpoint. Note that the endpoint must 12028c2ecf20Sopenharmony_ci * be selected in the protocol engine prior to this call. */ 12038c2ecf20Sopenharmony_cistatic u32 udc_read_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data, 12048c2ecf20Sopenharmony_ci u32 bytes) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci u32 tmpv; 12078c2ecf20Sopenharmony_ci int to = 1000; 12088c2ecf20Sopenharmony_ci u32 tmp, hwrep = ((hwep & 0x1E) << 1) | CTRL_RD_EN; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* Setup read of endpoint */ 12118c2ecf20Sopenharmony_ci writel(hwrep, USBD_CTRL(udc->udp_baseaddr)); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Wait until packet is ready */ 12148c2ecf20Sopenharmony_ci while ((((tmpv = readl(USBD_RXPLEN(udc->udp_baseaddr))) & 12158c2ecf20Sopenharmony_ci PKT_RDY) == 0) && (to > 0)) 12168c2ecf20Sopenharmony_ci to--; 12178c2ecf20Sopenharmony_ci if (!to) 12188c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "No packet ready on FIFO EP read\n"); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci /* Mask out count */ 12218c2ecf20Sopenharmony_ci tmp = tmpv & PKT_LNGTH_MASK; 12228c2ecf20Sopenharmony_ci if (bytes < tmp) 12238c2ecf20Sopenharmony_ci tmp = bytes; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci if ((tmp > 0) && (data != NULL)) 12268c2ecf20Sopenharmony_ci udc_pop_fifo(udc, (u8 *) data, tmp); 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr)); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* Clear the buffer */ 12318c2ecf20Sopenharmony_ci udc_clr_buffer_hwep(udc, hwep); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci return tmp; 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci/* Stuffs data into the FIFO, adjusts for alignment and data size */ 12378c2ecf20Sopenharmony_cistatic void udc_stuff_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci int n, i, bl; 12408c2ecf20Sopenharmony_ci u16 *p16; 12418c2ecf20Sopenharmony_ci u32 *p32, tmp, cbytes; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* Use optimal data transfer method based on source address and size */ 12448c2ecf20Sopenharmony_ci switch (((uintptr_t) data) & 0x3) { 12458c2ecf20Sopenharmony_ci case 0: /* 32-bit aligned */ 12468c2ecf20Sopenharmony_ci p32 = (u32 *) data; 12478c2ecf20Sopenharmony_ci cbytes = (bytes & ~0x3); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* Copy 32-bit aligned data first */ 12508c2ecf20Sopenharmony_ci for (n = 0; n < cbytes; n += 4) 12518c2ecf20Sopenharmony_ci writel(*p32++, USBD_TXDATA(udc->udp_baseaddr)); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci /* Handle any remaining bytes */ 12548c2ecf20Sopenharmony_ci bl = bytes - cbytes; 12558c2ecf20Sopenharmony_ci if (bl) { 12568c2ecf20Sopenharmony_ci tmp = 0; 12578c2ecf20Sopenharmony_ci for (n = 0; n < bl; n++) 12588c2ecf20Sopenharmony_ci tmp |= data[cbytes + n] << (n * 8); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci break; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci case 1: /* 8-bit aligned */ 12658c2ecf20Sopenharmony_ci case 3: 12668c2ecf20Sopenharmony_ci /* Each byte has to be handled independently */ 12678c2ecf20Sopenharmony_ci for (n = 0; n < bytes; n += 4) { 12688c2ecf20Sopenharmony_ci bl = bytes - n; 12698c2ecf20Sopenharmony_ci if (bl > 4) 12708c2ecf20Sopenharmony_ci bl = 4; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci tmp = 0; 12738c2ecf20Sopenharmony_ci for (i = 0; i < bl; i++) 12748c2ecf20Sopenharmony_ci tmp |= data[n + i] << (i * 8); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci break; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci case 2: /* 16-bit aligned */ 12818c2ecf20Sopenharmony_ci p16 = (u16 *) data; 12828c2ecf20Sopenharmony_ci cbytes = (bytes & ~0x3); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci /* Copy 32-bit aligned data first */ 12858c2ecf20Sopenharmony_ci for (n = 0; n < cbytes; n += 4) { 12868c2ecf20Sopenharmony_ci tmp = *p16++ & 0xFFFF; 12878c2ecf20Sopenharmony_ci tmp |= (*p16++ & 0xFFFF) << 16; 12888c2ecf20Sopenharmony_ci writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* Handle any remaining bytes */ 12928c2ecf20Sopenharmony_ci bl = bytes - cbytes; 12938c2ecf20Sopenharmony_ci if (bl) { 12948c2ecf20Sopenharmony_ci tmp = 0; 12958c2ecf20Sopenharmony_ci for (n = 0; n < bl; n++) 12968c2ecf20Sopenharmony_ci tmp |= data[cbytes + n] << (n * 8); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci break; 13018c2ecf20Sopenharmony_ci } 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci/* Write data to the FIFO for an endpoint. This function is for endpoints (such 13058c2ecf20Sopenharmony_ci * as EP0) that don't use DMA. Note that the endpoint must be selected in the 13068c2ecf20Sopenharmony_ci * protocol engine prior to this call. */ 13078c2ecf20Sopenharmony_cistatic void udc_write_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data, 13088c2ecf20Sopenharmony_ci u32 bytes) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci u32 hwwep = ((hwep & 0x1E) << 1) | CTRL_WR_EN; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if ((bytes > 0) && (data == NULL)) 13138c2ecf20Sopenharmony_ci return; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci /* Setup write of endpoint */ 13168c2ecf20Sopenharmony_ci writel(hwwep, USBD_CTRL(udc->udp_baseaddr)); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci writel(bytes, USBD_TXPLEN(udc->udp_baseaddr)); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci /* Need at least 1 byte to trigger TX */ 13218c2ecf20Sopenharmony_ci if (bytes == 0) 13228c2ecf20Sopenharmony_ci writel(0, USBD_TXDATA(udc->udp_baseaddr)); 13238c2ecf20Sopenharmony_ci else 13248c2ecf20Sopenharmony_ci udc_stuff_fifo(udc, (u8 *) data, bytes); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr)); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci udc_val_buffer_hwep(udc, hwep); 13298c2ecf20Sopenharmony_ci} 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci/* USB device reset - resets USB to a default state with just EP0 13328c2ecf20Sopenharmony_ci enabled */ 13338c2ecf20Sopenharmony_cistatic void uda_usb_reset(struct lpc32xx_udc *udc) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci u32 i = 0; 13368c2ecf20Sopenharmony_ci /* Re-init device controller and EP0 */ 13378c2ecf20Sopenharmony_ci udc_enable(udc); 13388c2ecf20Sopenharmony_ci udc->gadget.speed = USB_SPEED_FULL; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci for (i = 1; i < NUM_ENDPOINTS; i++) { 13418c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = &udc->ep[i]; 13428c2ecf20Sopenharmony_ci ep->req_pending = 0; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci} 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci/* Send a ZLP on EP0 */ 13478c2ecf20Sopenharmony_cistatic void udc_ep0_send_zlp(struct lpc32xx_udc *udc) 13488c2ecf20Sopenharmony_ci{ 13498c2ecf20Sopenharmony_ci udc_write_hwep(udc, EP_IN, NULL, 0); 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci/* Get current frame number */ 13538c2ecf20Sopenharmony_cistatic u16 udc_get_current_frame(struct lpc32xx_udc *udc) 13548c2ecf20Sopenharmony_ci{ 13558c2ecf20Sopenharmony_ci u16 flo, fhi; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_RD_FRAME); 13588c2ecf20Sopenharmony_ci flo = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME); 13598c2ecf20Sopenharmony_ci fhi = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci return (fhi << 8) | flo; 13628c2ecf20Sopenharmony_ci} 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci/* Set the device as configured - enables all endpoints */ 13658c2ecf20Sopenharmony_cistatic inline void udc_set_device_configured(struct lpc32xx_udc *udc) 13668c2ecf20Sopenharmony_ci{ 13678c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(CONF_DVICE)); 13688c2ecf20Sopenharmony_ci} 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci/* Set the device as unconfigured - disables all endpoints */ 13718c2ecf20Sopenharmony_cistatic inline void udc_set_device_unconfigured(struct lpc32xx_udc *udc) 13728c2ecf20Sopenharmony_ci{ 13738c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0)); 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci/* reinit == restore initial software state */ 13778c2ecf20Sopenharmony_cistatic void udc_reinit(struct lpc32xx_udc *udc) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci u32 i; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&udc->gadget.ep_list); 13828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci for (i = 0; i < NUM_ENDPOINTS; i++) { 13858c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = &udc->ep[i]; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci if (i != 0) 13888c2ecf20Sopenharmony_ci list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); 13898c2ecf20Sopenharmony_ci usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); 13908c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ep->queue); 13918c2ecf20Sopenharmony_ci ep->req_pending = 0; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 13958c2ecf20Sopenharmony_ci} 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci/* Must be called with lock */ 13988c2ecf20Sopenharmony_cistatic void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = ep->udc; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci list_del_init(&req->queue); 14038c2ecf20Sopenharmony_ci if (req->req.status == -EINPROGRESS) 14048c2ecf20Sopenharmony_ci req->req.status = status; 14058c2ecf20Sopenharmony_ci else 14068c2ecf20Sopenharmony_ci status = req->req.status; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci if (ep->lep) { 14098c2ecf20Sopenharmony_ci usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci /* Free DDs */ 14128c2ecf20Sopenharmony_ci udc_dd_free(udc, req->dd_desc_ptr); 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (status && status != -ESHUTDOWN) 14168c2ecf20Sopenharmony_ci ep_dbg(ep, "%s done %p, status %d\n", ep->ep.name, req, status); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci ep->req_pending = 0; 14198c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 14208c2ecf20Sopenharmony_ci usb_gadget_giveback_request(&ep->ep, &req->req); 14218c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 14228c2ecf20Sopenharmony_ci} 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci/* Must be called with lock */ 14258c2ecf20Sopenharmony_cistatic void nuke(struct lpc32xx_ep *ep, int status) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci while (!list_empty(&ep->queue)) { 14308c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 14318c2ecf20Sopenharmony_ci done(ep, req, status); 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (status == -ESHUTDOWN) { 14358c2ecf20Sopenharmony_ci uda_disable_hwepint(ep->udc, ep->hwep_num); 14368c2ecf20Sopenharmony_ci udc_disable_hwep(ep->udc, ep->hwep_num); 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci} 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci/* IN endpoint 0 transfer */ 14418c2ecf20Sopenharmony_cistatic int udc_ep0_in_req(struct lpc32xx_udc *udc) 14428c2ecf20Sopenharmony_ci{ 14438c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 14448c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep0 = &udc->ep[0]; 14458c2ecf20Sopenharmony_ci u32 tsend, ts = 0; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci if (list_empty(&ep0->queue)) 14488c2ecf20Sopenharmony_ci /* Nothing to send */ 14498c2ecf20Sopenharmony_ci return 0; 14508c2ecf20Sopenharmony_ci else 14518c2ecf20Sopenharmony_ci req = list_entry(ep0->queue.next, struct lpc32xx_request, 14528c2ecf20Sopenharmony_ci queue); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci tsend = ts = req->req.length - req->req.actual; 14558c2ecf20Sopenharmony_ci if (ts == 0) { 14568c2ecf20Sopenharmony_ci /* Send a ZLP */ 14578c2ecf20Sopenharmony_ci udc_ep0_send_zlp(udc); 14588c2ecf20Sopenharmony_ci done(ep0, req, 0); 14598c2ecf20Sopenharmony_ci return 1; 14608c2ecf20Sopenharmony_ci } else if (ts > ep0->ep.maxpacket) 14618c2ecf20Sopenharmony_ci ts = ep0->ep.maxpacket; /* Just send what we can */ 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* Write data to the EP0 FIFO and start transfer */ 14648c2ecf20Sopenharmony_ci udc_write_hwep(udc, EP_IN, (req->req.buf + req->req.actual), ts); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci /* Increment data pointer */ 14678c2ecf20Sopenharmony_ci req->req.actual += ts; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci if (tsend >= ep0->ep.maxpacket) 14708c2ecf20Sopenharmony_ci return 0; /* Stay in data transfer state */ 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci /* Transfer request is complete */ 14738c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 14748c2ecf20Sopenharmony_ci done(ep0, req, 0); 14758c2ecf20Sopenharmony_ci return 1; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci/* OUT endpoint 0 transfer */ 14798c2ecf20Sopenharmony_cistatic int udc_ep0_out_req(struct lpc32xx_udc *udc) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 14828c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep0 = &udc->ep[0]; 14838c2ecf20Sopenharmony_ci u32 tr, bufferspace; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci if (list_empty(&ep0->queue)) 14868c2ecf20Sopenharmony_ci return 0; 14878c2ecf20Sopenharmony_ci else 14888c2ecf20Sopenharmony_ci req = list_entry(ep0->queue.next, struct lpc32xx_request, 14898c2ecf20Sopenharmony_ci queue); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (req) { 14928c2ecf20Sopenharmony_ci if (req->req.length == 0) { 14938c2ecf20Sopenharmony_ci /* Just dequeue request */ 14948c2ecf20Sopenharmony_ci done(ep0, req, 0); 14958c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 14968c2ecf20Sopenharmony_ci return 1; 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci /* Get data from FIFO */ 15008c2ecf20Sopenharmony_ci bufferspace = req->req.length - req->req.actual; 15018c2ecf20Sopenharmony_ci if (bufferspace > ep0->ep.maxpacket) 15028c2ecf20Sopenharmony_ci bufferspace = ep0->ep.maxpacket; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* Copy data to buffer */ 15058c2ecf20Sopenharmony_ci prefetchw(req->req.buf + req->req.actual); 15068c2ecf20Sopenharmony_ci tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual, 15078c2ecf20Sopenharmony_ci bufferspace); 15088c2ecf20Sopenharmony_ci req->req.actual += bufferspace; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (tr < ep0->ep.maxpacket) { 15118c2ecf20Sopenharmony_ci /* This is the last packet */ 15128c2ecf20Sopenharmony_ci done(ep0, req, 0); 15138c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 15148c2ecf20Sopenharmony_ci return 1; 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci return 0; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci/* Must be called with lock */ 15228c2ecf20Sopenharmony_cistatic void stop_activity(struct lpc32xx_udc *udc) 15238c2ecf20Sopenharmony_ci{ 15248c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver = udc->driver; 15258c2ecf20Sopenharmony_ci int i; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci if (udc->gadget.speed == USB_SPEED_UNKNOWN) 15288c2ecf20Sopenharmony_ci driver = NULL; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci udc->gadget.speed = USB_SPEED_UNKNOWN; 15318c2ecf20Sopenharmony_ci udc->suspended = 0; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci for (i = 0; i < NUM_ENDPOINTS; i++) { 15348c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = &udc->ep[i]; 15358c2ecf20Sopenharmony_ci nuke(ep, -ESHUTDOWN); 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci if (driver) { 15388c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 15398c2ecf20Sopenharmony_ci driver->disconnect(&udc->gadget); 15408c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci isp1301_pullup_enable(udc, 0, 0); 15448c2ecf20Sopenharmony_ci udc_disable(udc); 15458c2ecf20Sopenharmony_ci udc_reinit(udc); 15468c2ecf20Sopenharmony_ci} 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci/* 15498c2ecf20Sopenharmony_ci * Activate or kill host pullup 15508c2ecf20Sopenharmony_ci * Can be called with or without lock 15518c2ecf20Sopenharmony_ci */ 15528c2ecf20Sopenharmony_cistatic void pullup(struct lpc32xx_udc *udc, int is_on) 15538c2ecf20Sopenharmony_ci{ 15548c2ecf20Sopenharmony_ci if (!udc->clocked) 15558c2ecf20Sopenharmony_ci return; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci if (!udc->enabled || !udc->vbus) 15588c2ecf20Sopenharmony_ci is_on = 0; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci if (is_on != udc->pullup) 15618c2ecf20Sopenharmony_ci isp1301_pullup_enable(udc, is_on, 0); 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci/* Must be called without lock */ 15658c2ecf20Sopenharmony_cistatic int lpc32xx_ep_disable(struct usb_ep *_ep) 15668c2ecf20Sopenharmony_ci{ 15678c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 15688c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = ep->udc; 15698c2ecf20Sopenharmony_ci unsigned long flags; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if ((ep->hwep_num_base == 0) || (ep->hwep_num == 0)) 15728c2ecf20Sopenharmony_ci return -EINVAL; 15738c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci nuke(ep, -ESHUTDOWN); 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci /* Clear all DMA statuses for this EP */ 15788c2ecf20Sopenharmony_ci udc_ep_dma_disable(udc, ep->hwep_num); 15798c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr)); 15808c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr)); 15818c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 15828c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr)); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci /* Remove the DD pointer in the UDCA */ 15858c2ecf20Sopenharmony_ci udc->udca_v_base[ep->hwep_num] = 0; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci /* Disable and reset endpoint and interrupt */ 15888c2ecf20Sopenharmony_ci uda_clear_hwepint(udc, ep->hwep_num); 15898c2ecf20Sopenharmony_ci udc_unrealize_hwep(udc, ep->hwep_num); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci ep->hwep_num = 0; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci atomic_dec(&udc->enabled_ep_cnt); 15968c2ecf20Sopenharmony_ci wake_up(&udc->ep_disable_wait_queue); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci return 0; 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci/* Must be called without lock */ 16028c2ecf20Sopenharmony_cistatic int lpc32xx_ep_enable(struct usb_ep *_ep, 16038c2ecf20Sopenharmony_ci const struct usb_endpoint_descriptor *desc) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 16068c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc; 16078c2ecf20Sopenharmony_ci u16 maxpacket; 16088c2ecf20Sopenharmony_ci u32 tmp; 16098c2ecf20Sopenharmony_ci unsigned long flags; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci /* Verify EP data */ 16128c2ecf20Sopenharmony_ci if ((!_ep) || (!ep) || (!desc) || 16138c2ecf20Sopenharmony_ci (desc->bDescriptorType != USB_DT_ENDPOINT)) 16148c2ecf20Sopenharmony_ci return -EINVAL; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci udc = ep->udc; 16178c2ecf20Sopenharmony_ci maxpacket = usb_endpoint_maxp(desc); 16188c2ecf20Sopenharmony_ci if ((maxpacket == 0) || (maxpacket > ep->maxpacket)) { 16198c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "bad ep descriptor's packet size\n"); 16208c2ecf20Sopenharmony_ci return -EINVAL; 16218c2ecf20Sopenharmony_ci } 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci /* Don't touch EP0 */ 16248c2ecf20Sopenharmony_ci if (ep->hwep_num_base == 0) { 16258c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Can't re-enable EP0!!!\n"); 16268c2ecf20Sopenharmony_ci return -EINVAL; 16278c2ecf20Sopenharmony_ci } 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* Is driver ready? */ 16308c2ecf20Sopenharmony_ci if ((!udc->driver) || (udc->gadget.speed == USB_SPEED_UNKNOWN)) { 16318c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "bogus device state\n"); 16328c2ecf20Sopenharmony_ci return -ESHUTDOWN; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; 16368c2ecf20Sopenharmony_ci switch (tmp) { 16378c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_CONTROL: 16388c2ecf20Sopenharmony_ci return -EINVAL; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_INT: 16418c2ecf20Sopenharmony_ci if (maxpacket > ep->maxpacket) { 16428c2ecf20Sopenharmony_ci dev_dbg(udc->dev, 16438c2ecf20Sopenharmony_ci "Bad INT endpoint maxpacket %d\n", maxpacket); 16448c2ecf20Sopenharmony_ci return -EINVAL; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci break; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: 16498c2ecf20Sopenharmony_ci switch (maxpacket) { 16508c2ecf20Sopenharmony_ci case 8: 16518c2ecf20Sopenharmony_ci case 16: 16528c2ecf20Sopenharmony_ci case 32: 16538c2ecf20Sopenharmony_ci case 64: 16548c2ecf20Sopenharmony_ci break; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci default: 16578c2ecf20Sopenharmony_ci dev_dbg(udc->dev, 16588c2ecf20Sopenharmony_ci "Bad BULK endpoint maxpacket %d\n", maxpacket); 16598c2ecf20Sopenharmony_ci return -EINVAL; 16608c2ecf20Sopenharmony_ci } 16618c2ecf20Sopenharmony_ci break; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: 16648c2ecf20Sopenharmony_ci break; 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci /* Initialize endpoint to match the selected descriptor */ 16698c2ecf20Sopenharmony_ci ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; 16708c2ecf20Sopenharmony_ci ep->ep.maxpacket = maxpacket; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci /* Map hardware endpoint from base and direction */ 16738c2ecf20Sopenharmony_ci if (ep->is_in) 16748c2ecf20Sopenharmony_ci /* IN endpoints are offset 1 from the OUT endpoint */ 16758c2ecf20Sopenharmony_ci ep->hwep_num = ep->hwep_num_base + EP_IN; 16768c2ecf20Sopenharmony_ci else 16778c2ecf20Sopenharmony_ci ep->hwep_num = ep->hwep_num_base; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci ep_dbg(ep, "EP enabled: %s, HW:%d, MP:%d IN:%d\n", ep->ep.name, 16808c2ecf20Sopenharmony_ci ep->hwep_num, maxpacket, (ep->is_in == 1)); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci /* Realize the endpoint, interrupt is enabled later when 16838c2ecf20Sopenharmony_ci * buffers are queued, IN EPs will NAK until buffers are ready */ 16848c2ecf20Sopenharmony_ci udc_realize_hwep(udc, ep->hwep_num, ep->ep.maxpacket); 16858c2ecf20Sopenharmony_ci udc_clr_buffer_hwep(udc, ep->hwep_num); 16868c2ecf20Sopenharmony_ci uda_disable_hwepint(udc, ep->hwep_num); 16878c2ecf20Sopenharmony_ci udc_clrstall_hwep(udc, ep->hwep_num); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci /* Clear all DMA statuses for this EP */ 16908c2ecf20Sopenharmony_ci udc_ep_dma_disable(udc, ep->hwep_num); 16918c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr)); 16928c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr)); 16938c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 16948c2ecf20Sopenharmony_ci writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr)); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci atomic_inc(&udc->enabled_ep_cnt); 16998c2ecf20Sopenharmony_ci return 0; 17008c2ecf20Sopenharmony_ci} 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci/* 17038c2ecf20Sopenharmony_ci * Allocate a USB request list 17048c2ecf20Sopenharmony_ci * Can be called with or without lock 17058c2ecf20Sopenharmony_ci */ 17068c2ecf20Sopenharmony_cistatic struct usb_request *lpc32xx_ep_alloc_request(struct usb_ep *_ep, 17078c2ecf20Sopenharmony_ci gfp_t gfp_flags) 17088c2ecf20Sopenharmony_ci{ 17098c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci req = kzalloc(sizeof(struct lpc32xx_request), gfp_flags); 17128c2ecf20Sopenharmony_ci if (!req) 17138c2ecf20Sopenharmony_ci return NULL; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&req->queue); 17168c2ecf20Sopenharmony_ci return &req->req; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci/* 17208c2ecf20Sopenharmony_ci * De-allocate a USB request list 17218c2ecf20Sopenharmony_ci * Can be called with or without lock 17228c2ecf20Sopenharmony_ci */ 17238c2ecf20Sopenharmony_cistatic void lpc32xx_ep_free_request(struct usb_ep *_ep, 17248c2ecf20Sopenharmony_ci struct usb_request *_req) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci req = container_of(_req, struct lpc32xx_request, req); 17298c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&req->queue)); 17308c2ecf20Sopenharmony_ci kfree(req); 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci/* Must be called without lock */ 17348c2ecf20Sopenharmony_cistatic int lpc32xx_ep_queue(struct usb_ep *_ep, 17358c2ecf20Sopenharmony_ci struct usb_request *_req, gfp_t gfp_flags) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 17388c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep; 17398c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc; 17408c2ecf20Sopenharmony_ci unsigned long flags; 17418c2ecf20Sopenharmony_ci int status = 0; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci req = container_of(_req, struct lpc32xx_request, req); 17448c2ecf20Sopenharmony_ci ep = container_of(_ep, struct lpc32xx_ep, ep); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci if (!_ep || !_req || !_req->complete || !_req->buf || 17478c2ecf20Sopenharmony_ci !list_empty(&req->queue)) 17488c2ecf20Sopenharmony_ci return -EINVAL; 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci udc = ep->udc; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci if (udc->gadget.speed == USB_SPEED_UNKNOWN) 17538c2ecf20Sopenharmony_ci return -EPIPE; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci if (ep->lep) { 17568c2ecf20Sopenharmony_ci struct lpc32xx_usbd_dd_gad *dd; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci status = usb_gadget_map_request(&udc->gadget, _req, ep->is_in); 17598c2ecf20Sopenharmony_ci if (status) 17608c2ecf20Sopenharmony_ci return status; 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci /* For the request, build a list of DDs */ 17638c2ecf20Sopenharmony_ci dd = udc_dd_alloc(udc); 17648c2ecf20Sopenharmony_ci if (!dd) { 17658c2ecf20Sopenharmony_ci /* Error allocating DD */ 17668c2ecf20Sopenharmony_ci return -ENOMEM; 17678c2ecf20Sopenharmony_ci } 17688c2ecf20Sopenharmony_ci req->dd_desc_ptr = dd; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci /* Setup the DMA descriptor */ 17718c2ecf20Sopenharmony_ci dd->dd_next_phy = dd->dd_next_v = 0; 17728c2ecf20Sopenharmony_ci dd->dd_buffer_addr = req->req.dma; 17738c2ecf20Sopenharmony_ci dd->dd_status = 0; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci /* Special handling for ISO EPs */ 17768c2ecf20Sopenharmony_ci if (ep->eptype == EP_ISO_TYPE) { 17778c2ecf20Sopenharmony_ci dd->dd_setup = DD_SETUP_ISO_EP | 17788c2ecf20Sopenharmony_ci DD_SETUP_PACKETLEN(0) | 17798c2ecf20Sopenharmony_ci DD_SETUP_DMALENBYTES(1); 17808c2ecf20Sopenharmony_ci dd->dd_iso_ps_mem_addr = dd->this_dma + 24; 17818c2ecf20Sopenharmony_ci if (ep->is_in) 17828c2ecf20Sopenharmony_ci dd->iso_status[0] = req->req.length; 17838c2ecf20Sopenharmony_ci else 17848c2ecf20Sopenharmony_ci dd->iso_status[0] = 0; 17858c2ecf20Sopenharmony_ci } else 17868c2ecf20Sopenharmony_ci dd->dd_setup = DD_SETUP_PACKETLEN(ep->ep.maxpacket) | 17878c2ecf20Sopenharmony_ci DD_SETUP_DMALENBYTES(req->req.length); 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci ep_dbg(ep, "%s queue req %p len %d buf %p (in=%d) z=%d\n", _ep->name, 17918c2ecf20Sopenharmony_ci _req, _req->length, _req->buf, ep->is_in, _req->zero); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci _req->status = -EINPROGRESS; 17968c2ecf20Sopenharmony_ci _req->actual = 0; 17978c2ecf20Sopenharmony_ci req->send_zlp = _req->zero; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci /* Kickstart empty queues */ 18008c2ecf20Sopenharmony_ci if (list_empty(&ep->queue)) { 18018c2ecf20Sopenharmony_ci list_add_tail(&req->queue, &ep->queue); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (ep->hwep_num_base == 0) { 18048c2ecf20Sopenharmony_ci /* Handle expected data direction */ 18058c2ecf20Sopenharmony_ci if (ep->is_in) { 18068c2ecf20Sopenharmony_ci /* IN packet to host */ 18078c2ecf20Sopenharmony_ci udc->ep0state = DATA_IN; 18088c2ecf20Sopenharmony_ci status = udc_ep0_in_req(udc); 18098c2ecf20Sopenharmony_ci } else { 18108c2ecf20Sopenharmony_ci /* OUT packet from host */ 18118c2ecf20Sopenharmony_ci udc->ep0state = DATA_OUT; 18128c2ecf20Sopenharmony_ci status = udc_ep0_out_req(udc); 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci } else if (ep->is_in) { 18158c2ecf20Sopenharmony_ci /* IN packet to host and kick off transfer */ 18168c2ecf20Sopenharmony_ci if (!ep->req_pending) 18178c2ecf20Sopenharmony_ci udc_ep_in_req_dma(udc, ep); 18188c2ecf20Sopenharmony_ci } else 18198c2ecf20Sopenharmony_ci /* OUT packet from host and kick off list */ 18208c2ecf20Sopenharmony_ci if (!ep->req_pending) 18218c2ecf20Sopenharmony_ci udc_ep_out_req_dma(udc, ep); 18228c2ecf20Sopenharmony_ci } else 18238c2ecf20Sopenharmony_ci list_add_tail(&req->queue, &ep->queue); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci return (status < 0) ? status : 0; 18288c2ecf20Sopenharmony_ci} 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci/* Must be called without lock */ 18318c2ecf20Sopenharmony_cistatic int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep; 18348c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 18358c2ecf20Sopenharmony_ci unsigned long flags; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci ep = container_of(_ep, struct lpc32xx_ep, ep); 18388c2ecf20Sopenharmony_ci if (!_ep || ep->hwep_num_base == 0) 18398c2ecf20Sopenharmony_ci return -EINVAL; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci spin_lock_irqsave(&ep->udc->lock, flags); 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci /* make sure it's actually queued on this endpoint */ 18448c2ecf20Sopenharmony_ci list_for_each_entry(req, &ep->queue, queue) { 18458c2ecf20Sopenharmony_ci if (&req->req == _req) 18468c2ecf20Sopenharmony_ci break; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci if (&req->req != _req) { 18498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ep->udc->lock, flags); 18508c2ecf20Sopenharmony_ci return -EINVAL; 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci done(ep, req, -ECONNRESET); 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ep->udc->lock, flags); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci return 0; 18588c2ecf20Sopenharmony_ci} 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci/* Must be called without lock */ 18618c2ecf20Sopenharmony_cistatic int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value) 18628c2ecf20Sopenharmony_ci{ 18638c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 18648c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc; 18658c2ecf20Sopenharmony_ci unsigned long flags; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci if ((!ep) || (ep->hwep_num <= 1)) 18688c2ecf20Sopenharmony_ci return -EINVAL; 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci /* Don't halt an IN EP */ 18718c2ecf20Sopenharmony_ci if (ep->is_in) 18728c2ecf20Sopenharmony_ci return -EAGAIN; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci udc = ep->udc; 18758c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci if (value == 1) { 18788c2ecf20Sopenharmony_ci /* stall */ 18798c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num), 18808c2ecf20Sopenharmony_ci DAT_WR_BYTE(EP_STAT_ST)); 18818c2ecf20Sopenharmony_ci } else { 18828c2ecf20Sopenharmony_ci /* End stall */ 18838c2ecf20Sopenharmony_ci ep->wedge = 0; 18848c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num), 18858c2ecf20Sopenharmony_ci DAT_WR_BYTE(0)); 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci return 0; 18918c2ecf20Sopenharmony_ci} 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci/* set the halt feature and ignores clear requests */ 18948c2ecf20Sopenharmony_cistatic int lpc32xx_ep_set_wedge(struct usb_ep *_ep) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci if (!_ep || !ep->udc) 18998c2ecf20Sopenharmony_ci return -EINVAL; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci ep->wedge = 1; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci return usb_ep_set_halt(_ep); 19048c2ecf20Sopenharmony_ci} 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_cistatic const struct usb_ep_ops lpc32xx_ep_ops = { 19078c2ecf20Sopenharmony_ci .enable = lpc32xx_ep_enable, 19088c2ecf20Sopenharmony_ci .disable = lpc32xx_ep_disable, 19098c2ecf20Sopenharmony_ci .alloc_request = lpc32xx_ep_alloc_request, 19108c2ecf20Sopenharmony_ci .free_request = lpc32xx_ep_free_request, 19118c2ecf20Sopenharmony_ci .queue = lpc32xx_ep_queue, 19128c2ecf20Sopenharmony_ci .dequeue = lpc32xx_ep_dequeue, 19138c2ecf20Sopenharmony_ci .set_halt = lpc32xx_ep_set_halt, 19148c2ecf20Sopenharmony_ci .set_wedge = lpc32xx_ep_set_wedge, 19158c2ecf20Sopenharmony_ci}; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci/* Send a ZLP on a non-0 IN EP */ 19188c2ecf20Sopenharmony_cistatic void udc_send_in_zlp(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 19198c2ecf20Sopenharmony_ci{ 19208c2ecf20Sopenharmony_ci /* Clear EP status */ 19218c2ecf20Sopenharmony_ci udc_clearep_getsts(udc, ep->hwep_num); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci /* Send ZLP via FIFO mechanism */ 19248c2ecf20Sopenharmony_ci udc_write_hwep(udc, ep->hwep_num, NULL, 0); 19258c2ecf20Sopenharmony_ci} 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci/* 19288c2ecf20Sopenharmony_ci * Handle EP completion for ZLP 19298c2ecf20Sopenharmony_ci * This function will only be called when a delayed ZLP needs to be sent out 19308c2ecf20Sopenharmony_ci * after a DMA transfer has filled both buffers. 19318c2ecf20Sopenharmony_ci */ 19328c2ecf20Sopenharmony_cistatic void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 19338c2ecf20Sopenharmony_ci{ 19348c2ecf20Sopenharmony_ci u32 epstatus; 19358c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci if (ep->hwep_num <= 0) 19388c2ecf20Sopenharmony_ci return; 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci uda_clear_hwepint(udc, ep->hwep_num); 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci /* If this interrupt isn't enabled, return now */ 19438c2ecf20Sopenharmony_ci if (!(udc->enabled_hwepints & (1 << ep->hwep_num))) 19448c2ecf20Sopenharmony_ci return; 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* Get endpoint status */ 19478c2ecf20Sopenharmony_ci epstatus = udc_clearep_getsts(udc, ep->hwep_num); 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* 19508c2ecf20Sopenharmony_ci * This should never happen, but protect against writing to the 19518c2ecf20Sopenharmony_ci * buffer when full. 19528c2ecf20Sopenharmony_ci */ 19538c2ecf20Sopenharmony_ci if (epstatus & EP_SEL_F) 19548c2ecf20Sopenharmony_ci return; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci if (ep->is_in) { 19578c2ecf20Sopenharmony_ci udc_send_in_zlp(udc, ep); 19588c2ecf20Sopenharmony_ci uda_disable_hwepint(udc, ep->hwep_num); 19598c2ecf20Sopenharmony_ci } else 19608c2ecf20Sopenharmony_ci return; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci /* If there isn't a request waiting, something went wrong */ 19638c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 19648c2ecf20Sopenharmony_ci if (req) { 19658c2ecf20Sopenharmony_ci done(ep, req, 0); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci /* Start another request if ready */ 19688c2ecf20Sopenharmony_ci if (!list_empty(&ep->queue)) { 19698c2ecf20Sopenharmony_ci if (ep->is_in) 19708c2ecf20Sopenharmony_ci udc_ep_in_req_dma(udc, ep); 19718c2ecf20Sopenharmony_ci else 19728c2ecf20Sopenharmony_ci udc_ep_out_req_dma(udc, ep); 19738c2ecf20Sopenharmony_ci } else 19748c2ecf20Sopenharmony_ci ep->req_pending = 0; 19758c2ecf20Sopenharmony_ci } 19768c2ecf20Sopenharmony_ci} 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci/* DMA end of transfer completion */ 19808c2ecf20Sopenharmony_cistatic void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 19818c2ecf20Sopenharmony_ci{ 19828c2ecf20Sopenharmony_ci u32 status; 19838c2ecf20Sopenharmony_ci struct lpc32xx_request *req; 19848c2ecf20Sopenharmony_ci struct lpc32xx_usbd_dd_gad *dd; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FILES 19878c2ecf20Sopenharmony_ci ep->totalints++; 19888c2ecf20Sopenharmony_ci#endif 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 19918c2ecf20Sopenharmony_ci if (!req) { 19928c2ecf20Sopenharmony_ci ep_err(ep, "DMA interrupt on no req!\n"); 19938c2ecf20Sopenharmony_ci return; 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci dd = req->dd_desc_ptr; 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* DMA descriptor should always be retired for this call */ 19988c2ecf20Sopenharmony_ci if (!(dd->dd_status & DD_STATUS_DD_RETIRED)) 19998c2ecf20Sopenharmony_ci ep_warn(ep, "DMA descriptor did not retire\n"); 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci /* Disable DMA */ 20028c2ecf20Sopenharmony_ci udc_ep_dma_disable(udc, ep->hwep_num); 20038c2ecf20Sopenharmony_ci writel((1 << ep->hwep_num), USBD_EOTINTCLR(udc->udp_baseaddr)); 20048c2ecf20Sopenharmony_ci writel((1 << ep->hwep_num), USBD_NDDRTINTCLR(udc->udp_baseaddr)); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci /* System error? */ 20078c2ecf20Sopenharmony_ci if (readl(USBD_SYSERRTINTST(udc->udp_baseaddr)) & 20088c2ecf20Sopenharmony_ci (1 << ep->hwep_num)) { 20098c2ecf20Sopenharmony_ci writel((1 << ep->hwep_num), 20108c2ecf20Sopenharmony_ci USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 20118c2ecf20Sopenharmony_ci ep_err(ep, "AHB critical error!\n"); 20128c2ecf20Sopenharmony_ci ep->req_pending = 0; 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci /* The error could have occurred on a packet of a multipacket 20158c2ecf20Sopenharmony_ci * transfer, so recovering the transfer is not possible. Close 20168c2ecf20Sopenharmony_ci * the request with an error */ 20178c2ecf20Sopenharmony_ci done(ep, req, -ECONNABORTED); 20188c2ecf20Sopenharmony_ci return; 20198c2ecf20Sopenharmony_ci } 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci /* Handle the current DD's status */ 20228c2ecf20Sopenharmony_ci status = dd->dd_status; 20238c2ecf20Sopenharmony_ci switch (status & DD_STATUS_STS_MASK) { 20248c2ecf20Sopenharmony_ci case DD_STATUS_STS_NS: 20258c2ecf20Sopenharmony_ci /* DD not serviced? This shouldn't happen! */ 20268c2ecf20Sopenharmony_ci ep->req_pending = 0; 20278c2ecf20Sopenharmony_ci ep_err(ep, "DMA critical EP error: DD not serviced (0x%x)!\n", 20288c2ecf20Sopenharmony_ci status); 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci done(ep, req, -ECONNABORTED); 20318c2ecf20Sopenharmony_ci return; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci case DD_STATUS_STS_BS: 20348c2ecf20Sopenharmony_ci /* Interrupt only fires on EOT - This shouldn't happen! */ 20358c2ecf20Sopenharmony_ci ep->req_pending = 0; 20368c2ecf20Sopenharmony_ci ep_err(ep, "DMA critical EP error: EOT prior to service completion (0x%x)!\n", 20378c2ecf20Sopenharmony_ci status); 20388c2ecf20Sopenharmony_ci done(ep, req, -ECONNABORTED); 20398c2ecf20Sopenharmony_ci return; 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci case DD_STATUS_STS_NC: 20428c2ecf20Sopenharmony_ci case DD_STATUS_STS_DUR: 20438c2ecf20Sopenharmony_ci /* Really just a short packet, not an underrun */ 20448c2ecf20Sopenharmony_ci /* This is a good status and what we expect */ 20458c2ecf20Sopenharmony_ci break; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci default: 20488c2ecf20Sopenharmony_ci /* Data overrun, system error, or unknown */ 20498c2ecf20Sopenharmony_ci ep->req_pending = 0; 20508c2ecf20Sopenharmony_ci ep_err(ep, "DMA critical EP error: System error (0x%x)!\n", 20518c2ecf20Sopenharmony_ci status); 20528c2ecf20Sopenharmony_ci done(ep, req, -ECONNABORTED); 20538c2ecf20Sopenharmony_ci return; 20548c2ecf20Sopenharmony_ci } 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_ci /* ISO endpoints are handled differently */ 20578c2ecf20Sopenharmony_ci if (ep->eptype == EP_ISO_TYPE) { 20588c2ecf20Sopenharmony_ci if (ep->is_in) 20598c2ecf20Sopenharmony_ci req->req.actual = req->req.length; 20608c2ecf20Sopenharmony_ci else 20618c2ecf20Sopenharmony_ci req->req.actual = dd->iso_status[0] & 0xFFFF; 20628c2ecf20Sopenharmony_ci } else 20638c2ecf20Sopenharmony_ci req->req.actual += DD_STATUS_CURDMACNT(status); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci /* Send a ZLP if necessary. This will be done for non-int 20668c2ecf20Sopenharmony_ci * packets which have a size that is a divisor of MAXP */ 20678c2ecf20Sopenharmony_ci if (req->send_zlp) { 20688c2ecf20Sopenharmony_ci /* 20698c2ecf20Sopenharmony_ci * If at least 1 buffer is available, send the ZLP now. 20708c2ecf20Sopenharmony_ci * Otherwise, the ZLP send needs to be deferred until a 20718c2ecf20Sopenharmony_ci * buffer is available. 20728c2ecf20Sopenharmony_ci */ 20738c2ecf20Sopenharmony_ci if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) { 20748c2ecf20Sopenharmony_ci udc_clearep_getsts(udc, ep->hwep_num); 20758c2ecf20Sopenharmony_ci uda_enable_hwepint(udc, ep->hwep_num); 20768c2ecf20Sopenharmony_ci udc_clearep_getsts(udc, ep->hwep_num); 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci /* Let the EP interrupt handle the ZLP */ 20798c2ecf20Sopenharmony_ci return; 20808c2ecf20Sopenharmony_ci } else 20818c2ecf20Sopenharmony_ci udc_send_in_zlp(udc, ep); 20828c2ecf20Sopenharmony_ci } 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci /* Transfer request is complete */ 20858c2ecf20Sopenharmony_ci done(ep, req, 0); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci /* Start another request if ready */ 20888c2ecf20Sopenharmony_ci udc_clearep_getsts(udc, ep->hwep_num); 20898c2ecf20Sopenharmony_ci if (!list_empty((&ep->queue))) { 20908c2ecf20Sopenharmony_ci if (ep->is_in) 20918c2ecf20Sopenharmony_ci udc_ep_in_req_dma(udc, ep); 20928c2ecf20Sopenharmony_ci else 20938c2ecf20Sopenharmony_ci udc_ep_out_req_dma(udc, ep); 20948c2ecf20Sopenharmony_ci } else 20958c2ecf20Sopenharmony_ci ep->req_pending = 0; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci} 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci/* 21008c2ecf20Sopenharmony_ci * 21018c2ecf20Sopenharmony_ci * Endpoint 0 functions 21028c2ecf20Sopenharmony_ci * 21038c2ecf20Sopenharmony_ci */ 21048c2ecf20Sopenharmony_cistatic void udc_handle_dev(struct lpc32xx_udc *udc) 21058c2ecf20Sopenharmony_ci{ 21068c2ecf20Sopenharmony_ci u32 tmp; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_GET_DEV_STAT); 21098c2ecf20Sopenharmony_ci tmp = udc_protocol_cmd_r(udc, DAT_GET_DEV_STAT); 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci if (tmp & DEV_RST) 21128c2ecf20Sopenharmony_ci uda_usb_reset(udc); 21138c2ecf20Sopenharmony_ci else if (tmp & DEV_CON_CH) 21148c2ecf20Sopenharmony_ci uda_power_event(udc, (tmp & DEV_CON)); 21158c2ecf20Sopenharmony_ci else if (tmp & DEV_SUS_CH) { 21168c2ecf20Sopenharmony_ci if (tmp & DEV_SUS) { 21178c2ecf20Sopenharmony_ci if (udc->vbus == 0) 21188c2ecf20Sopenharmony_ci stop_activity(udc); 21198c2ecf20Sopenharmony_ci else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) && 21208c2ecf20Sopenharmony_ci udc->driver) { 21218c2ecf20Sopenharmony_ci /* Power down transceiver */ 21228c2ecf20Sopenharmony_ci udc->poweron = 0; 21238c2ecf20Sopenharmony_ci schedule_work(&udc->pullup_job); 21248c2ecf20Sopenharmony_ci uda_resm_susp_event(udc, 1); 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci } else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) && 21278c2ecf20Sopenharmony_ci udc->driver && udc->vbus) { 21288c2ecf20Sopenharmony_ci uda_resm_susp_event(udc, 0); 21298c2ecf20Sopenharmony_ci /* Power up transceiver */ 21308c2ecf20Sopenharmony_ci udc->poweron = 1; 21318c2ecf20Sopenharmony_ci schedule_work(&udc->pullup_job); 21328c2ecf20Sopenharmony_ci } 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci} 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_cistatic int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex) 21378c2ecf20Sopenharmony_ci{ 21388c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep; 21398c2ecf20Sopenharmony_ci u32 ep0buff = 0, tmp; 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci switch (reqtype & USB_RECIP_MASK) { 21428c2ecf20Sopenharmony_ci case USB_RECIP_INTERFACE: 21438c2ecf20Sopenharmony_ci break; /* Not supported */ 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci case USB_RECIP_DEVICE: 21468c2ecf20Sopenharmony_ci ep0buff = udc->gadget.is_selfpowered; 21478c2ecf20Sopenharmony_ci if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP)) 21488c2ecf20Sopenharmony_ci ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP); 21498c2ecf20Sopenharmony_ci break; 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci case USB_RECIP_ENDPOINT: 21528c2ecf20Sopenharmony_ci tmp = wIndex & USB_ENDPOINT_NUMBER_MASK; 21538c2ecf20Sopenharmony_ci ep = &udc->ep[tmp]; 21548c2ecf20Sopenharmony_ci if ((tmp == 0) || (tmp >= NUM_ENDPOINTS)) 21558c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (wIndex & USB_DIR_IN) { 21588c2ecf20Sopenharmony_ci if (!ep->is_in) 21598c2ecf20Sopenharmony_ci return -EOPNOTSUPP; /* Something's wrong */ 21608c2ecf20Sopenharmony_ci } else if (ep->is_in) 21618c2ecf20Sopenharmony_ci return -EOPNOTSUPP; /* Not an IN endpoint */ 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci /* Get status of the endpoint */ 21648c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_SEL_EP(ep->hwep_num)); 21658c2ecf20Sopenharmony_ci tmp = udc_protocol_cmd_r(udc, DAT_SEL_EP(ep->hwep_num)); 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci if (tmp & EP_SEL_ST) 21688c2ecf20Sopenharmony_ci ep0buff = (1 << USB_ENDPOINT_HALT); 21698c2ecf20Sopenharmony_ci else 21708c2ecf20Sopenharmony_ci ep0buff = 0; 21718c2ecf20Sopenharmony_ci break; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci default: 21748c2ecf20Sopenharmony_ci break; 21758c2ecf20Sopenharmony_ci } 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci /* Return data */ 21788c2ecf20Sopenharmony_ci udc_write_hwep(udc, EP_IN, &ep0buff, 2); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci return 0; 21818c2ecf20Sopenharmony_ci} 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_cistatic void udc_handle_ep0_setup(struct lpc32xx_udc *udc) 21848c2ecf20Sopenharmony_ci{ 21858c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep, *ep0 = &udc->ep[0]; 21868c2ecf20Sopenharmony_ci struct usb_ctrlrequest ctrlpkt; 21878c2ecf20Sopenharmony_ci int i, bytes; 21888c2ecf20Sopenharmony_ci u16 wIndex, wValue, reqtype, req, tmp; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci /* Nuke previous transfers */ 21918c2ecf20Sopenharmony_ci nuke(ep0, -EPROTO); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci /* Get setup packet */ 21948c2ecf20Sopenharmony_ci bytes = udc_read_hwep(udc, EP_OUT, (u32 *) &ctrlpkt, 8); 21958c2ecf20Sopenharmony_ci if (bytes != 8) { 21968c2ecf20Sopenharmony_ci ep_warn(ep0, "Incorrectly sized setup packet (s/b 8, is %d)!\n", 21978c2ecf20Sopenharmony_ci bytes); 21988c2ecf20Sopenharmony_ci return; 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci /* Native endianness */ 22028c2ecf20Sopenharmony_ci wIndex = le16_to_cpu(ctrlpkt.wIndex); 22038c2ecf20Sopenharmony_ci wValue = le16_to_cpu(ctrlpkt.wValue); 22048c2ecf20Sopenharmony_ci reqtype = le16_to_cpu(ctrlpkt.bRequestType); 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci /* Set direction of EP0 */ 22078c2ecf20Sopenharmony_ci if (likely(reqtype & USB_DIR_IN)) 22088c2ecf20Sopenharmony_ci ep0->is_in = 1; 22098c2ecf20Sopenharmony_ci else 22108c2ecf20Sopenharmony_ci ep0->is_in = 0; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci /* Handle SETUP packet */ 22138c2ecf20Sopenharmony_ci req = le16_to_cpu(ctrlpkt.bRequest); 22148c2ecf20Sopenharmony_ci switch (req) { 22158c2ecf20Sopenharmony_ci case USB_REQ_CLEAR_FEATURE: 22168c2ecf20Sopenharmony_ci case USB_REQ_SET_FEATURE: 22178c2ecf20Sopenharmony_ci switch (reqtype) { 22188c2ecf20Sopenharmony_ci case (USB_TYPE_STANDARD | USB_RECIP_DEVICE): 22198c2ecf20Sopenharmony_ci if (wValue != USB_DEVICE_REMOTE_WAKEUP) 22208c2ecf20Sopenharmony_ci goto stall; /* Nothing else handled */ 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci /* Tell board about event */ 22238c2ecf20Sopenharmony_ci if (req == USB_REQ_CLEAR_FEATURE) 22248c2ecf20Sopenharmony_ci udc->dev_status &= 22258c2ecf20Sopenharmony_ci ~(1 << USB_DEVICE_REMOTE_WAKEUP); 22268c2ecf20Sopenharmony_ci else 22278c2ecf20Sopenharmony_ci udc->dev_status |= 22288c2ecf20Sopenharmony_ci (1 << USB_DEVICE_REMOTE_WAKEUP); 22298c2ecf20Sopenharmony_ci uda_remwkp_cgh(udc); 22308c2ecf20Sopenharmony_ci goto zlp_send; 22318c2ecf20Sopenharmony_ci 22328c2ecf20Sopenharmony_ci case (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): 22338c2ecf20Sopenharmony_ci tmp = wIndex & USB_ENDPOINT_NUMBER_MASK; 22348c2ecf20Sopenharmony_ci if ((wValue != USB_ENDPOINT_HALT) || 22358c2ecf20Sopenharmony_ci (tmp >= NUM_ENDPOINTS)) 22368c2ecf20Sopenharmony_ci break; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci /* Find hardware endpoint from logical endpoint */ 22398c2ecf20Sopenharmony_ci ep = &udc->ep[tmp]; 22408c2ecf20Sopenharmony_ci tmp = ep->hwep_num; 22418c2ecf20Sopenharmony_ci if (tmp == 0) 22428c2ecf20Sopenharmony_ci break; 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci if (req == USB_REQ_SET_FEATURE) 22458c2ecf20Sopenharmony_ci udc_stall_hwep(udc, tmp); 22468c2ecf20Sopenharmony_ci else if (!ep->wedge) 22478c2ecf20Sopenharmony_ci udc_clrstall_hwep(udc, tmp); 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci goto zlp_send; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci default: 22528c2ecf20Sopenharmony_ci break; 22538c2ecf20Sopenharmony_ci } 22548c2ecf20Sopenharmony_ci break; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci case USB_REQ_SET_ADDRESS: 22578c2ecf20Sopenharmony_ci if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { 22588c2ecf20Sopenharmony_ci udc_set_address(udc, wValue); 22598c2ecf20Sopenharmony_ci goto zlp_send; 22608c2ecf20Sopenharmony_ci } 22618c2ecf20Sopenharmony_ci break; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci case USB_REQ_GET_STATUS: 22648c2ecf20Sopenharmony_ci udc_get_status(udc, reqtype, wIndex); 22658c2ecf20Sopenharmony_ci return; 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci default: 22688c2ecf20Sopenharmony_ci break; /* Let GadgetFS handle the descriptor instead */ 22698c2ecf20Sopenharmony_ci } 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci if (likely(udc->driver)) { 22728c2ecf20Sopenharmony_ci /* device-2-host (IN) or no data setup command, process 22738c2ecf20Sopenharmony_ci * immediately */ 22748c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 22758c2ecf20Sopenharmony_ci i = udc->driver->setup(&udc->gadget, &ctrlpkt); 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 22788c2ecf20Sopenharmony_ci if (req == USB_REQ_SET_CONFIGURATION) { 22798c2ecf20Sopenharmony_ci /* Configuration is set after endpoints are realized */ 22808c2ecf20Sopenharmony_ci if (wValue) { 22818c2ecf20Sopenharmony_ci /* Set configuration */ 22828c2ecf20Sopenharmony_ci udc_set_device_configured(udc); 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_MODE, 22858c2ecf20Sopenharmony_ci DAT_WR_BYTE(AP_CLK | 22868c2ecf20Sopenharmony_ci INAK_BI | INAK_II)); 22878c2ecf20Sopenharmony_ci } else { 22888c2ecf20Sopenharmony_ci /* Clear configuration */ 22898c2ecf20Sopenharmony_ci udc_set_device_unconfigured(udc); 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci /* Disable NAK interrupts */ 22928c2ecf20Sopenharmony_ci udc_protocol_cmd_data_w(udc, CMD_SET_MODE, 22938c2ecf20Sopenharmony_ci DAT_WR_BYTE(AP_CLK)); 22948c2ecf20Sopenharmony_ci } 22958c2ecf20Sopenharmony_ci } 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci if (i < 0) { 22988c2ecf20Sopenharmony_ci /* setup processing failed, force stall */ 22998c2ecf20Sopenharmony_ci dev_dbg(udc->dev, 23008c2ecf20Sopenharmony_ci "req %02x.%02x protocol STALL; stat %d\n", 23018c2ecf20Sopenharmony_ci reqtype, req, i); 23028c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 23038c2ecf20Sopenharmony_ci goto stall; 23048c2ecf20Sopenharmony_ci } 23058c2ecf20Sopenharmony_ci } 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci if (!ep0->is_in) 23088c2ecf20Sopenharmony_ci udc_ep0_send_zlp(udc); /* ZLP IN packet on data phase */ 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_ci return; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_cistall: 23138c2ecf20Sopenharmony_ci udc_stall_hwep(udc, EP_IN); 23148c2ecf20Sopenharmony_ci return; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_cizlp_send: 23178c2ecf20Sopenharmony_ci udc_ep0_send_zlp(udc); 23188c2ecf20Sopenharmony_ci return; 23198c2ecf20Sopenharmony_ci} 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci/* IN endpoint 0 transfer */ 23228c2ecf20Sopenharmony_cistatic void udc_handle_ep0_in(struct lpc32xx_udc *udc) 23238c2ecf20Sopenharmony_ci{ 23248c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep0 = &udc->ep[0]; 23258c2ecf20Sopenharmony_ci u32 epstatus; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci /* Clear EP interrupt */ 23288c2ecf20Sopenharmony_ci epstatus = udc_clearep_getsts(udc, EP_IN); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FILES 23318c2ecf20Sopenharmony_ci ep0->totalints++; 23328c2ecf20Sopenharmony_ci#endif 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci /* Stalled? Clear stall and reset buffers */ 23358c2ecf20Sopenharmony_ci if (epstatus & EP_SEL_ST) { 23368c2ecf20Sopenharmony_ci udc_clrstall_hwep(udc, EP_IN); 23378c2ecf20Sopenharmony_ci nuke(ep0, -ECONNABORTED); 23388c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 23398c2ecf20Sopenharmony_ci return; 23408c2ecf20Sopenharmony_ci } 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci /* Is a buffer available? */ 23438c2ecf20Sopenharmony_ci if (!(epstatus & EP_SEL_F)) { 23448c2ecf20Sopenharmony_ci /* Handle based on current state */ 23458c2ecf20Sopenharmony_ci if (udc->ep0state == DATA_IN) 23468c2ecf20Sopenharmony_ci udc_ep0_in_req(udc); 23478c2ecf20Sopenharmony_ci else { 23488c2ecf20Sopenharmony_ci /* Unknown state for EP0 oe end of DATA IN phase */ 23498c2ecf20Sopenharmony_ci nuke(ep0, -ECONNABORTED); 23508c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 23518c2ecf20Sopenharmony_ci } 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci} 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci/* OUT endpoint 0 transfer */ 23568c2ecf20Sopenharmony_cistatic void udc_handle_ep0_out(struct lpc32xx_udc *udc) 23578c2ecf20Sopenharmony_ci{ 23588c2ecf20Sopenharmony_ci struct lpc32xx_ep *ep0 = &udc->ep[0]; 23598c2ecf20Sopenharmony_ci u32 epstatus; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci /* Clear EP interrupt */ 23628c2ecf20Sopenharmony_ci epstatus = udc_clearep_getsts(udc, EP_OUT); 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_GADGET_DEBUG_FILES 23668c2ecf20Sopenharmony_ci ep0->totalints++; 23678c2ecf20Sopenharmony_ci#endif 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci /* Stalled? */ 23708c2ecf20Sopenharmony_ci if (epstatus & EP_SEL_ST) { 23718c2ecf20Sopenharmony_ci udc_clrstall_hwep(udc, EP_OUT); 23728c2ecf20Sopenharmony_ci nuke(ep0, -ECONNABORTED); 23738c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 23748c2ecf20Sopenharmony_ci return; 23758c2ecf20Sopenharmony_ci } 23768c2ecf20Sopenharmony_ci 23778c2ecf20Sopenharmony_ci /* A NAK may occur if a packet couldn't be received yet */ 23788c2ecf20Sopenharmony_ci if (epstatus & EP_SEL_EPN) 23798c2ecf20Sopenharmony_ci return; 23808c2ecf20Sopenharmony_ci /* Setup packet incoming? */ 23818c2ecf20Sopenharmony_ci if (epstatus & EP_SEL_STP) { 23828c2ecf20Sopenharmony_ci nuke(ep0, 0); 23838c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 23848c2ecf20Sopenharmony_ci } 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci /* Data available? */ 23878c2ecf20Sopenharmony_ci if (epstatus & EP_SEL_F) 23888c2ecf20Sopenharmony_ci /* Handle based on current state */ 23898c2ecf20Sopenharmony_ci switch (udc->ep0state) { 23908c2ecf20Sopenharmony_ci case WAIT_FOR_SETUP: 23918c2ecf20Sopenharmony_ci udc_handle_ep0_setup(udc); 23928c2ecf20Sopenharmony_ci break; 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_ci case DATA_OUT: 23958c2ecf20Sopenharmony_ci udc_ep0_out_req(udc); 23968c2ecf20Sopenharmony_ci break; 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci default: 23998c2ecf20Sopenharmony_ci /* Unknown state for EP0 */ 24008c2ecf20Sopenharmony_ci nuke(ep0, -ECONNABORTED); 24018c2ecf20Sopenharmony_ci udc->ep0state = WAIT_FOR_SETUP; 24028c2ecf20Sopenharmony_ci } 24038c2ecf20Sopenharmony_ci} 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci/* Must be called without lock */ 24068c2ecf20Sopenharmony_cistatic int lpc32xx_get_frame(struct usb_gadget *gadget) 24078c2ecf20Sopenharmony_ci{ 24088c2ecf20Sopenharmony_ci int frame; 24098c2ecf20Sopenharmony_ci unsigned long flags; 24108c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = to_udc(gadget); 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci if (!udc->clocked) 24138c2ecf20Sopenharmony_ci return -EINVAL; 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci frame = (int) udc_get_current_frame(udc); 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci return frame; 24228c2ecf20Sopenharmony_ci} 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_cistatic int lpc32xx_wakeup(struct usb_gadget *gadget) 24258c2ecf20Sopenharmony_ci{ 24268c2ecf20Sopenharmony_ci return -ENOTSUPP; 24278c2ecf20Sopenharmony_ci} 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_cistatic int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on) 24308c2ecf20Sopenharmony_ci{ 24318c2ecf20Sopenharmony_ci gadget->is_selfpowered = (is_on != 0); 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci return 0; 24348c2ecf20Sopenharmony_ci} 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci/* 24378c2ecf20Sopenharmony_ci * vbus is here! turn everything on that's ready 24388c2ecf20Sopenharmony_ci * Must be called without lock 24398c2ecf20Sopenharmony_ci */ 24408c2ecf20Sopenharmony_cistatic int lpc32xx_vbus_session(struct usb_gadget *gadget, int is_active) 24418c2ecf20Sopenharmony_ci{ 24428c2ecf20Sopenharmony_ci unsigned long flags; 24438c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = to_udc(gadget); 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci /* Doesn't need lock */ 24488c2ecf20Sopenharmony_ci if (udc->driver) { 24498c2ecf20Sopenharmony_ci udc_clk_set(udc, 1); 24508c2ecf20Sopenharmony_ci udc_enable(udc); 24518c2ecf20Sopenharmony_ci pullup(udc, is_active); 24528c2ecf20Sopenharmony_ci } else { 24538c2ecf20Sopenharmony_ci stop_activity(udc); 24548c2ecf20Sopenharmony_ci pullup(udc, 0); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 24578c2ecf20Sopenharmony_ci /* 24588c2ecf20Sopenharmony_ci * Wait for all the endpoints to disable, 24598c2ecf20Sopenharmony_ci * before disabling clocks. Don't wait if 24608c2ecf20Sopenharmony_ci * endpoints are not enabled. 24618c2ecf20Sopenharmony_ci */ 24628c2ecf20Sopenharmony_ci if (atomic_read(&udc->enabled_ep_cnt)) 24638c2ecf20Sopenharmony_ci wait_event_interruptible(udc->ep_disable_wait_queue, 24648c2ecf20Sopenharmony_ci (atomic_read(&udc->enabled_ep_cnt) == 0)); 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci udc_clk_set(udc, 0); 24698c2ecf20Sopenharmony_ci } 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci return 0; 24748c2ecf20Sopenharmony_ci} 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci/* Can be called with or without lock */ 24778c2ecf20Sopenharmony_cistatic int lpc32xx_pullup(struct usb_gadget *gadget, int is_on) 24788c2ecf20Sopenharmony_ci{ 24798c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = to_udc(gadget); 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci /* Doesn't need lock */ 24828c2ecf20Sopenharmony_ci pullup(udc, is_on); 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci return 0; 24858c2ecf20Sopenharmony_ci} 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_cistatic int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *); 24888c2ecf20Sopenharmony_cistatic int lpc32xx_stop(struct usb_gadget *); 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_cistatic const struct usb_gadget_ops lpc32xx_udc_ops = { 24918c2ecf20Sopenharmony_ci .get_frame = lpc32xx_get_frame, 24928c2ecf20Sopenharmony_ci .wakeup = lpc32xx_wakeup, 24938c2ecf20Sopenharmony_ci .set_selfpowered = lpc32xx_set_selfpowered, 24948c2ecf20Sopenharmony_ci .vbus_session = lpc32xx_vbus_session, 24958c2ecf20Sopenharmony_ci .pullup = lpc32xx_pullup, 24968c2ecf20Sopenharmony_ci .udc_start = lpc32xx_start, 24978c2ecf20Sopenharmony_ci .udc_stop = lpc32xx_stop, 24988c2ecf20Sopenharmony_ci}; 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_cistatic void nop_release(struct device *dev) 25018c2ecf20Sopenharmony_ci{ 25028c2ecf20Sopenharmony_ci /* nothing to free */ 25038c2ecf20Sopenharmony_ci} 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_cistatic const struct lpc32xx_udc controller_template = { 25068c2ecf20Sopenharmony_ci .gadget = { 25078c2ecf20Sopenharmony_ci .ops = &lpc32xx_udc_ops, 25088c2ecf20Sopenharmony_ci .name = driver_name, 25098c2ecf20Sopenharmony_ci .dev = { 25108c2ecf20Sopenharmony_ci .init_name = "gadget", 25118c2ecf20Sopenharmony_ci .release = nop_release, 25128c2ecf20Sopenharmony_ci } 25138c2ecf20Sopenharmony_ci }, 25148c2ecf20Sopenharmony_ci .ep[0] = { 25158c2ecf20Sopenharmony_ci .ep = { 25168c2ecf20Sopenharmony_ci .name = "ep0", 25178c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 25188c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, 25198c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 25208c2ecf20Sopenharmony_ci }, 25218c2ecf20Sopenharmony_ci .maxpacket = 64, 25228c2ecf20Sopenharmony_ci .hwep_num_base = 0, 25238c2ecf20Sopenharmony_ci .hwep_num = 0, /* Can be 0 or 1, has special handling */ 25248c2ecf20Sopenharmony_ci .lep = 0, 25258c2ecf20Sopenharmony_ci .eptype = EP_CTL_TYPE, 25268c2ecf20Sopenharmony_ci }, 25278c2ecf20Sopenharmony_ci .ep[1] = { 25288c2ecf20Sopenharmony_ci .ep = { 25298c2ecf20Sopenharmony_ci .name = "ep1-int", 25308c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 25318c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 25328c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 25338c2ecf20Sopenharmony_ci }, 25348c2ecf20Sopenharmony_ci .maxpacket = 64, 25358c2ecf20Sopenharmony_ci .hwep_num_base = 2, 25368c2ecf20Sopenharmony_ci .hwep_num = 0, /* 2 or 3, will be set later */ 25378c2ecf20Sopenharmony_ci .lep = 1, 25388c2ecf20Sopenharmony_ci .eptype = EP_INT_TYPE, 25398c2ecf20Sopenharmony_ci }, 25408c2ecf20Sopenharmony_ci .ep[2] = { 25418c2ecf20Sopenharmony_ci .ep = { 25428c2ecf20Sopenharmony_ci .name = "ep2-bulk", 25438c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 25448c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 25458c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 25468c2ecf20Sopenharmony_ci }, 25478c2ecf20Sopenharmony_ci .maxpacket = 64, 25488c2ecf20Sopenharmony_ci .hwep_num_base = 4, 25498c2ecf20Sopenharmony_ci .hwep_num = 0, /* 4 or 5, will be set later */ 25508c2ecf20Sopenharmony_ci .lep = 2, 25518c2ecf20Sopenharmony_ci .eptype = EP_BLK_TYPE, 25528c2ecf20Sopenharmony_ci }, 25538c2ecf20Sopenharmony_ci .ep[3] = { 25548c2ecf20Sopenharmony_ci .ep = { 25558c2ecf20Sopenharmony_ci .name = "ep3-iso", 25568c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 25578c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 25588c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 25598c2ecf20Sopenharmony_ci }, 25608c2ecf20Sopenharmony_ci .maxpacket = 1023, 25618c2ecf20Sopenharmony_ci .hwep_num_base = 6, 25628c2ecf20Sopenharmony_ci .hwep_num = 0, /* 6 or 7, will be set later */ 25638c2ecf20Sopenharmony_ci .lep = 3, 25648c2ecf20Sopenharmony_ci .eptype = EP_ISO_TYPE, 25658c2ecf20Sopenharmony_ci }, 25668c2ecf20Sopenharmony_ci .ep[4] = { 25678c2ecf20Sopenharmony_ci .ep = { 25688c2ecf20Sopenharmony_ci .name = "ep4-int", 25698c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 25708c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 25718c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 25728c2ecf20Sopenharmony_ci }, 25738c2ecf20Sopenharmony_ci .maxpacket = 64, 25748c2ecf20Sopenharmony_ci .hwep_num_base = 8, 25758c2ecf20Sopenharmony_ci .hwep_num = 0, /* 8 or 9, will be set later */ 25768c2ecf20Sopenharmony_ci .lep = 4, 25778c2ecf20Sopenharmony_ci .eptype = EP_INT_TYPE, 25788c2ecf20Sopenharmony_ci }, 25798c2ecf20Sopenharmony_ci .ep[5] = { 25808c2ecf20Sopenharmony_ci .ep = { 25818c2ecf20Sopenharmony_ci .name = "ep5-bulk", 25828c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 25838c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 25848c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 25858c2ecf20Sopenharmony_ci }, 25868c2ecf20Sopenharmony_ci .maxpacket = 64, 25878c2ecf20Sopenharmony_ci .hwep_num_base = 10, 25888c2ecf20Sopenharmony_ci .hwep_num = 0, /* 10 or 11, will be set later */ 25898c2ecf20Sopenharmony_ci .lep = 5, 25908c2ecf20Sopenharmony_ci .eptype = EP_BLK_TYPE, 25918c2ecf20Sopenharmony_ci }, 25928c2ecf20Sopenharmony_ci .ep[6] = { 25938c2ecf20Sopenharmony_ci .ep = { 25948c2ecf20Sopenharmony_ci .name = "ep6-iso", 25958c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 25968c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 25978c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 25988c2ecf20Sopenharmony_ci }, 25998c2ecf20Sopenharmony_ci .maxpacket = 1023, 26008c2ecf20Sopenharmony_ci .hwep_num_base = 12, 26018c2ecf20Sopenharmony_ci .hwep_num = 0, /* 12 or 13, will be set later */ 26028c2ecf20Sopenharmony_ci .lep = 6, 26038c2ecf20Sopenharmony_ci .eptype = EP_ISO_TYPE, 26048c2ecf20Sopenharmony_ci }, 26058c2ecf20Sopenharmony_ci .ep[7] = { 26068c2ecf20Sopenharmony_ci .ep = { 26078c2ecf20Sopenharmony_ci .name = "ep7-int", 26088c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 26098c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 26108c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 26118c2ecf20Sopenharmony_ci }, 26128c2ecf20Sopenharmony_ci .maxpacket = 64, 26138c2ecf20Sopenharmony_ci .hwep_num_base = 14, 26148c2ecf20Sopenharmony_ci .hwep_num = 0, 26158c2ecf20Sopenharmony_ci .lep = 7, 26168c2ecf20Sopenharmony_ci .eptype = EP_INT_TYPE, 26178c2ecf20Sopenharmony_ci }, 26188c2ecf20Sopenharmony_ci .ep[8] = { 26198c2ecf20Sopenharmony_ci .ep = { 26208c2ecf20Sopenharmony_ci .name = "ep8-bulk", 26218c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 26228c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 26238c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 26248c2ecf20Sopenharmony_ci }, 26258c2ecf20Sopenharmony_ci .maxpacket = 64, 26268c2ecf20Sopenharmony_ci .hwep_num_base = 16, 26278c2ecf20Sopenharmony_ci .hwep_num = 0, 26288c2ecf20Sopenharmony_ci .lep = 8, 26298c2ecf20Sopenharmony_ci .eptype = EP_BLK_TYPE, 26308c2ecf20Sopenharmony_ci }, 26318c2ecf20Sopenharmony_ci .ep[9] = { 26328c2ecf20Sopenharmony_ci .ep = { 26338c2ecf20Sopenharmony_ci .name = "ep9-iso", 26348c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 26358c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 26368c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 26378c2ecf20Sopenharmony_ci }, 26388c2ecf20Sopenharmony_ci .maxpacket = 1023, 26398c2ecf20Sopenharmony_ci .hwep_num_base = 18, 26408c2ecf20Sopenharmony_ci .hwep_num = 0, 26418c2ecf20Sopenharmony_ci .lep = 9, 26428c2ecf20Sopenharmony_ci .eptype = EP_ISO_TYPE, 26438c2ecf20Sopenharmony_ci }, 26448c2ecf20Sopenharmony_ci .ep[10] = { 26458c2ecf20Sopenharmony_ci .ep = { 26468c2ecf20Sopenharmony_ci .name = "ep10-int", 26478c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 26488c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 26498c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 26508c2ecf20Sopenharmony_ci }, 26518c2ecf20Sopenharmony_ci .maxpacket = 64, 26528c2ecf20Sopenharmony_ci .hwep_num_base = 20, 26538c2ecf20Sopenharmony_ci .hwep_num = 0, 26548c2ecf20Sopenharmony_ci .lep = 10, 26558c2ecf20Sopenharmony_ci .eptype = EP_INT_TYPE, 26568c2ecf20Sopenharmony_ci }, 26578c2ecf20Sopenharmony_ci .ep[11] = { 26588c2ecf20Sopenharmony_ci .ep = { 26598c2ecf20Sopenharmony_ci .name = "ep11-bulk", 26608c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 26618c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 26628c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 26638c2ecf20Sopenharmony_ci }, 26648c2ecf20Sopenharmony_ci .maxpacket = 64, 26658c2ecf20Sopenharmony_ci .hwep_num_base = 22, 26668c2ecf20Sopenharmony_ci .hwep_num = 0, 26678c2ecf20Sopenharmony_ci .lep = 11, 26688c2ecf20Sopenharmony_ci .eptype = EP_BLK_TYPE, 26698c2ecf20Sopenharmony_ci }, 26708c2ecf20Sopenharmony_ci .ep[12] = { 26718c2ecf20Sopenharmony_ci .ep = { 26728c2ecf20Sopenharmony_ci .name = "ep12-iso", 26738c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 26748c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 26758c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 26768c2ecf20Sopenharmony_ci }, 26778c2ecf20Sopenharmony_ci .maxpacket = 1023, 26788c2ecf20Sopenharmony_ci .hwep_num_base = 24, 26798c2ecf20Sopenharmony_ci .hwep_num = 0, 26808c2ecf20Sopenharmony_ci .lep = 12, 26818c2ecf20Sopenharmony_ci .eptype = EP_ISO_TYPE, 26828c2ecf20Sopenharmony_ci }, 26838c2ecf20Sopenharmony_ci .ep[13] = { 26848c2ecf20Sopenharmony_ci .ep = { 26858c2ecf20Sopenharmony_ci .name = "ep13-int", 26868c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 26878c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 26888c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 26898c2ecf20Sopenharmony_ci }, 26908c2ecf20Sopenharmony_ci .maxpacket = 64, 26918c2ecf20Sopenharmony_ci .hwep_num_base = 26, 26928c2ecf20Sopenharmony_ci .hwep_num = 0, 26938c2ecf20Sopenharmony_ci .lep = 13, 26948c2ecf20Sopenharmony_ci .eptype = EP_INT_TYPE, 26958c2ecf20Sopenharmony_ci }, 26968c2ecf20Sopenharmony_ci .ep[14] = { 26978c2ecf20Sopenharmony_ci .ep = { 26988c2ecf20Sopenharmony_ci .name = "ep14-bulk", 26998c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 27008c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 27018c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 27028c2ecf20Sopenharmony_ci }, 27038c2ecf20Sopenharmony_ci .maxpacket = 64, 27048c2ecf20Sopenharmony_ci .hwep_num_base = 28, 27058c2ecf20Sopenharmony_ci .hwep_num = 0, 27068c2ecf20Sopenharmony_ci .lep = 14, 27078c2ecf20Sopenharmony_ci .eptype = EP_BLK_TYPE, 27088c2ecf20Sopenharmony_ci }, 27098c2ecf20Sopenharmony_ci .ep[15] = { 27108c2ecf20Sopenharmony_ci .ep = { 27118c2ecf20Sopenharmony_ci .name = "ep15-bulk", 27128c2ecf20Sopenharmony_ci .ops = &lpc32xx_ep_ops, 27138c2ecf20Sopenharmony_ci .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 27148c2ecf20Sopenharmony_ci USB_EP_CAPS_DIR_ALL), 27158c2ecf20Sopenharmony_ci }, 27168c2ecf20Sopenharmony_ci .maxpacket = 1023, 27178c2ecf20Sopenharmony_ci .hwep_num_base = 30, 27188c2ecf20Sopenharmony_ci .hwep_num = 0, 27198c2ecf20Sopenharmony_ci .lep = 15, 27208c2ecf20Sopenharmony_ci .eptype = EP_BLK_TYPE, 27218c2ecf20Sopenharmony_ci }, 27228c2ecf20Sopenharmony_ci}; 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_ci/* ISO and status interrupts */ 27258c2ecf20Sopenharmony_cistatic irqreturn_t lpc32xx_usb_lp_irq(int irq, void *_udc) 27268c2ecf20Sopenharmony_ci{ 27278c2ecf20Sopenharmony_ci u32 tmp, devstat; 27288c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = _udc; 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci /* Read the device status register */ 27338c2ecf20Sopenharmony_ci devstat = readl(USBD_DEVINTST(udc->udp_baseaddr)); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci devstat &= ~USBD_EP_FAST; 27368c2ecf20Sopenharmony_ci writel(devstat, USBD_DEVINTCLR(udc->udp_baseaddr)); 27378c2ecf20Sopenharmony_ci devstat = devstat & udc->enabled_devints; 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci /* Device specific handling needed? */ 27408c2ecf20Sopenharmony_ci if (devstat & USBD_DEV_STAT) 27418c2ecf20Sopenharmony_ci udc_handle_dev(udc); 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci /* Start of frame? (devstat & FRAME_INT): 27448c2ecf20Sopenharmony_ci * The frame interrupt isn't really needed for ISO support, 27458c2ecf20Sopenharmony_ci * as the driver will queue the necessary packets */ 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci /* Error? */ 27488c2ecf20Sopenharmony_ci if (devstat & ERR_INT) { 27498c2ecf20Sopenharmony_ci /* All types of errors, from cable removal during transfer to 27508c2ecf20Sopenharmony_ci * misc protocol and bit errors. These are mostly for just info, 27518c2ecf20Sopenharmony_ci * as the USB hardware will work around these. If these errors 27528c2ecf20Sopenharmony_ci * happen alot, something is wrong. */ 27538c2ecf20Sopenharmony_ci udc_protocol_cmd_w(udc, CMD_RD_ERR_STAT); 27548c2ecf20Sopenharmony_ci tmp = udc_protocol_cmd_r(udc, DAT_RD_ERR_STAT); 27558c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "Device error (0x%x)!\n", tmp); 27568c2ecf20Sopenharmony_ci } 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci return IRQ_HANDLED; 27618c2ecf20Sopenharmony_ci} 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci/* EP interrupts */ 27648c2ecf20Sopenharmony_cistatic irqreturn_t lpc32xx_usb_hp_irq(int irq, void *_udc) 27658c2ecf20Sopenharmony_ci{ 27668c2ecf20Sopenharmony_ci u32 tmp; 27678c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = _udc; 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci /* Read the device status register */ 27728c2ecf20Sopenharmony_ci writel(USBD_EP_FAST, USBD_DEVINTCLR(udc->udp_baseaddr)); 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci /* Endpoints */ 27758c2ecf20Sopenharmony_ci tmp = readl(USBD_EPINTST(udc->udp_baseaddr)); 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci /* Special handling for EP0 */ 27788c2ecf20Sopenharmony_ci if (tmp & (EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) { 27798c2ecf20Sopenharmony_ci /* Handle EP0 IN */ 27808c2ecf20Sopenharmony_ci if (tmp & (EP_MASK_SEL(0, EP_IN))) 27818c2ecf20Sopenharmony_ci udc_handle_ep0_in(udc); 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci /* Handle EP0 OUT */ 27848c2ecf20Sopenharmony_ci if (tmp & (EP_MASK_SEL(0, EP_OUT))) 27858c2ecf20Sopenharmony_ci udc_handle_ep0_out(udc); 27868c2ecf20Sopenharmony_ci } 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci /* All other EPs */ 27898c2ecf20Sopenharmony_ci if (tmp & ~(EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) { 27908c2ecf20Sopenharmony_ci int i; 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci /* Handle other EP interrupts */ 27938c2ecf20Sopenharmony_ci for (i = 1; i < NUM_ENDPOINTS; i++) { 27948c2ecf20Sopenharmony_ci if (tmp & (1 << udc->ep[i].hwep_num)) 27958c2ecf20Sopenharmony_ci udc_handle_eps(udc, &udc->ep[i]); 27968c2ecf20Sopenharmony_ci } 27978c2ecf20Sopenharmony_ci } 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 28028c2ecf20Sopenharmony_ci} 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_cistatic irqreturn_t lpc32xx_usb_devdma_irq(int irq, void *_udc) 28058c2ecf20Sopenharmony_ci{ 28068c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = _udc; 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci int i; 28098c2ecf20Sopenharmony_ci u32 tmp; 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci /* Handle EP DMA EOT interrupts */ 28148c2ecf20Sopenharmony_ci tmp = readl(USBD_EOTINTST(udc->udp_baseaddr)) | 28158c2ecf20Sopenharmony_ci (readl(USBD_EPDMAST(udc->udp_baseaddr)) & 28168c2ecf20Sopenharmony_ci readl(USBD_NDDRTINTST(udc->udp_baseaddr))) | 28178c2ecf20Sopenharmony_ci readl(USBD_SYSERRTINTST(udc->udp_baseaddr)); 28188c2ecf20Sopenharmony_ci for (i = 1; i < NUM_ENDPOINTS; i++) { 28198c2ecf20Sopenharmony_ci if (tmp & (1 << udc->ep[i].hwep_num)) 28208c2ecf20Sopenharmony_ci udc_handle_dma_ep(udc, &udc->ep[i]); 28218c2ecf20Sopenharmony_ci } 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci return IRQ_HANDLED; 28268c2ecf20Sopenharmony_ci} 28278c2ecf20Sopenharmony_ci 28288c2ecf20Sopenharmony_ci/* 28298c2ecf20Sopenharmony_ci * 28308c2ecf20Sopenharmony_ci * VBUS detection, pullup handler, and Gadget cable state notification 28318c2ecf20Sopenharmony_ci * 28328c2ecf20Sopenharmony_ci */ 28338c2ecf20Sopenharmony_cistatic void vbus_work(struct lpc32xx_udc *udc) 28348c2ecf20Sopenharmony_ci{ 28358c2ecf20Sopenharmony_ci u8 value; 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci if (udc->enabled != 0) { 28388c2ecf20Sopenharmony_ci /* Discharge VBUS real quick */ 28398c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 28408c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG); 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci /* Give VBUS some time (100mS) to discharge */ 28438c2ecf20Sopenharmony_ci msleep(100); 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci /* Disable VBUS discharge resistor */ 28468c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 28478c2ecf20Sopenharmony_ci ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, 28488c2ecf20Sopenharmony_ci OTG1_VBUS_DISCHRG); 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci /* Clear interrupt */ 28518c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 28528c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_LATCH | 28538c2ecf20Sopenharmony_ci ISP1301_I2C_REG_CLEAR_ADDR, ~0); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci /* Get the VBUS status from the transceiver */ 28568c2ecf20Sopenharmony_ci value = i2c_smbus_read_byte_data(udc->isp1301_i2c_client, 28578c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_SOURCE); 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci /* VBUS on or off? */ 28608c2ecf20Sopenharmony_ci if (value & INT_SESS_VLD) 28618c2ecf20Sopenharmony_ci udc->vbus = 1; 28628c2ecf20Sopenharmony_ci else 28638c2ecf20Sopenharmony_ci udc->vbus = 0; 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci /* VBUS changed? */ 28668c2ecf20Sopenharmony_ci if (udc->last_vbus != udc->vbus) { 28678c2ecf20Sopenharmony_ci udc->last_vbus = udc->vbus; 28688c2ecf20Sopenharmony_ci lpc32xx_vbus_session(&udc->gadget, udc->vbus); 28698c2ecf20Sopenharmony_ci } 28708c2ecf20Sopenharmony_ci } 28718c2ecf20Sopenharmony_ci} 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_cistatic irqreturn_t lpc32xx_usb_vbus_irq(int irq, void *_udc) 28748c2ecf20Sopenharmony_ci{ 28758c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = _udc; 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci vbus_work(udc); 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci return IRQ_HANDLED; 28808c2ecf20Sopenharmony_ci} 28818c2ecf20Sopenharmony_ci 28828c2ecf20Sopenharmony_cistatic int lpc32xx_start(struct usb_gadget *gadget, 28838c2ecf20Sopenharmony_ci struct usb_gadget_driver *driver) 28848c2ecf20Sopenharmony_ci{ 28858c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = to_udc(gadget); 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) { 28888c2ecf20Sopenharmony_ci dev_err(udc->dev, "bad parameter.\n"); 28898c2ecf20Sopenharmony_ci return -EINVAL; 28908c2ecf20Sopenharmony_ci } 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci if (udc->driver) { 28938c2ecf20Sopenharmony_ci dev_err(udc->dev, "UDC already has a gadget driver\n"); 28948c2ecf20Sopenharmony_ci return -EBUSY; 28958c2ecf20Sopenharmony_ci } 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci udc->driver = driver; 28988c2ecf20Sopenharmony_ci udc->gadget.dev.of_node = udc->dev->of_node; 28998c2ecf20Sopenharmony_ci udc->enabled = 1; 29008c2ecf20Sopenharmony_ci udc->gadget.is_selfpowered = 1; 29018c2ecf20Sopenharmony_ci udc->vbus = 0; 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci /* Force VBUS process once to check for cable insertion */ 29048c2ecf20Sopenharmony_ci udc->last_vbus = udc->vbus = 0; 29058c2ecf20Sopenharmony_ci vbus_work(udc); 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ci /* enable interrupts */ 29088c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 29098c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_FALLING, INT_SESS_VLD | INT_VBUS_VLD); 29108c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 29118c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_RISING, INT_SESS_VLD | INT_VBUS_VLD); 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci return 0; 29148c2ecf20Sopenharmony_ci} 29158c2ecf20Sopenharmony_ci 29168c2ecf20Sopenharmony_cistatic int lpc32xx_stop(struct usb_gadget *gadget) 29178c2ecf20Sopenharmony_ci{ 29188c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = to_udc(gadget); 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 29218c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 29228c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 29238c2ecf20Sopenharmony_ci ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci if (udc->clocked) { 29268c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 29278c2ecf20Sopenharmony_ci stop_activity(udc); 29288c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_ci /* 29318c2ecf20Sopenharmony_ci * Wait for all the endpoints to disable, 29328c2ecf20Sopenharmony_ci * before disabling clocks. Don't wait if 29338c2ecf20Sopenharmony_ci * endpoints are not enabled. 29348c2ecf20Sopenharmony_ci */ 29358c2ecf20Sopenharmony_ci if (atomic_read(&udc->enabled_ep_cnt)) 29368c2ecf20Sopenharmony_ci wait_event_interruptible(udc->ep_disable_wait_queue, 29378c2ecf20Sopenharmony_ci (atomic_read(&udc->enabled_ep_cnt) == 0)); 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 29408c2ecf20Sopenharmony_ci udc_clk_set(udc, 0); 29418c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 29428c2ecf20Sopenharmony_ci } 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci udc->enabled = 0; 29458c2ecf20Sopenharmony_ci udc->driver = NULL; 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci return 0; 29488c2ecf20Sopenharmony_ci} 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_cistatic void lpc32xx_udc_shutdown(struct platform_device *dev) 29518c2ecf20Sopenharmony_ci{ 29528c2ecf20Sopenharmony_ci /* Force disconnect on reboot */ 29538c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = platform_get_drvdata(dev); 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci pullup(udc, 0); 29568c2ecf20Sopenharmony_ci} 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci/* 29598c2ecf20Sopenharmony_ci * Callbacks to be overridden by options passed via OF (TODO) 29608c2ecf20Sopenharmony_ci */ 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_cistatic void lpc32xx_usbd_conn_chg(int conn) 29638c2ecf20Sopenharmony_ci{ 29648c2ecf20Sopenharmony_ci /* Do nothing, it might be nice to enable an LED 29658c2ecf20Sopenharmony_ci * based on conn state being !0 */ 29668c2ecf20Sopenharmony_ci} 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_cistatic void lpc32xx_usbd_susp_chg(int susp) 29698c2ecf20Sopenharmony_ci{ 29708c2ecf20Sopenharmony_ci /* Device suspend if susp != 0 */ 29718c2ecf20Sopenharmony_ci} 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_cistatic void lpc32xx_rmwkup_chg(int remote_wakup_enable) 29748c2ecf20Sopenharmony_ci{ 29758c2ecf20Sopenharmony_ci /* Enable or disable USB remote wakeup */ 29768c2ecf20Sopenharmony_ci} 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_cistatic struct lpc32xx_usbd_cfg lpc32xx_usbddata = { 29798c2ecf20Sopenharmony_ci .vbus_drv_pol = 0, 29808c2ecf20Sopenharmony_ci .conn_chgb = &lpc32xx_usbd_conn_chg, 29818c2ecf20Sopenharmony_ci .susp_chgb = &lpc32xx_usbd_susp_chg, 29828c2ecf20Sopenharmony_ci .rmwk_chgb = &lpc32xx_rmwkup_chg, 29838c2ecf20Sopenharmony_ci}; 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_cistatic u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_cistatic int lpc32xx_udc_probe(struct platform_device *pdev) 29898c2ecf20Sopenharmony_ci{ 29908c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 29918c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc; 29928c2ecf20Sopenharmony_ci int retval, i; 29938c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 29948c2ecf20Sopenharmony_ci struct device_node *isp1301_node; 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci udc = devm_kmemdup(dev, &controller_template, sizeof(*udc), GFP_KERNEL); 29978c2ecf20Sopenharmony_ci if (!udc) 29988c2ecf20Sopenharmony_ci return -ENOMEM; 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci for (i = 0; i <= 15; i++) 30018c2ecf20Sopenharmony_ci udc->ep[i].udc = udc; 30028c2ecf20Sopenharmony_ci udc->gadget.ep0 = &udc->ep[0].ep; 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_ci /* init software state */ 30058c2ecf20Sopenharmony_ci udc->gadget.dev.parent = dev; 30068c2ecf20Sopenharmony_ci udc->pdev = pdev; 30078c2ecf20Sopenharmony_ci udc->dev = &pdev->dev; 30088c2ecf20Sopenharmony_ci udc->enabled = 0; 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 30118c2ecf20Sopenharmony_ci isp1301_node = of_parse_phandle(pdev->dev.of_node, 30128c2ecf20Sopenharmony_ci "transceiver", 0); 30138c2ecf20Sopenharmony_ci } else { 30148c2ecf20Sopenharmony_ci isp1301_node = NULL; 30158c2ecf20Sopenharmony_ci } 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci udc->isp1301_i2c_client = isp1301_get_client(isp1301_node); 30188c2ecf20Sopenharmony_ci of_node_put(isp1301_node); 30198c2ecf20Sopenharmony_ci if (!udc->isp1301_i2c_client) { 30208c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 30218c2ecf20Sopenharmony_ci } 30228c2ecf20Sopenharmony_ci 30238c2ecf20Sopenharmony_ci dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n", 30248c2ecf20Sopenharmony_ci udc->isp1301_i2c_client->addr); 30258c2ecf20Sopenharmony_ci 30268c2ecf20Sopenharmony_ci pdev->dev.dma_mask = &lpc32xx_usbd_dmamask; 30278c2ecf20Sopenharmony_ci retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 30288c2ecf20Sopenharmony_ci if (retval) 30298c2ecf20Sopenharmony_ci return retval; 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci udc->board = &lpc32xx_usbddata; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ci /* 30348c2ecf20Sopenharmony_ci * Resources are mapped as follows: 30358c2ecf20Sopenharmony_ci * IORESOURCE_MEM, base address and size of USB space 30368c2ecf20Sopenharmony_ci * IORESOURCE_IRQ, USB device low priority interrupt number 30378c2ecf20Sopenharmony_ci * IORESOURCE_IRQ, USB device high priority interrupt number 30388c2ecf20Sopenharmony_ci * IORESOURCE_IRQ, USB device interrupt number 30398c2ecf20Sopenharmony_ci * IORESOURCE_IRQ, USB transceiver interrupt number 30408c2ecf20Sopenharmony_ci */ 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci spin_lock_init(&udc->lock); 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci /* Get IRQs */ 30458c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 30468c2ecf20Sopenharmony_ci udc->udp_irq[i] = platform_get_irq(pdev, i); 30478c2ecf20Sopenharmony_ci if (udc->udp_irq[i] < 0) 30488c2ecf20Sopenharmony_ci return udc->udp_irq[i]; 30498c2ecf20Sopenharmony_ci } 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci udc->udp_baseaddr = devm_platform_ioremap_resource(pdev, 0); 30528c2ecf20Sopenharmony_ci if (IS_ERR(udc->udp_baseaddr)) { 30538c2ecf20Sopenharmony_ci dev_err(udc->dev, "IO map failure\n"); 30548c2ecf20Sopenharmony_ci return PTR_ERR(udc->udp_baseaddr); 30558c2ecf20Sopenharmony_ci } 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci /* Get USB device clock */ 30588c2ecf20Sopenharmony_ci udc->usb_slv_clk = devm_clk_get(&pdev->dev, NULL); 30598c2ecf20Sopenharmony_ci if (IS_ERR(udc->usb_slv_clk)) { 30608c2ecf20Sopenharmony_ci dev_err(udc->dev, "failed to acquire USB device clock\n"); 30618c2ecf20Sopenharmony_ci return PTR_ERR(udc->usb_slv_clk); 30628c2ecf20Sopenharmony_ci } 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci /* Enable USB device clock */ 30658c2ecf20Sopenharmony_ci retval = clk_prepare_enable(udc->usb_slv_clk); 30668c2ecf20Sopenharmony_ci if (retval < 0) { 30678c2ecf20Sopenharmony_ci dev_err(udc->dev, "failed to start USB device clock\n"); 30688c2ecf20Sopenharmony_ci return retval; 30698c2ecf20Sopenharmony_ci } 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci /* Setup deferred workqueue data */ 30728c2ecf20Sopenharmony_ci udc->poweron = udc->pullup = 0; 30738c2ecf20Sopenharmony_ci INIT_WORK(&udc->pullup_job, pullup_work); 30748c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 30758c2ecf20Sopenharmony_ci INIT_WORK(&udc->power_job, power_work); 30768c2ecf20Sopenharmony_ci#endif 30778c2ecf20Sopenharmony_ci 30788c2ecf20Sopenharmony_ci /* All clocks are now on */ 30798c2ecf20Sopenharmony_ci udc->clocked = 1; 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci isp1301_udc_configure(udc); 30828c2ecf20Sopenharmony_ci /* Allocate memory for the UDCA */ 30838c2ecf20Sopenharmony_ci udc->udca_v_base = dma_alloc_coherent(&pdev->dev, UDCA_BUFF_SIZE, 30848c2ecf20Sopenharmony_ci &dma_handle, 30858c2ecf20Sopenharmony_ci (GFP_KERNEL | GFP_DMA)); 30868c2ecf20Sopenharmony_ci if (!udc->udca_v_base) { 30878c2ecf20Sopenharmony_ci dev_err(udc->dev, "error getting UDCA region\n"); 30888c2ecf20Sopenharmony_ci retval = -ENOMEM; 30898c2ecf20Sopenharmony_ci goto i2c_fail; 30908c2ecf20Sopenharmony_ci } 30918c2ecf20Sopenharmony_ci udc->udca_p_base = dma_handle; 30928c2ecf20Sopenharmony_ci dev_dbg(udc->dev, "DMA buffer(0x%x bytes), P:0x%08x, V:0x%p\n", 30938c2ecf20Sopenharmony_ci UDCA_BUFF_SIZE, udc->udca_p_base, udc->udca_v_base); 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci /* Setup the DD DMA memory pool */ 30968c2ecf20Sopenharmony_ci udc->dd_cache = dma_pool_create("udc_dd", udc->dev, 30978c2ecf20Sopenharmony_ci sizeof(struct lpc32xx_usbd_dd_gad), 30988c2ecf20Sopenharmony_ci sizeof(u32), 0); 30998c2ecf20Sopenharmony_ci if (!udc->dd_cache) { 31008c2ecf20Sopenharmony_ci dev_err(udc->dev, "error getting DD DMA region\n"); 31018c2ecf20Sopenharmony_ci retval = -ENOMEM; 31028c2ecf20Sopenharmony_ci goto dma_alloc_fail; 31038c2ecf20Sopenharmony_ci } 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci /* Clear USB peripheral and initialize gadget endpoints */ 31068c2ecf20Sopenharmony_ci udc_disable(udc); 31078c2ecf20Sopenharmony_ci udc_reinit(udc); 31088c2ecf20Sopenharmony_ci 31098c2ecf20Sopenharmony_ci /* Request IRQs - low and high priority USB device IRQs are routed to 31108c2ecf20Sopenharmony_ci * the same handler, while the DMA interrupt is routed elsewhere */ 31118c2ecf20Sopenharmony_ci retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_LP], 31128c2ecf20Sopenharmony_ci lpc32xx_usb_lp_irq, 0, "udc_lp", udc); 31138c2ecf20Sopenharmony_ci if (retval < 0) { 31148c2ecf20Sopenharmony_ci dev_err(udc->dev, "LP request irq %d failed\n", 31158c2ecf20Sopenharmony_ci udc->udp_irq[IRQ_USB_LP]); 31168c2ecf20Sopenharmony_ci goto irq_req_fail; 31178c2ecf20Sopenharmony_ci } 31188c2ecf20Sopenharmony_ci retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_HP], 31198c2ecf20Sopenharmony_ci lpc32xx_usb_hp_irq, 0, "udc_hp", udc); 31208c2ecf20Sopenharmony_ci if (retval < 0) { 31218c2ecf20Sopenharmony_ci dev_err(udc->dev, "HP request irq %d failed\n", 31228c2ecf20Sopenharmony_ci udc->udp_irq[IRQ_USB_HP]); 31238c2ecf20Sopenharmony_ci goto irq_req_fail; 31248c2ecf20Sopenharmony_ci } 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci retval = devm_request_irq(dev, udc->udp_irq[IRQ_USB_DEVDMA], 31278c2ecf20Sopenharmony_ci lpc32xx_usb_devdma_irq, 0, "udc_dma", udc); 31288c2ecf20Sopenharmony_ci if (retval < 0) { 31298c2ecf20Sopenharmony_ci dev_err(udc->dev, "DEV request irq %d failed\n", 31308c2ecf20Sopenharmony_ci udc->udp_irq[IRQ_USB_DEVDMA]); 31318c2ecf20Sopenharmony_ci goto irq_req_fail; 31328c2ecf20Sopenharmony_ci } 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci /* The transceiver interrupt is used for VBUS detection and will 31358c2ecf20Sopenharmony_ci kick off the VBUS handler function */ 31368c2ecf20Sopenharmony_ci retval = devm_request_threaded_irq(dev, udc->udp_irq[IRQ_USB_ATX], NULL, 31378c2ecf20Sopenharmony_ci lpc32xx_usb_vbus_irq, IRQF_ONESHOT, 31388c2ecf20Sopenharmony_ci "udc_otg", udc); 31398c2ecf20Sopenharmony_ci if (retval < 0) { 31408c2ecf20Sopenharmony_ci dev_err(udc->dev, "VBUS request irq %d failed\n", 31418c2ecf20Sopenharmony_ci udc->udp_irq[IRQ_USB_ATX]); 31428c2ecf20Sopenharmony_ci goto irq_req_fail; 31438c2ecf20Sopenharmony_ci } 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci /* Initialize wait queue */ 31468c2ecf20Sopenharmony_ci init_waitqueue_head(&udc->ep_disable_wait_queue); 31478c2ecf20Sopenharmony_ci atomic_set(&udc->enabled_ep_cnt, 0); 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci retval = usb_add_gadget_udc(dev, &udc->gadget); 31508c2ecf20Sopenharmony_ci if (retval < 0) 31518c2ecf20Sopenharmony_ci goto add_gadget_fail; 31528c2ecf20Sopenharmony_ci 31538c2ecf20Sopenharmony_ci dev_set_drvdata(dev, udc); 31548c2ecf20Sopenharmony_ci device_init_wakeup(dev, 1); 31558c2ecf20Sopenharmony_ci create_debug_file(udc); 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci /* Disable clocks for now */ 31588c2ecf20Sopenharmony_ci udc_clk_set(udc, 0); 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_ci dev_info(udc->dev, "%s version %s\n", driver_name, DRIVER_VERSION); 31618c2ecf20Sopenharmony_ci return 0; 31628c2ecf20Sopenharmony_ci 31638c2ecf20Sopenharmony_ciadd_gadget_fail: 31648c2ecf20Sopenharmony_ciirq_req_fail: 31658c2ecf20Sopenharmony_ci dma_pool_destroy(udc->dd_cache); 31668c2ecf20Sopenharmony_cidma_alloc_fail: 31678c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, 31688c2ecf20Sopenharmony_ci udc->udca_v_base, udc->udca_p_base); 31698c2ecf20Sopenharmony_cii2c_fail: 31708c2ecf20Sopenharmony_ci clk_disable_unprepare(udc->usb_slv_clk); 31718c2ecf20Sopenharmony_ci dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval); 31728c2ecf20Sopenharmony_ci 31738c2ecf20Sopenharmony_ci return retval; 31748c2ecf20Sopenharmony_ci} 31758c2ecf20Sopenharmony_ci 31768c2ecf20Sopenharmony_cistatic int lpc32xx_udc_remove(struct platform_device *pdev) 31778c2ecf20Sopenharmony_ci{ 31788c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = platform_get_drvdata(pdev); 31798c2ecf20Sopenharmony_ci 31808c2ecf20Sopenharmony_ci usb_del_gadget_udc(&udc->gadget); 31818c2ecf20Sopenharmony_ci if (udc->driver) 31828c2ecf20Sopenharmony_ci return -EBUSY; 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci udc_clk_set(udc, 1); 31858c2ecf20Sopenharmony_ci udc_disable(udc); 31868c2ecf20Sopenharmony_ci pullup(udc, 0); 31878c2ecf20Sopenharmony_ci 31888c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 0); 31898c2ecf20Sopenharmony_ci remove_debug_file(udc); 31908c2ecf20Sopenharmony_ci 31918c2ecf20Sopenharmony_ci dma_pool_destroy(udc->dd_cache); 31928c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, 31938c2ecf20Sopenharmony_ci udc->udca_v_base, udc->udca_p_base); 31948c2ecf20Sopenharmony_ci 31958c2ecf20Sopenharmony_ci clk_disable_unprepare(udc->usb_slv_clk); 31968c2ecf20Sopenharmony_ci 31978c2ecf20Sopenharmony_ci return 0; 31988c2ecf20Sopenharmony_ci} 31998c2ecf20Sopenharmony_ci 32008c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 32018c2ecf20Sopenharmony_cistatic int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg) 32028c2ecf20Sopenharmony_ci{ 32038c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = platform_get_drvdata(pdev); 32048c2ecf20Sopenharmony_ci 32058c2ecf20Sopenharmony_ci if (udc->clocked) { 32068c2ecf20Sopenharmony_ci /* Power down ISP */ 32078c2ecf20Sopenharmony_ci udc->poweron = 0; 32088c2ecf20Sopenharmony_ci isp1301_set_powerstate(udc, 0); 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci /* Disable clocking */ 32118c2ecf20Sopenharmony_ci udc_clk_set(udc, 0); 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci /* Keep clock flag on, so we know to re-enable clocks 32148c2ecf20Sopenharmony_ci on resume */ 32158c2ecf20Sopenharmony_ci udc->clocked = 1; 32168c2ecf20Sopenharmony_ci 32178c2ecf20Sopenharmony_ci /* Kill global USB clock */ 32188c2ecf20Sopenharmony_ci clk_disable_unprepare(udc->usb_slv_clk); 32198c2ecf20Sopenharmony_ci } 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci return 0; 32228c2ecf20Sopenharmony_ci} 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_cistatic int lpc32xx_udc_resume(struct platform_device *pdev) 32258c2ecf20Sopenharmony_ci{ 32268c2ecf20Sopenharmony_ci struct lpc32xx_udc *udc = platform_get_drvdata(pdev); 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci if (udc->clocked) { 32298c2ecf20Sopenharmony_ci /* Enable global USB clock */ 32308c2ecf20Sopenharmony_ci clk_prepare_enable(udc->usb_slv_clk); 32318c2ecf20Sopenharmony_ci 32328c2ecf20Sopenharmony_ci /* Enable clocking */ 32338c2ecf20Sopenharmony_ci udc_clk_set(udc, 1); 32348c2ecf20Sopenharmony_ci 32358c2ecf20Sopenharmony_ci /* ISP back to normal power mode */ 32368c2ecf20Sopenharmony_ci udc->poweron = 1; 32378c2ecf20Sopenharmony_ci isp1301_set_powerstate(udc, 1); 32388c2ecf20Sopenharmony_ci } 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci return 0; 32418c2ecf20Sopenharmony_ci} 32428c2ecf20Sopenharmony_ci#else 32438c2ecf20Sopenharmony_ci#define lpc32xx_udc_suspend NULL 32448c2ecf20Sopenharmony_ci#define lpc32xx_udc_resume NULL 32458c2ecf20Sopenharmony_ci#endif 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 32488c2ecf20Sopenharmony_cistatic const struct of_device_id lpc32xx_udc_of_match[] = { 32498c2ecf20Sopenharmony_ci { .compatible = "nxp,lpc3220-udc", }, 32508c2ecf20Sopenharmony_ci { }, 32518c2ecf20Sopenharmony_ci}; 32528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); 32538c2ecf20Sopenharmony_ci#endif 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_cistatic struct platform_driver lpc32xx_udc_driver = { 32568c2ecf20Sopenharmony_ci .remove = lpc32xx_udc_remove, 32578c2ecf20Sopenharmony_ci .shutdown = lpc32xx_udc_shutdown, 32588c2ecf20Sopenharmony_ci .suspend = lpc32xx_udc_suspend, 32598c2ecf20Sopenharmony_ci .resume = lpc32xx_udc_resume, 32608c2ecf20Sopenharmony_ci .driver = { 32618c2ecf20Sopenharmony_ci .name = driver_name, 32628c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(lpc32xx_udc_of_match), 32638c2ecf20Sopenharmony_ci }, 32648c2ecf20Sopenharmony_ci}; 32658c2ecf20Sopenharmony_ci 32668c2ecf20Sopenharmony_cimodule_platform_driver_probe(lpc32xx_udc_driver, lpc32xx_udc_probe); 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LPC32XX udc driver"); 32698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); 32708c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 32718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 32728c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:lpc32xx_udc"); 3273