190fccb52SAndrzej Pietrasiewicz /* 290fccb52SAndrzej Pietrasiewicz * USB Gadget driver for LPC32xx 390fccb52SAndrzej Pietrasiewicz * 490fccb52SAndrzej Pietrasiewicz * Authors: 590fccb52SAndrzej Pietrasiewicz * Kevin Wells <kevin.wells@nxp.com> 690fccb52SAndrzej Pietrasiewicz * Mike James 790fccb52SAndrzej Pietrasiewicz * Roland Stigge <stigge@antcom.de> 890fccb52SAndrzej Pietrasiewicz * 990fccb52SAndrzej Pietrasiewicz * Copyright (C) 2006 Philips Semiconductors 1090fccb52SAndrzej Pietrasiewicz * Copyright (C) 2009 NXP Semiconductors 1190fccb52SAndrzej Pietrasiewicz * Copyright (C) 2012 Roland Stigge 1290fccb52SAndrzej Pietrasiewicz * 1390fccb52SAndrzej Pietrasiewicz * Note: This driver is based on original work done by Mike James for 1490fccb52SAndrzej Pietrasiewicz * the LPC3180. 1590fccb52SAndrzej Pietrasiewicz * 1690fccb52SAndrzej Pietrasiewicz * This program is free software; you can redistribute it and/or modify 1790fccb52SAndrzej Pietrasiewicz * it under the terms of the GNU General Public License as published by 1890fccb52SAndrzej Pietrasiewicz * the Free Software Foundation; either version 2 of the License, or 1990fccb52SAndrzej Pietrasiewicz * (at your option) any later version. 2090fccb52SAndrzej Pietrasiewicz * 2190fccb52SAndrzej Pietrasiewicz * This program is distributed in the hope that it will be useful, 2290fccb52SAndrzej Pietrasiewicz * but WITHOUT ANY WARRANTY; without even the implied warranty of 2390fccb52SAndrzej Pietrasiewicz * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2490fccb52SAndrzej Pietrasiewicz * GNU General Public License for more details. 2590fccb52SAndrzej Pietrasiewicz * 2690fccb52SAndrzej Pietrasiewicz * You should have received a copy of the GNU General Public License 2790fccb52SAndrzej Pietrasiewicz * along with this program; if not, write to the Free Software 2890fccb52SAndrzej Pietrasiewicz * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2990fccb52SAndrzej Pietrasiewicz */ 3090fccb52SAndrzej Pietrasiewicz 3190fccb52SAndrzej Pietrasiewicz #include <linux/kernel.h> 3290fccb52SAndrzej Pietrasiewicz #include <linux/module.h> 3390fccb52SAndrzej Pietrasiewicz #include <linux/platform_device.h> 3490fccb52SAndrzej Pietrasiewicz #include <linux/delay.h> 3590fccb52SAndrzej Pietrasiewicz #include <linux/ioport.h> 3690fccb52SAndrzej Pietrasiewicz #include <linux/slab.h> 3790fccb52SAndrzej Pietrasiewicz #include <linux/errno.h> 3890fccb52SAndrzej Pietrasiewicz #include <linux/init.h> 3990fccb52SAndrzej Pietrasiewicz #include <linux/list.h> 4090fccb52SAndrzej Pietrasiewicz #include <linux/interrupt.h> 4190fccb52SAndrzej Pietrasiewicz #include <linux/proc_fs.h> 4290fccb52SAndrzej Pietrasiewicz #include <linux/clk.h> 4390fccb52SAndrzej Pietrasiewicz #include <linux/usb/ch9.h> 4490fccb52SAndrzej Pietrasiewicz #include <linux/usb/gadget.h> 4590fccb52SAndrzej Pietrasiewicz #include <linux/i2c.h> 4690fccb52SAndrzej Pietrasiewicz #include <linux/kthread.h> 4790fccb52SAndrzej Pietrasiewicz #include <linux/freezer.h> 4890fccb52SAndrzej Pietrasiewicz #include <linux/dma-mapping.h> 4990fccb52SAndrzej Pietrasiewicz #include <linux/dmapool.h> 5090fccb52SAndrzej Pietrasiewicz #include <linux/workqueue.h> 5190fccb52SAndrzej Pietrasiewicz #include <linux/of.h> 5290fccb52SAndrzej Pietrasiewicz #include <linux/usb/isp1301.h> 5390fccb52SAndrzej Pietrasiewicz 5490fccb52SAndrzej Pietrasiewicz #include <asm/byteorder.h> 5590fccb52SAndrzej Pietrasiewicz #include <mach/hardware.h> 5690fccb52SAndrzej Pietrasiewicz #include <linux/io.h> 5790fccb52SAndrzej Pietrasiewicz #include <asm/irq.h> 5890fccb52SAndrzej Pietrasiewicz 5990fccb52SAndrzej Pietrasiewicz #include <mach/platform.h> 6090fccb52SAndrzej Pietrasiewicz #include <mach/irqs.h> 6190fccb52SAndrzej Pietrasiewicz #include <mach/board.h> 6290fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_USB_GADGET_DEBUG_FILES 6390fccb52SAndrzej Pietrasiewicz #include <linux/debugfs.h> 6490fccb52SAndrzej Pietrasiewicz #include <linux/seq_file.h> 6590fccb52SAndrzej Pietrasiewicz #endif 6690fccb52SAndrzej Pietrasiewicz 6790fccb52SAndrzej Pietrasiewicz /* 6890fccb52SAndrzej Pietrasiewicz * USB device configuration structure 6990fccb52SAndrzej Pietrasiewicz */ 7090fccb52SAndrzej Pietrasiewicz typedef void (*usc_chg_event)(int); 7190fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_cfg { 7290fccb52SAndrzej Pietrasiewicz int vbus_drv_pol; /* 0=active low drive for VBUS via ISP1301 */ 7390fccb52SAndrzej Pietrasiewicz usc_chg_event conn_chgb; /* Connection change event (optional) */ 7490fccb52SAndrzej Pietrasiewicz usc_chg_event susp_chgb; /* Suspend/resume event (optional) */ 7590fccb52SAndrzej Pietrasiewicz usc_chg_event rmwk_chgb; /* Enable/disable remote wakeup */ 7690fccb52SAndrzej Pietrasiewicz }; 7790fccb52SAndrzej Pietrasiewicz 7890fccb52SAndrzej Pietrasiewicz /* 7990fccb52SAndrzej Pietrasiewicz * controller driver data structures 8090fccb52SAndrzej Pietrasiewicz */ 8190fccb52SAndrzej Pietrasiewicz 8290fccb52SAndrzej Pietrasiewicz /* 16 endpoints (not to be confused with 32 hardware endpoints) */ 8390fccb52SAndrzej Pietrasiewicz #define NUM_ENDPOINTS 16 8490fccb52SAndrzej Pietrasiewicz 8590fccb52SAndrzej Pietrasiewicz /* 8690fccb52SAndrzej Pietrasiewicz * IRQ indices make reading the code a little easier 8790fccb52SAndrzej Pietrasiewicz */ 8890fccb52SAndrzej Pietrasiewicz #define IRQ_USB_LP 0 8990fccb52SAndrzej Pietrasiewicz #define IRQ_USB_HP 1 9090fccb52SAndrzej Pietrasiewicz #define IRQ_USB_DEVDMA 2 9190fccb52SAndrzej Pietrasiewicz #define IRQ_USB_ATX 3 9290fccb52SAndrzej Pietrasiewicz 9390fccb52SAndrzej Pietrasiewicz #define EP_OUT 0 /* RX (from host) */ 9490fccb52SAndrzej Pietrasiewicz #define EP_IN 1 /* TX (to host) */ 9590fccb52SAndrzej Pietrasiewicz 9690fccb52SAndrzej Pietrasiewicz /* Returns the interrupt mask for the selected hardware endpoint */ 9790fccb52SAndrzej Pietrasiewicz #define EP_MASK_SEL(ep, dir) (1 << (((ep) * 2) + dir)) 9890fccb52SAndrzej Pietrasiewicz 9990fccb52SAndrzej Pietrasiewicz #define EP_INT_TYPE 0 10090fccb52SAndrzej Pietrasiewicz #define EP_ISO_TYPE 1 10190fccb52SAndrzej Pietrasiewicz #define EP_BLK_TYPE 2 10290fccb52SAndrzej Pietrasiewicz #define EP_CTL_TYPE 3 10390fccb52SAndrzej Pietrasiewicz 10490fccb52SAndrzej Pietrasiewicz /* EP0 states */ 10590fccb52SAndrzej Pietrasiewicz #define WAIT_FOR_SETUP 0 /* Wait for setup packet */ 10690fccb52SAndrzej Pietrasiewicz #define DATA_IN 1 /* Expect dev->host transfer */ 10790fccb52SAndrzej Pietrasiewicz #define DATA_OUT 2 /* Expect host->dev transfer */ 10890fccb52SAndrzej Pietrasiewicz 10990fccb52SAndrzej Pietrasiewicz /* DD (DMA Descriptor) structure, requires word alignment, this is already 11090fccb52SAndrzej Pietrasiewicz * defined in the LPC32XX USB device header file, but this version is slightly 11190fccb52SAndrzej Pietrasiewicz * modified to tag some work data with each DMA descriptor. */ 11290fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_dd_gad { 11390fccb52SAndrzej Pietrasiewicz u32 dd_next_phy; 11490fccb52SAndrzej Pietrasiewicz u32 dd_setup; 11590fccb52SAndrzej Pietrasiewicz u32 dd_buffer_addr; 11690fccb52SAndrzej Pietrasiewicz u32 dd_status; 11790fccb52SAndrzej Pietrasiewicz u32 dd_iso_ps_mem_addr; 11890fccb52SAndrzej Pietrasiewicz u32 this_dma; 11990fccb52SAndrzej Pietrasiewicz u32 iso_status[6]; /* 5 spare */ 12090fccb52SAndrzej Pietrasiewicz u32 dd_next_v; 12190fccb52SAndrzej Pietrasiewicz }; 12290fccb52SAndrzej Pietrasiewicz 12390fccb52SAndrzej Pietrasiewicz /* 12490fccb52SAndrzej Pietrasiewicz * Logical endpoint structure 12590fccb52SAndrzej Pietrasiewicz */ 12690fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep { 12790fccb52SAndrzej Pietrasiewicz struct usb_ep ep; 12890fccb52SAndrzej Pietrasiewicz struct list_head queue; 12990fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc; 13090fccb52SAndrzej Pietrasiewicz 13190fccb52SAndrzej Pietrasiewicz u32 hwep_num_base; /* Physical hardware EP */ 13290fccb52SAndrzej Pietrasiewicz u32 hwep_num; /* Maps to hardware endpoint */ 13390fccb52SAndrzej Pietrasiewicz u32 maxpacket; 13490fccb52SAndrzej Pietrasiewicz u32 lep; 13590fccb52SAndrzej Pietrasiewicz 13690fccb52SAndrzej Pietrasiewicz bool is_in; 13790fccb52SAndrzej Pietrasiewicz bool req_pending; 13890fccb52SAndrzej Pietrasiewicz u32 eptype; 13990fccb52SAndrzej Pietrasiewicz 14090fccb52SAndrzej Pietrasiewicz u32 totalints; 14190fccb52SAndrzej Pietrasiewicz 14290fccb52SAndrzej Pietrasiewicz bool wedge; 14390fccb52SAndrzej Pietrasiewicz }; 14490fccb52SAndrzej Pietrasiewicz 14590fccb52SAndrzej Pietrasiewicz /* 14690fccb52SAndrzej Pietrasiewicz * Common UDC structure 14790fccb52SAndrzej Pietrasiewicz */ 14890fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc { 14990fccb52SAndrzej Pietrasiewicz struct usb_gadget gadget; 15090fccb52SAndrzej Pietrasiewicz struct usb_gadget_driver *driver; 15190fccb52SAndrzej Pietrasiewicz struct platform_device *pdev; 15290fccb52SAndrzej Pietrasiewicz struct device *dev; 15390fccb52SAndrzej Pietrasiewicz struct dentry *pde; 15490fccb52SAndrzej Pietrasiewicz spinlock_t lock; 15590fccb52SAndrzej Pietrasiewicz struct i2c_client *isp1301_i2c_client; 15690fccb52SAndrzej Pietrasiewicz 15790fccb52SAndrzej Pietrasiewicz /* Board and device specific */ 15890fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_cfg *board; 15990fccb52SAndrzej Pietrasiewicz u32 io_p_start; 16090fccb52SAndrzej Pietrasiewicz u32 io_p_size; 16190fccb52SAndrzej Pietrasiewicz void __iomem *udp_baseaddr; 16290fccb52SAndrzej Pietrasiewicz int udp_irq[4]; 16390fccb52SAndrzej Pietrasiewicz struct clk *usb_pll_clk; 16490fccb52SAndrzej Pietrasiewicz struct clk *usb_slv_clk; 16590fccb52SAndrzej Pietrasiewicz struct clk *usb_otg_clk; 16690fccb52SAndrzej Pietrasiewicz 16790fccb52SAndrzej Pietrasiewicz /* DMA support */ 16890fccb52SAndrzej Pietrasiewicz u32 *udca_v_base; 16990fccb52SAndrzej Pietrasiewicz u32 udca_p_base; 17090fccb52SAndrzej Pietrasiewicz struct dma_pool *dd_cache; 17190fccb52SAndrzej Pietrasiewicz 17290fccb52SAndrzej Pietrasiewicz /* Common EP and control data */ 17390fccb52SAndrzej Pietrasiewicz u32 enabled_devints; 17490fccb52SAndrzej Pietrasiewicz u32 enabled_hwepints; 17590fccb52SAndrzej Pietrasiewicz u32 dev_status; 17690fccb52SAndrzej Pietrasiewicz u32 realized_eps; 17790fccb52SAndrzej Pietrasiewicz 17890fccb52SAndrzej Pietrasiewicz /* VBUS detection, pullup, and power flags */ 17990fccb52SAndrzej Pietrasiewicz u8 vbus; 18090fccb52SAndrzej Pietrasiewicz u8 last_vbus; 18190fccb52SAndrzej Pietrasiewicz int pullup; 18290fccb52SAndrzej Pietrasiewicz int poweron; 18390fccb52SAndrzej Pietrasiewicz 18490fccb52SAndrzej Pietrasiewicz /* Work queues related to I2C support */ 18590fccb52SAndrzej Pietrasiewicz struct work_struct pullup_job; 18690fccb52SAndrzej Pietrasiewicz struct work_struct vbus_job; 18790fccb52SAndrzej Pietrasiewicz struct work_struct power_job; 18890fccb52SAndrzej Pietrasiewicz 18990fccb52SAndrzej Pietrasiewicz /* USB device peripheral - various */ 19090fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep ep[NUM_ENDPOINTS]; 19190fccb52SAndrzej Pietrasiewicz bool enabled; 19290fccb52SAndrzej Pietrasiewicz bool clocked; 19390fccb52SAndrzej Pietrasiewicz bool suspended; 19490fccb52SAndrzej Pietrasiewicz int ep0state; 19590fccb52SAndrzej Pietrasiewicz atomic_t enabled_ep_cnt; 19690fccb52SAndrzej Pietrasiewicz wait_queue_head_t ep_disable_wait_queue; 19790fccb52SAndrzej Pietrasiewicz }; 19890fccb52SAndrzej Pietrasiewicz 19990fccb52SAndrzej Pietrasiewicz /* 20090fccb52SAndrzej Pietrasiewicz * Endpoint request 20190fccb52SAndrzej Pietrasiewicz */ 20290fccb52SAndrzej Pietrasiewicz struct lpc32xx_request { 20390fccb52SAndrzej Pietrasiewicz struct usb_request req; 20490fccb52SAndrzej Pietrasiewicz struct list_head queue; 20590fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_dd_gad *dd_desc_ptr; 20690fccb52SAndrzej Pietrasiewicz bool mapped; 20790fccb52SAndrzej Pietrasiewicz bool send_zlp; 20890fccb52SAndrzej Pietrasiewicz }; 20990fccb52SAndrzej Pietrasiewicz 21090fccb52SAndrzej Pietrasiewicz static inline struct lpc32xx_udc *to_udc(struct usb_gadget *g) 21190fccb52SAndrzej Pietrasiewicz { 21290fccb52SAndrzej Pietrasiewicz return container_of(g, struct lpc32xx_udc, gadget); 21390fccb52SAndrzej Pietrasiewicz } 21490fccb52SAndrzej Pietrasiewicz 21590fccb52SAndrzej Pietrasiewicz #define ep_dbg(epp, fmt, arg...) \ 21690fccb52SAndrzej Pietrasiewicz dev_dbg(epp->udc->dev, "%s: " fmt, __func__, ## arg) 21790fccb52SAndrzej Pietrasiewicz #define ep_err(epp, fmt, arg...) \ 21890fccb52SAndrzej Pietrasiewicz dev_err(epp->udc->dev, "%s: " fmt, __func__, ## arg) 21990fccb52SAndrzej Pietrasiewicz #define ep_info(epp, fmt, arg...) \ 22090fccb52SAndrzej Pietrasiewicz dev_info(epp->udc->dev, "%s: " fmt, __func__, ## arg) 22190fccb52SAndrzej Pietrasiewicz #define ep_warn(epp, fmt, arg...) \ 22290fccb52SAndrzej Pietrasiewicz dev_warn(epp->udc->dev, "%s:" fmt, __func__, ## arg) 22390fccb52SAndrzej Pietrasiewicz 22490fccb52SAndrzej Pietrasiewicz #define UDCA_BUFF_SIZE (128) 22590fccb52SAndrzej Pietrasiewicz 22690fccb52SAndrzej Pietrasiewicz /* TODO: When the clock framework is introduced in LPC32xx, IO_ADDRESS will 22790fccb52SAndrzej Pietrasiewicz * be replaced with an inremap()ed pointer 22890fccb52SAndrzej Pietrasiewicz * */ 22990fccb52SAndrzej Pietrasiewicz #define USB_CTRL IO_ADDRESS(LPC32XX_CLK_PM_BASE + 0x64) 23090fccb52SAndrzej Pietrasiewicz 23190fccb52SAndrzej Pietrasiewicz /* USB_CTRL bit defines */ 23290fccb52SAndrzej Pietrasiewicz #define USB_SLAVE_HCLK_EN (1 << 24) 23390fccb52SAndrzej Pietrasiewicz #define USB_HOST_NEED_CLK_EN (1 << 21) 23490fccb52SAndrzej Pietrasiewicz #define USB_DEV_NEED_CLK_EN (1 << 22) 23590fccb52SAndrzej Pietrasiewicz 23690fccb52SAndrzej Pietrasiewicz /********************************************************************** 23790fccb52SAndrzej Pietrasiewicz * USB device controller register offsets 23890fccb52SAndrzej Pietrasiewicz **********************************************************************/ 23990fccb52SAndrzej Pietrasiewicz 24090fccb52SAndrzej Pietrasiewicz #define USBD_DEVINTST(x) ((x) + 0x200) 24190fccb52SAndrzej Pietrasiewicz #define USBD_DEVINTEN(x) ((x) + 0x204) 24290fccb52SAndrzej Pietrasiewicz #define USBD_DEVINTCLR(x) ((x) + 0x208) 24390fccb52SAndrzej Pietrasiewicz #define USBD_DEVINTSET(x) ((x) + 0x20C) 24490fccb52SAndrzej Pietrasiewicz #define USBD_CMDCODE(x) ((x) + 0x210) 24590fccb52SAndrzej Pietrasiewicz #define USBD_CMDDATA(x) ((x) + 0x214) 24690fccb52SAndrzej Pietrasiewicz #define USBD_RXDATA(x) ((x) + 0x218) 24790fccb52SAndrzej Pietrasiewicz #define USBD_TXDATA(x) ((x) + 0x21C) 24890fccb52SAndrzej Pietrasiewicz #define USBD_RXPLEN(x) ((x) + 0x220) 24990fccb52SAndrzej Pietrasiewicz #define USBD_TXPLEN(x) ((x) + 0x224) 25090fccb52SAndrzej Pietrasiewicz #define USBD_CTRL(x) ((x) + 0x228) 25190fccb52SAndrzej Pietrasiewicz #define USBD_DEVINTPRI(x) ((x) + 0x22C) 25290fccb52SAndrzej Pietrasiewicz #define USBD_EPINTST(x) ((x) + 0x230) 25390fccb52SAndrzej Pietrasiewicz #define USBD_EPINTEN(x) ((x) + 0x234) 25490fccb52SAndrzej Pietrasiewicz #define USBD_EPINTCLR(x) ((x) + 0x238) 25590fccb52SAndrzej Pietrasiewicz #define USBD_EPINTSET(x) ((x) + 0x23C) 25690fccb52SAndrzej Pietrasiewicz #define USBD_EPINTPRI(x) ((x) + 0x240) 25790fccb52SAndrzej Pietrasiewicz #define USBD_REEP(x) ((x) + 0x244) 25890fccb52SAndrzej Pietrasiewicz #define USBD_EPIND(x) ((x) + 0x248) 25990fccb52SAndrzej Pietrasiewicz #define USBD_EPMAXPSIZE(x) ((x) + 0x24C) 26090fccb52SAndrzej Pietrasiewicz /* DMA support registers only below */ 26190fccb52SAndrzej Pietrasiewicz /* Set, clear, or get enabled state of the DMA request status. If 26290fccb52SAndrzej Pietrasiewicz * enabled, an IN or OUT token will start a DMA transfer for the EP */ 26390fccb52SAndrzej Pietrasiewicz #define USBD_DMARST(x) ((x) + 0x250) 26490fccb52SAndrzej Pietrasiewicz #define USBD_DMARCLR(x) ((x) + 0x254) 26590fccb52SAndrzej Pietrasiewicz #define USBD_DMARSET(x) ((x) + 0x258) 26690fccb52SAndrzej Pietrasiewicz /* DMA UDCA head pointer */ 26790fccb52SAndrzej Pietrasiewicz #define USBD_UDCAH(x) ((x) + 0x280) 26890fccb52SAndrzej Pietrasiewicz /* EP DMA status, enable, and disable. This is used to specifically 26990fccb52SAndrzej Pietrasiewicz * enabled or disable DMA for a specific EP */ 27090fccb52SAndrzej Pietrasiewicz #define USBD_EPDMAST(x) ((x) + 0x284) 27190fccb52SAndrzej Pietrasiewicz #define USBD_EPDMAEN(x) ((x) + 0x288) 27290fccb52SAndrzej Pietrasiewicz #define USBD_EPDMADIS(x) ((x) + 0x28C) 27390fccb52SAndrzej Pietrasiewicz /* DMA master interrupts enable and pending interrupts */ 27490fccb52SAndrzej Pietrasiewicz #define USBD_DMAINTST(x) ((x) + 0x290) 27590fccb52SAndrzej Pietrasiewicz #define USBD_DMAINTEN(x) ((x) + 0x294) 27690fccb52SAndrzej Pietrasiewicz /* DMA end of transfer interrupt enable, disable, status */ 27790fccb52SAndrzej Pietrasiewicz #define USBD_EOTINTST(x) ((x) + 0x2A0) 27890fccb52SAndrzej Pietrasiewicz #define USBD_EOTINTCLR(x) ((x) + 0x2A4) 27990fccb52SAndrzej Pietrasiewicz #define USBD_EOTINTSET(x) ((x) + 0x2A8) 28090fccb52SAndrzej Pietrasiewicz /* New DD request interrupt enable, disable, status */ 28190fccb52SAndrzej Pietrasiewicz #define USBD_NDDRTINTST(x) ((x) + 0x2AC) 28290fccb52SAndrzej Pietrasiewicz #define USBD_NDDRTINTCLR(x) ((x) + 0x2B0) 28390fccb52SAndrzej Pietrasiewicz #define USBD_NDDRTINTSET(x) ((x) + 0x2B4) 28490fccb52SAndrzej Pietrasiewicz /* DMA error interrupt enable, disable, status */ 28590fccb52SAndrzej Pietrasiewicz #define USBD_SYSERRTINTST(x) ((x) + 0x2B8) 28690fccb52SAndrzej Pietrasiewicz #define USBD_SYSERRTINTCLR(x) ((x) + 0x2BC) 28790fccb52SAndrzej Pietrasiewicz #define USBD_SYSERRTINTSET(x) ((x) + 0x2C0) 28890fccb52SAndrzej Pietrasiewicz 28990fccb52SAndrzej Pietrasiewicz /********************************************************************** 29090fccb52SAndrzej Pietrasiewicz * USBD_DEVINTST/USBD_DEVINTEN/USBD_DEVINTCLR/USBD_DEVINTSET/ 29190fccb52SAndrzej Pietrasiewicz * USBD_DEVINTPRI register definitions 29290fccb52SAndrzej Pietrasiewicz **********************************************************************/ 29390fccb52SAndrzej Pietrasiewicz #define USBD_ERR_INT (1 << 9) 29490fccb52SAndrzej Pietrasiewicz #define USBD_EP_RLZED (1 << 8) 29590fccb52SAndrzej Pietrasiewicz #define USBD_TXENDPKT (1 << 7) 29690fccb52SAndrzej Pietrasiewicz #define USBD_RXENDPKT (1 << 6) 29790fccb52SAndrzej Pietrasiewicz #define USBD_CDFULL (1 << 5) 29890fccb52SAndrzej Pietrasiewicz #define USBD_CCEMPTY (1 << 4) 29990fccb52SAndrzej Pietrasiewicz #define USBD_DEV_STAT (1 << 3) 30090fccb52SAndrzej Pietrasiewicz #define USBD_EP_SLOW (1 << 2) 30190fccb52SAndrzej Pietrasiewicz #define USBD_EP_FAST (1 << 1) 30290fccb52SAndrzej Pietrasiewicz #define USBD_FRAME (1 << 0) 30390fccb52SAndrzej Pietrasiewicz 30490fccb52SAndrzej Pietrasiewicz /********************************************************************** 30590fccb52SAndrzej Pietrasiewicz * USBD_EPINTST/USBD_EPINTEN/USBD_EPINTCLR/USBD_EPINTSET/ 30690fccb52SAndrzej Pietrasiewicz * USBD_EPINTPRI register definitions 30790fccb52SAndrzej Pietrasiewicz **********************************************************************/ 30890fccb52SAndrzej Pietrasiewicz /* End point selection macro (RX) */ 30990fccb52SAndrzej Pietrasiewicz #define USBD_RX_EP_SEL(e) (1 << ((e) << 1)) 31090fccb52SAndrzej Pietrasiewicz 31190fccb52SAndrzej Pietrasiewicz /* End point selection macro (TX) */ 31290fccb52SAndrzej Pietrasiewicz #define USBD_TX_EP_SEL(e) (1 << (((e) << 1) + 1)) 31390fccb52SAndrzej Pietrasiewicz 31490fccb52SAndrzej Pietrasiewicz /********************************************************************** 31590fccb52SAndrzej Pietrasiewicz * USBD_REEP/USBD_DMARST/USBD_DMARCLR/USBD_DMARSET/USBD_EPDMAST/ 31690fccb52SAndrzej Pietrasiewicz * USBD_EPDMAEN/USBD_EPDMADIS/ 31790fccb52SAndrzej Pietrasiewicz * USBD_NDDRTINTST/USBD_NDDRTINTCLR/USBD_NDDRTINTSET/ 31890fccb52SAndrzej Pietrasiewicz * USBD_EOTINTST/USBD_EOTINTCLR/USBD_EOTINTSET/ 31990fccb52SAndrzej Pietrasiewicz * USBD_SYSERRTINTST/USBD_SYSERRTINTCLR/USBD_SYSERRTINTSET 32090fccb52SAndrzej Pietrasiewicz * register definitions 32190fccb52SAndrzej Pietrasiewicz **********************************************************************/ 32290fccb52SAndrzej Pietrasiewicz /* Endpoint selection macro */ 32390fccb52SAndrzej Pietrasiewicz #define USBD_EP_SEL(e) (1 << (e)) 32490fccb52SAndrzej Pietrasiewicz 32590fccb52SAndrzej Pietrasiewicz /********************************************************************** 32690fccb52SAndrzej Pietrasiewicz * SBD_DMAINTST/USBD_DMAINTEN 32790fccb52SAndrzej Pietrasiewicz **********************************************************************/ 32890fccb52SAndrzej Pietrasiewicz #define USBD_SYS_ERR_INT (1 << 2) 32990fccb52SAndrzej Pietrasiewicz #define USBD_NEW_DD_INT (1 << 1) 33090fccb52SAndrzej Pietrasiewicz #define USBD_EOT_INT (1 << 0) 33190fccb52SAndrzej Pietrasiewicz 33290fccb52SAndrzej Pietrasiewicz /********************************************************************** 33390fccb52SAndrzej Pietrasiewicz * USBD_RXPLEN register definitions 33490fccb52SAndrzej Pietrasiewicz **********************************************************************/ 33590fccb52SAndrzej Pietrasiewicz #define USBD_PKT_RDY (1 << 11) 33690fccb52SAndrzej Pietrasiewicz #define USBD_DV (1 << 10) 33790fccb52SAndrzej Pietrasiewicz #define USBD_PK_LEN_MASK 0x3FF 33890fccb52SAndrzej Pietrasiewicz 33990fccb52SAndrzej Pietrasiewicz /********************************************************************** 34090fccb52SAndrzej Pietrasiewicz * USBD_CTRL register definitions 34190fccb52SAndrzej Pietrasiewicz **********************************************************************/ 34290fccb52SAndrzej Pietrasiewicz #define USBD_LOG_ENDPOINT(e) ((e) << 2) 34390fccb52SAndrzej Pietrasiewicz #define USBD_WR_EN (1 << 1) 34490fccb52SAndrzej Pietrasiewicz #define USBD_RD_EN (1 << 0) 34590fccb52SAndrzej Pietrasiewicz 34690fccb52SAndrzej Pietrasiewicz /********************************************************************** 34790fccb52SAndrzej Pietrasiewicz * USBD_CMDCODE register definitions 34890fccb52SAndrzej Pietrasiewicz **********************************************************************/ 34990fccb52SAndrzej Pietrasiewicz #define USBD_CMD_CODE(c) ((c) << 16) 35090fccb52SAndrzej Pietrasiewicz #define USBD_CMD_PHASE(p) ((p) << 8) 35190fccb52SAndrzej Pietrasiewicz 35290fccb52SAndrzej Pietrasiewicz /********************************************************************** 35390fccb52SAndrzej Pietrasiewicz * USBD_DMARST/USBD_DMARCLR/USBD_DMARSET register definitions 35490fccb52SAndrzej Pietrasiewicz **********************************************************************/ 35590fccb52SAndrzej Pietrasiewicz #define USBD_DMAEP(e) (1 << (e)) 35690fccb52SAndrzej Pietrasiewicz 35790fccb52SAndrzej Pietrasiewicz /* DD (DMA Descriptor) structure, requires word alignment */ 35890fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_dd { 35990fccb52SAndrzej Pietrasiewicz u32 *dd_next; 36090fccb52SAndrzej Pietrasiewicz u32 dd_setup; 36190fccb52SAndrzej Pietrasiewicz u32 dd_buffer_addr; 36290fccb52SAndrzej Pietrasiewicz u32 dd_status; 36390fccb52SAndrzej Pietrasiewicz u32 dd_iso_ps_mem_addr; 36490fccb52SAndrzej Pietrasiewicz }; 36590fccb52SAndrzej Pietrasiewicz 36690fccb52SAndrzej Pietrasiewicz /* dd_setup bit defines */ 36790fccb52SAndrzej Pietrasiewicz #define DD_SETUP_ATLE_DMA_MODE 0x01 36890fccb52SAndrzej Pietrasiewicz #define DD_SETUP_NEXT_DD_VALID 0x04 36990fccb52SAndrzej Pietrasiewicz #define DD_SETUP_ISO_EP 0x10 37090fccb52SAndrzej Pietrasiewicz #define DD_SETUP_PACKETLEN(n) (((n) & 0x7FF) << 5) 37190fccb52SAndrzej Pietrasiewicz #define DD_SETUP_DMALENBYTES(n) (((n) & 0xFFFF) << 16) 37290fccb52SAndrzej Pietrasiewicz 37390fccb52SAndrzej Pietrasiewicz /* dd_status bit defines */ 37490fccb52SAndrzej Pietrasiewicz #define DD_STATUS_DD_RETIRED 0x01 37590fccb52SAndrzej Pietrasiewicz #define DD_STATUS_STS_MASK 0x1E 37690fccb52SAndrzej Pietrasiewicz #define DD_STATUS_STS_NS 0x00 /* Not serviced */ 37790fccb52SAndrzej Pietrasiewicz #define DD_STATUS_STS_BS 0x02 /* Being serviced */ 37890fccb52SAndrzej Pietrasiewicz #define DD_STATUS_STS_NC 0x04 /* Normal completion */ 37990fccb52SAndrzej Pietrasiewicz #define DD_STATUS_STS_DUR 0x06 /* Data underrun (short packet) */ 38090fccb52SAndrzej Pietrasiewicz #define DD_STATUS_STS_DOR 0x08 /* Data overrun */ 38190fccb52SAndrzej Pietrasiewicz #define DD_STATUS_STS_SE 0x12 /* System error */ 38290fccb52SAndrzej Pietrasiewicz #define DD_STATUS_PKT_VAL 0x20 /* Packet valid */ 38390fccb52SAndrzej Pietrasiewicz #define DD_STATUS_LSB_EX 0x40 /* LS byte extracted (ATLE) */ 38490fccb52SAndrzej Pietrasiewicz #define DD_STATUS_MSB_EX 0x80 /* MS byte extracted (ATLE) */ 38590fccb52SAndrzej Pietrasiewicz #define DD_STATUS_MLEN(n) (((n) >> 8) & 0x3F) 38690fccb52SAndrzej Pietrasiewicz #define DD_STATUS_CURDMACNT(n) (((n) >> 16) & 0xFFFF) 38790fccb52SAndrzej Pietrasiewicz 38890fccb52SAndrzej Pietrasiewicz /* 38990fccb52SAndrzej Pietrasiewicz * 39090fccb52SAndrzej Pietrasiewicz * Protocol engine bits below 39190fccb52SAndrzej Pietrasiewicz * 39290fccb52SAndrzej Pietrasiewicz */ 39390fccb52SAndrzej Pietrasiewicz /* Device Interrupt Bit Definitions */ 39490fccb52SAndrzej Pietrasiewicz #define FRAME_INT 0x00000001 39590fccb52SAndrzej Pietrasiewicz #define EP_FAST_INT 0x00000002 39690fccb52SAndrzej Pietrasiewicz #define EP_SLOW_INT 0x00000004 39790fccb52SAndrzej Pietrasiewicz #define DEV_STAT_INT 0x00000008 39890fccb52SAndrzej Pietrasiewicz #define CCEMTY_INT 0x00000010 39990fccb52SAndrzej Pietrasiewicz #define CDFULL_INT 0x00000020 40090fccb52SAndrzej Pietrasiewicz #define RxENDPKT_INT 0x00000040 40190fccb52SAndrzej Pietrasiewicz #define TxENDPKT_INT 0x00000080 40290fccb52SAndrzej Pietrasiewicz #define EP_RLZED_INT 0x00000100 40390fccb52SAndrzej Pietrasiewicz #define ERR_INT 0x00000200 40490fccb52SAndrzej Pietrasiewicz 40590fccb52SAndrzej Pietrasiewicz /* Rx & Tx Packet Length Definitions */ 40690fccb52SAndrzej Pietrasiewicz #define PKT_LNGTH_MASK 0x000003FF 40790fccb52SAndrzej Pietrasiewicz #define PKT_DV 0x00000400 40890fccb52SAndrzej Pietrasiewicz #define PKT_RDY 0x00000800 40990fccb52SAndrzej Pietrasiewicz 41090fccb52SAndrzej Pietrasiewicz /* USB Control Definitions */ 41190fccb52SAndrzej Pietrasiewicz #define CTRL_RD_EN 0x00000001 41290fccb52SAndrzej Pietrasiewicz #define CTRL_WR_EN 0x00000002 41390fccb52SAndrzej Pietrasiewicz 41490fccb52SAndrzej Pietrasiewicz /* Command Codes */ 41590fccb52SAndrzej Pietrasiewicz #define CMD_SET_ADDR 0x00D00500 41690fccb52SAndrzej Pietrasiewicz #define CMD_CFG_DEV 0x00D80500 41790fccb52SAndrzej Pietrasiewicz #define CMD_SET_MODE 0x00F30500 41890fccb52SAndrzej Pietrasiewicz #define CMD_RD_FRAME 0x00F50500 41990fccb52SAndrzej Pietrasiewicz #define DAT_RD_FRAME 0x00F50200 42090fccb52SAndrzej Pietrasiewicz #define CMD_RD_TEST 0x00FD0500 42190fccb52SAndrzej Pietrasiewicz #define DAT_RD_TEST 0x00FD0200 42290fccb52SAndrzej Pietrasiewicz #define CMD_SET_DEV_STAT 0x00FE0500 42390fccb52SAndrzej Pietrasiewicz #define CMD_GET_DEV_STAT 0x00FE0500 42490fccb52SAndrzej Pietrasiewicz #define DAT_GET_DEV_STAT 0x00FE0200 42590fccb52SAndrzej Pietrasiewicz #define CMD_GET_ERR_CODE 0x00FF0500 42690fccb52SAndrzej Pietrasiewicz #define DAT_GET_ERR_CODE 0x00FF0200 42790fccb52SAndrzej Pietrasiewicz #define CMD_RD_ERR_STAT 0x00FB0500 42890fccb52SAndrzej Pietrasiewicz #define DAT_RD_ERR_STAT 0x00FB0200 42990fccb52SAndrzej Pietrasiewicz #define DAT_WR_BYTE(x) (0x00000100 | ((x) << 16)) 43090fccb52SAndrzej Pietrasiewicz #define CMD_SEL_EP(x) (0x00000500 | ((x) << 16)) 43190fccb52SAndrzej Pietrasiewicz #define DAT_SEL_EP(x) (0x00000200 | ((x) << 16)) 43290fccb52SAndrzej Pietrasiewicz #define CMD_SEL_EP_CLRI(x) (0x00400500 | ((x) << 16)) 43390fccb52SAndrzej Pietrasiewicz #define DAT_SEL_EP_CLRI(x) (0x00400200 | ((x) << 16)) 43490fccb52SAndrzej Pietrasiewicz #define CMD_SET_EP_STAT(x) (0x00400500 | ((x) << 16)) 43590fccb52SAndrzej Pietrasiewicz #define CMD_CLR_BUF 0x00F20500 43690fccb52SAndrzej Pietrasiewicz #define DAT_CLR_BUF 0x00F20200 43790fccb52SAndrzej Pietrasiewicz #define CMD_VALID_BUF 0x00FA0500 43890fccb52SAndrzej Pietrasiewicz 43990fccb52SAndrzej Pietrasiewicz /* Device Address Register Definitions */ 44090fccb52SAndrzej Pietrasiewicz #define DEV_ADDR_MASK 0x7F 44190fccb52SAndrzej Pietrasiewicz #define DEV_EN 0x80 44290fccb52SAndrzej Pietrasiewicz 44390fccb52SAndrzej Pietrasiewicz /* Device Configure Register Definitions */ 44490fccb52SAndrzej Pietrasiewicz #define CONF_DVICE 0x01 44590fccb52SAndrzej Pietrasiewicz 44690fccb52SAndrzej Pietrasiewicz /* Device Mode Register Definitions */ 44790fccb52SAndrzej Pietrasiewicz #define AP_CLK 0x01 44890fccb52SAndrzej Pietrasiewicz #define INAK_CI 0x02 44990fccb52SAndrzej Pietrasiewicz #define INAK_CO 0x04 45090fccb52SAndrzej Pietrasiewicz #define INAK_II 0x08 45190fccb52SAndrzej Pietrasiewicz #define INAK_IO 0x10 45290fccb52SAndrzej Pietrasiewicz #define INAK_BI 0x20 45390fccb52SAndrzej Pietrasiewicz #define INAK_BO 0x40 45490fccb52SAndrzej Pietrasiewicz 45590fccb52SAndrzej Pietrasiewicz /* Device Status Register Definitions */ 45690fccb52SAndrzej Pietrasiewicz #define DEV_CON 0x01 45790fccb52SAndrzej Pietrasiewicz #define DEV_CON_CH 0x02 45890fccb52SAndrzej Pietrasiewicz #define DEV_SUS 0x04 45990fccb52SAndrzej Pietrasiewicz #define DEV_SUS_CH 0x08 46090fccb52SAndrzej Pietrasiewicz #define DEV_RST 0x10 46190fccb52SAndrzej Pietrasiewicz 46290fccb52SAndrzej Pietrasiewicz /* Error Code Register Definitions */ 46390fccb52SAndrzej Pietrasiewicz #define ERR_EC_MASK 0x0F 46490fccb52SAndrzej Pietrasiewicz #define ERR_EA 0x10 46590fccb52SAndrzej Pietrasiewicz 46690fccb52SAndrzej Pietrasiewicz /* Error Status Register Definitions */ 46790fccb52SAndrzej Pietrasiewicz #define ERR_PID 0x01 46890fccb52SAndrzej Pietrasiewicz #define ERR_UEPKT 0x02 46990fccb52SAndrzej Pietrasiewicz #define ERR_DCRC 0x04 47090fccb52SAndrzej Pietrasiewicz #define ERR_TIMOUT 0x08 47190fccb52SAndrzej Pietrasiewicz #define ERR_EOP 0x10 47290fccb52SAndrzej Pietrasiewicz #define ERR_B_OVRN 0x20 47390fccb52SAndrzej Pietrasiewicz #define ERR_BTSTF 0x40 47490fccb52SAndrzej Pietrasiewicz #define ERR_TGL 0x80 47590fccb52SAndrzej Pietrasiewicz 47690fccb52SAndrzej Pietrasiewicz /* Endpoint Select Register Definitions */ 47790fccb52SAndrzej Pietrasiewicz #define EP_SEL_F 0x01 47890fccb52SAndrzej Pietrasiewicz #define EP_SEL_ST 0x02 47990fccb52SAndrzej Pietrasiewicz #define EP_SEL_STP 0x04 48090fccb52SAndrzej Pietrasiewicz #define EP_SEL_PO 0x08 48190fccb52SAndrzej Pietrasiewicz #define EP_SEL_EPN 0x10 48290fccb52SAndrzej Pietrasiewicz #define EP_SEL_B_1_FULL 0x20 48390fccb52SAndrzej Pietrasiewicz #define EP_SEL_B_2_FULL 0x40 48490fccb52SAndrzej Pietrasiewicz 48590fccb52SAndrzej Pietrasiewicz /* Endpoint Status Register Definitions */ 48690fccb52SAndrzej Pietrasiewicz #define EP_STAT_ST 0x01 48790fccb52SAndrzej Pietrasiewicz #define EP_STAT_DA 0x20 48890fccb52SAndrzej Pietrasiewicz #define EP_STAT_RF_MO 0x40 48990fccb52SAndrzej Pietrasiewicz #define EP_STAT_CND_ST 0x80 49090fccb52SAndrzej Pietrasiewicz 49190fccb52SAndrzej Pietrasiewicz /* Clear Buffer Register Definitions */ 49290fccb52SAndrzej Pietrasiewicz #define CLR_BUF_PO 0x01 49390fccb52SAndrzej Pietrasiewicz 49490fccb52SAndrzej Pietrasiewicz /* DMA Interrupt Bit Definitions */ 49590fccb52SAndrzej Pietrasiewicz #define EOT_INT 0x01 49690fccb52SAndrzej Pietrasiewicz #define NDD_REQ_INT 0x02 49790fccb52SAndrzej Pietrasiewicz #define SYS_ERR_INT 0x04 49890fccb52SAndrzej Pietrasiewicz 49990fccb52SAndrzej Pietrasiewicz #define DRIVER_VERSION "1.03" 50090fccb52SAndrzej Pietrasiewicz static const char driver_name[] = "lpc32xx_udc"; 50190fccb52SAndrzej Pietrasiewicz 50290fccb52SAndrzej Pietrasiewicz /* 50390fccb52SAndrzej Pietrasiewicz * 50490fccb52SAndrzej Pietrasiewicz * proc interface support 50590fccb52SAndrzej Pietrasiewicz * 50690fccb52SAndrzej Pietrasiewicz */ 50790fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_USB_GADGET_DEBUG_FILES 50890fccb52SAndrzej Pietrasiewicz static char *epnames[] = {"INT", "ISO", "BULK", "CTRL"}; 50990fccb52SAndrzej Pietrasiewicz static const char debug_filename[] = "driver/udc"; 51090fccb52SAndrzej Pietrasiewicz 51190fccb52SAndrzej Pietrasiewicz static void proc_ep_show(struct seq_file *s, struct lpc32xx_ep *ep) 51290fccb52SAndrzej Pietrasiewicz { 51390fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 51490fccb52SAndrzej Pietrasiewicz 51590fccb52SAndrzej Pietrasiewicz seq_printf(s, "\n"); 51690fccb52SAndrzej Pietrasiewicz seq_printf(s, "%12s, maxpacket %4d %3s", 51790fccb52SAndrzej Pietrasiewicz ep->ep.name, ep->ep.maxpacket, 51890fccb52SAndrzej Pietrasiewicz ep->is_in ? "in" : "out"); 51990fccb52SAndrzej Pietrasiewicz seq_printf(s, " type %4s", epnames[ep->eptype]); 52090fccb52SAndrzej Pietrasiewicz seq_printf(s, " ints: %12d", ep->totalints); 52190fccb52SAndrzej Pietrasiewicz 52290fccb52SAndrzej Pietrasiewicz if (list_empty(&ep->queue)) 52390fccb52SAndrzej Pietrasiewicz seq_printf(s, "\t(queue empty)\n"); 52490fccb52SAndrzej Pietrasiewicz else { 52590fccb52SAndrzej Pietrasiewicz list_for_each_entry(req, &ep->queue, queue) { 52690fccb52SAndrzej Pietrasiewicz u32 length = req->req.actual; 52790fccb52SAndrzej Pietrasiewicz 52890fccb52SAndrzej Pietrasiewicz seq_printf(s, "\treq %p len %d/%d buf %p\n", 52990fccb52SAndrzej Pietrasiewicz &req->req, length, 53090fccb52SAndrzej Pietrasiewicz req->req.length, req->req.buf); 53190fccb52SAndrzej Pietrasiewicz } 53290fccb52SAndrzej Pietrasiewicz } 53390fccb52SAndrzej Pietrasiewicz } 53490fccb52SAndrzej Pietrasiewicz 53590fccb52SAndrzej Pietrasiewicz static int proc_udc_show(struct seq_file *s, void *unused) 53690fccb52SAndrzej Pietrasiewicz { 53790fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = s->private; 53890fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep; 53990fccb52SAndrzej Pietrasiewicz unsigned long flags; 54090fccb52SAndrzej Pietrasiewicz 54190fccb52SAndrzej Pietrasiewicz seq_printf(s, "%s: version %s\n", driver_name, DRIVER_VERSION); 54290fccb52SAndrzej Pietrasiewicz 54390fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 54490fccb52SAndrzej Pietrasiewicz 54590fccb52SAndrzej Pietrasiewicz seq_printf(s, "vbus %s, pullup %s, %s powered%s, gadget %s\n\n", 54690fccb52SAndrzej Pietrasiewicz udc->vbus ? "present" : "off", 54790fccb52SAndrzej Pietrasiewicz udc->enabled ? (udc->vbus ? "active" : "enabled") : 54890fccb52SAndrzej Pietrasiewicz "disabled", 549d60d9392SPeter Chen udc->gadget.is_selfpowered ? "self" : "VBUS", 55090fccb52SAndrzej Pietrasiewicz udc->suspended ? ", suspended" : "", 55190fccb52SAndrzej Pietrasiewicz udc->driver ? udc->driver->driver.name : "(none)"); 55290fccb52SAndrzej Pietrasiewicz 55390fccb52SAndrzej Pietrasiewicz if (udc->enabled && udc->vbus) { 55490fccb52SAndrzej Pietrasiewicz proc_ep_show(s, &udc->ep[0]); 55590fccb52SAndrzej Pietrasiewicz list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) 55690fccb52SAndrzej Pietrasiewicz proc_ep_show(s, ep); 55790fccb52SAndrzej Pietrasiewicz } 55890fccb52SAndrzej Pietrasiewicz 55990fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 56090fccb52SAndrzej Pietrasiewicz 56190fccb52SAndrzej Pietrasiewicz return 0; 56290fccb52SAndrzej Pietrasiewicz } 56390fccb52SAndrzej Pietrasiewicz 56490fccb52SAndrzej Pietrasiewicz static int proc_udc_open(struct inode *inode, struct file *file) 56590fccb52SAndrzej Pietrasiewicz { 56690fccb52SAndrzej Pietrasiewicz return single_open(file, proc_udc_show, PDE_DATA(inode)); 56790fccb52SAndrzej Pietrasiewicz } 56890fccb52SAndrzej Pietrasiewicz 56990fccb52SAndrzej Pietrasiewicz static const struct file_operations proc_ops = { 57090fccb52SAndrzej Pietrasiewicz .owner = THIS_MODULE, 57190fccb52SAndrzej Pietrasiewicz .open = proc_udc_open, 57290fccb52SAndrzej Pietrasiewicz .read = seq_read, 57390fccb52SAndrzej Pietrasiewicz .llseek = seq_lseek, 57490fccb52SAndrzej Pietrasiewicz .release = single_release, 57590fccb52SAndrzej Pietrasiewicz }; 57690fccb52SAndrzej Pietrasiewicz 57790fccb52SAndrzej Pietrasiewicz static void create_debug_file(struct lpc32xx_udc *udc) 57890fccb52SAndrzej Pietrasiewicz { 57990fccb52SAndrzej Pietrasiewicz udc->pde = debugfs_create_file(debug_filename, 0, NULL, udc, &proc_ops); 58090fccb52SAndrzej Pietrasiewicz } 58190fccb52SAndrzej Pietrasiewicz 58290fccb52SAndrzej Pietrasiewicz static void remove_debug_file(struct lpc32xx_udc *udc) 58390fccb52SAndrzej Pietrasiewicz { 58490fccb52SAndrzej Pietrasiewicz debugfs_remove(udc->pde); 58590fccb52SAndrzej Pietrasiewicz } 58690fccb52SAndrzej Pietrasiewicz 58790fccb52SAndrzej Pietrasiewicz #else 58890fccb52SAndrzej Pietrasiewicz static inline void create_debug_file(struct lpc32xx_udc *udc) {} 58990fccb52SAndrzej Pietrasiewicz static inline void remove_debug_file(struct lpc32xx_udc *udc) {} 59090fccb52SAndrzej Pietrasiewicz #endif 59190fccb52SAndrzej Pietrasiewicz 59290fccb52SAndrzej Pietrasiewicz /* Primary initialization sequence for the ISP1301 transceiver */ 59390fccb52SAndrzej Pietrasiewicz static void isp1301_udc_configure(struct lpc32xx_udc *udc) 59490fccb52SAndrzej Pietrasiewicz { 59590fccb52SAndrzej Pietrasiewicz /* LPC32XX only supports DAT_SE0 USB mode */ 59690fccb52SAndrzej Pietrasiewicz /* This sequence is important */ 59790fccb52SAndrzej Pietrasiewicz 59890fccb52SAndrzej Pietrasiewicz /* Disable transparent UART mode first */ 59990fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 60090fccb52SAndrzej Pietrasiewicz (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), 60190fccb52SAndrzej Pietrasiewicz MC1_UART_EN); 60290fccb52SAndrzej Pietrasiewicz 60390fccb52SAndrzej Pietrasiewicz /* Set full speed and SE0 mode */ 60490fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 60590fccb52SAndrzej Pietrasiewicz (ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); 60690fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 60790fccb52SAndrzej Pietrasiewicz ISP1301_I2C_MODE_CONTROL_1, (MC1_SPEED_REG | MC1_DAT_SE0)); 60890fccb52SAndrzej Pietrasiewicz 60990fccb52SAndrzej Pietrasiewicz /* 61090fccb52SAndrzej Pietrasiewicz * The PSW_OE enable bit state is reversed in the ISP1301 User's Guide 61190fccb52SAndrzej Pietrasiewicz */ 61290fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 61390fccb52SAndrzej Pietrasiewicz (ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); 61490fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 61590fccb52SAndrzej Pietrasiewicz ISP1301_I2C_MODE_CONTROL_2, (MC2_BI_DI | MC2_SPD_SUSP_CTRL)); 61690fccb52SAndrzej Pietrasiewicz 61790fccb52SAndrzej Pietrasiewicz /* Driver VBUS_DRV high or low depending on board setup */ 61890fccb52SAndrzej Pietrasiewicz if (udc->board->vbus_drv_pol != 0) 61990fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 62090fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DRV); 62190fccb52SAndrzej Pietrasiewicz else 62290fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 62390fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, 62490fccb52SAndrzej Pietrasiewicz OTG1_VBUS_DRV); 62590fccb52SAndrzej Pietrasiewicz 62690fccb52SAndrzej Pietrasiewicz /* Bi-directional mode with suspend control 62790fccb52SAndrzej Pietrasiewicz * Enable both pulldowns for now - the pullup will be enable when VBUS 62890fccb52SAndrzej Pietrasiewicz * is detected */ 62990fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 63090fccb52SAndrzej Pietrasiewicz (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), ~0); 63190fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 63290fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1, 63390fccb52SAndrzej Pietrasiewicz (0 | OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN)); 63490fccb52SAndrzej Pietrasiewicz 63590fccb52SAndrzej Pietrasiewicz /* Discharge VBUS (just in case) */ 63690fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 63790fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG); 63890fccb52SAndrzej Pietrasiewicz msleep(1); 63990fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 64090fccb52SAndrzej Pietrasiewicz (ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR), 64190fccb52SAndrzej Pietrasiewicz OTG1_VBUS_DISCHRG); 64290fccb52SAndrzej Pietrasiewicz 64390fccb52SAndrzej Pietrasiewicz /* Clear and enable VBUS high edge interrupt */ 64490fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 64590fccb52SAndrzej Pietrasiewicz ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 64690fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 64790fccb52SAndrzej Pietrasiewicz ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 64890fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 64990fccb52SAndrzej Pietrasiewicz ISP1301_I2C_INTERRUPT_FALLING, INT_VBUS_VLD); 65090fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 65190fccb52SAndrzej Pietrasiewicz ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR, ~0); 65290fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 65390fccb52SAndrzej Pietrasiewicz ISP1301_I2C_INTERRUPT_RISING, INT_VBUS_VLD); 65490fccb52SAndrzej Pietrasiewicz 65590fccb52SAndrzej Pietrasiewicz /* Enable usb_need_clk clock after transceiver is initialized */ 65690fccb52SAndrzej Pietrasiewicz writel((readl(USB_CTRL) | USB_DEV_NEED_CLK_EN), USB_CTRL); 65790fccb52SAndrzej Pietrasiewicz 65890fccb52SAndrzej Pietrasiewicz dev_info(udc->dev, "ISP1301 Vendor ID : 0x%04x\n", 65990fccb52SAndrzej Pietrasiewicz i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x00)); 66090fccb52SAndrzej Pietrasiewicz dev_info(udc->dev, "ISP1301 Product ID : 0x%04x\n", 66190fccb52SAndrzej Pietrasiewicz i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x02)); 66290fccb52SAndrzej Pietrasiewicz dev_info(udc->dev, "ISP1301 Version ID : 0x%04x\n", 66390fccb52SAndrzej Pietrasiewicz i2c_smbus_read_word_data(udc->isp1301_i2c_client, 0x14)); 66490fccb52SAndrzej Pietrasiewicz } 66590fccb52SAndrzej Pietrasiewicz 66690fccb52SAndrzej Pietrasiewicz /* Enables or disables the USB device pullup via the ISP1301 transceiver */ 66790fccb52SAndrzej Pietrasiewicz static void isp1301_pullup_set(struct lpc32xx_udc *udc) 66890fccb52SAndrzej Pietrasiewicz { 66990fccb52SAndrzej Pietrasiewicz if (udc->pullup) 67090fccb52SAndrzej Pietrasiewicz /* Enable pullup for bus signalling */ 67190fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 67290fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1, OTG1_DP_PULLUP); 67390fccb52SAndrzej Pietrasiewicz else 67490fccb52SAndrzej Pietrasiewicz /* Enable pullup for bus signalling */ 67590fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 67690fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, 67790fccb52SAndrzej Pietrasiewicz OTG1_DP_PULLUP); 67890fccb52SAndrzej Pietrasiewicz } 67990fccb52SAndrzej Pietrasiewicz 68090fccb52SAndrzej Pietrasiewicz static void pullup_work(struct work_struct *work) 68190fccb52SAndrzej Pietrasiewicz { 68290fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = 68390fccb52SAndrzej Pietrasiewicz container_of(work, struct lpc32xx_udc, pullup_job); 68490fccb52SAndrzej Pietrasiewicz 68590fccb52SAndrzej Pietrasiewicz isp1301_pullup_set(udc); 68690fccb52SAndrzej Pietrasiewicz } 68790fccb52SAndrzej Pietrasiewicz 68890fccb52SAndrzej Pietrasiewicz static void isp1301_pullup_enable(struct lpc32xx_udc *udc, int en_pullup, 68990fccb52SAndrzej Pietrasiewicz int block) 69090fccb52SAndrzej Pietrasiewicz { 69190fccb52SAndrzej Pietrasiewicz if (en_pullup == udc->pullup) 69290fccb52SAndrzej Pietrasiewicz return; 69390fccb52SAndrzej Pietrasiewicz 69490fccb52SAndrzej Pietrasiewicz udc->pullup = en_pullup; 69590fccb52SAndrzej Pietrasiewicz if (block) 69690fccb52SAndrzej Pietrasiewicz isp1301_pullup_set(udc); 69790fccb52SAndrzej Pietrasiewicz else 69890fccb52SAndrzej Pietrasiewicz /* defer slow i2c pull up setting */ 69990fccb52SAndrzej Pietrasiewicz schedule_work(&udc->pullup_job); 70090fccb52SAndrzej Pietrasiewicz } 70190fccb52SAndrzej Pietrasiewicz 70290fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_PM 70390fccb52SAndrzej Pietrasiewicz /* Powers up or down the ISP1301 transceiver */ 70490fccb52SAndrzej Pietrasiewicz static void isp1301_set_powerstate(struct lpc32xx_udc *udc, int enable) 70590fccb52SAndrzej Pietrasiewicz { 70690fccb52SAndrzej Pietrasiewicz if (enable != 0) 70790fccb52SAndrzej Pietrasiewicz /* Power up ISP1301 - this ISP1301 will automatically wakeup 70890fccb52SAndrzej Pietrasiewicz when VBUS is detected */ 70990fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 71090fccb52SAndrzej Pietrasiewicz ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR, 71190fccb52SAndrzej Pietrasiewicz MC2_GLOBAL_PWR_DN); 71290fccb52SAndrzej Pietrasiewicz else 71390fccb52SAndrzej Pietrasiewicz /* Power down ISP1301 */ 71490fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 71590fccb52SAndrzej Pietrasiewicz ISP1301_I2C_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN); 71690fccb52SAndrzej Pietrasiewicz } 71790fccb52SAndrzej Pietrasiewicz 71890fccb52SAndrzej Pietrasiewicz static void power_work(struct work_struct *work) 71990fccb52SAndrzej Pietrasiewicz { 72090fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = 72190fccb52SAndrzej Pietrasiewicz container_of(work, struct lpc32xx_udc, power_job); 72290fccb52SAndrzej Pietrasiewicz 72390fccb52SAndrzej Pietrasiewicz isp1301_set_powerstate(udc, udc->poweron); 72490fccb52SAndrzej Pietrasiewicz } 72590fccb52SAndrzej Pietrasiewicz #endif 72690fccb52SAndrzej Pietrasiewicz 72790fccb52SAndrzej Pietrasiewicz /* 72890fccb52SAndrzej Pietrasiewicz * 72990fccb52SAndrzej Pietrasiewicz * USB protocol engine command/data read/write helper functions 73090fccb52SAndrzej Pietrasiewicz * 73190fccb52SAndrzej Pietrasiewicz */ 73290fccb52SAndrzej Pietrasiewicz /* Issues a single command to the USB device state machine */ 73390fccb52SAndrzej Pietrasiewicz static void udc_protocol_cmd_w(struct lpc32xx_udc *udc, u32 cmd) 73490fccb52SAndrzej Pietrasiewicz { 73590fccb52SAndrzej Pietrasiewicz u32 pass = 0; 73690fccb52SAndrzej Pietrasiewicz int to; 73790fccb52SAndrzej Pietrasiewicz 73890fccb52SAndrzej Pietrasiewicz /* EP may lock on CLRI if this read isn't done */ 73990fccb52SAndrzej Pietrasiewicz u32 tmp = readl(USBD_DEVINTST(udc->udp_baseaddr)); 74090fccb52SAndrzej Pietrasiewicz (void) tmp; 74190fccb52SAndrzej Pietrasiewicz 74290fccb52SAndrzej Pietrasiewicz while (pass == 0) { 74390fccb52SAndrzej Pietrasiewicz writel(USBD_CCEMPTY, USBD_DEVINTCLR(udc->udp_baseaddr)); 74490fccb52SAndrzej Pietrasiewicz 74590fccb52SAndrzej Pietrasiewicz /* Write command code */ 74690fccb52SAndrzej Pietrasiewicz writel(cmd, USBD_CMDCODE(udc->udp_baseaddr)); 74790fccb52SAndrzej Pietrasiewicz to = 10000; 74890fccb52SAndrzej Pietrasiewicz while (((readl(USBD_DEVINTST(udc->udp_baseaddr)) & 74990fccb52SAndrzej Pietrasiewicz USBD_CCEMPTY) == 0) && (to > 0)) { 75090fccb52SAndrzej Pietrasiewicz to--; 75190fccb52SAndrzej Pietrasiewicz } 75290fccb52SAndrzej Pietrasiewicz 75390fccb52SAndrzej Pietrasiewicz if (to > 0) 75490fccb52SAndrzej Pietrasiewicz pass = 1; 75590fccb52SAndrzej Pietrasiewicz 75690fccb52SAndrzej Pietrasiewicz cpu_relax(); 75790fccb52SAndrzej Pietrasiewicz } 75890fccb52SAndrzej Pietrasiewicz } 75990fccb52SAndrzej Pietrasiewicz 76090fccb52SAndrzej Pietrasiewicz /* Issues 2 commands (or command and data) to the USB device state machine */ 76190fccb52SAndrzej Pietrasiewicz static inline void udc_protocol_cmd_data_w(struct lpc32xx_udc *udc, u32 cmd, 76290fccb52SAndrzej Pietrasiewicz u32 data) 76390fccb52SAndrzej Pietrasiewicz { 76490fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, cmd); 76590fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, data); 76690fccb52SAndrzej Pietrasiewicz } 76790fccb52SAndrzej Pietrasiewicz 76890fccb52SAndrzej Pietrasiewicz /* Issues a single command to the USB device state machine and reads 76990fccb52SAndrzej Pietrasiewicz * response data */ 77090fccb52SAndrzej Pietrasiewicz static u32 udc_protocol_cmd_r(struct lpc32xx_udc *udc, u32 cmd) 77190fccb52SAndrzej Pietrasiewicz { 77290fccb52SAndrzej Pietrasiewicz u32 tmp; 77390fccb52SAndrzej Pietrasiewicz int to = 1000; 77490fccb52SAndrzej Pietrasiewicz 77590fccb52SAndrzej Pietrasiewicz /* Write a command and read data from the protocol engine */ 77690fccb52SAndrzej Pietrasiewicz writel((USBD_CDFULL | USBD_CCEMPTY), 77790fccb52SAndrzej Pietrasiewicz USBD_DEVINTCLR(udc->udp_baseaddr)); 77890fccb52SAndrzej Pietrasiewicz 77990fccb52SAndrzej Pietrasiewicz /* Write command code */ 78090fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, cmd); 78190fccb52SAndrzej Pietrasiewicz 78290fccb52SAndrzej Pietrasiewicz tmp = readl(USBD_DEVINTST(udc->udp_baseaddr)); 78390fccb52SAndrzej Pietrasiewicz while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & USBD_CDFULL)) 78490fccb52SAndrzej Pietrasiewicz && (to > 0)) 78590fccb52SAndrzej Pietrasiewicz to--; 78690fccb52SAndrzej Pietrasiewicz if (!to) 78790fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, 78890fccb52SAndrzej Pietrasiewicz "Protocol engine didn't receive response (CDFULL)\n"); 78990fccb52SAndrzej Pietrasiewicz 79090fccb52SAndrzej Pietrasiewicz return readl(USBD_CMDDATA(udc->udp_baseaddr)); 79190fccb52SAndrzej Pietrasiewicz } 79290fccb52SAndrzej Pietrasiewicz 79390fccb52SAndrzej Pietrasiewicz /* 79490fccb52SAndrzej Pietrasiewicz * 79590fccb52SAndrzej Pietrasiewicz * USB device interrupt mask support functions 79690fccb52SAndrzej Pietrasiewicz * 79790fccb52SAndrzej Pietrasiewicz */ 79890fccb52SAndrzej Pietrasiewicz /* Enable one or more USB device interrupts */ 79990fccb52SAndrzej Pietrasiewicz static inline void uda_enable_devint(struct lpc32xx_udc *udc, u32 devmask) 80090fccb52SAndrzej Pietrasiewicz { 80190fccb52SAndrzej Pietrasiewicz udc->enabled_devints |= devmask; 80290fccb52SAndrzej Pietrasiewicz writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr)); 80390fccb52SAndrzej Pietrasiewicz } 80490fccb52SAndrzej Pietrasiewicz 80590fccb52SAndrzej Pietrasiewicz /* Disable one or more USB device interrupts */ 80690fccb52SAndrzej Pietrasiewicz static inline void uda_disable_devint(struct lpc32xx_udc *udc, u32 mask) 80790fccb52SAndrzej Pietrasiewicz { 80890fccb52SAndrzej Pietrasiewicz udc->enabled_devints &= ~mask; 80990fccb52SAndrzej Pietrasiewicz writel(udc->enabled_devints, USBD_DEVINTEN(udc->udp_baseaddr)); 81090fccb52SAndrzej Pietrasiewicz } 81190fccb52SAndrzej Pietrasiewicz 81290fccb52SAndrzej Pietrasiewicz /* Clear one or more USB device interrupts */ 81390fccb52SAndrzej Pietrasiewicz static inline void uda_clear_devint(struct lpc32xx_udc *udc, u32 mask) 81490fccb52SAndrzej Pietrasiewicz { 81590fccb52SAndrzej Pietrasiewicz writel(mask, USBD_DEVINTCLR(udc->udp_baseaddr)); 81690fccb52SAndrzej Pietrasiewicz } 81790fccb52SAndrzej Pietrasiewicz 81890fccb52SAndrzej Pietrasiewicz /* 81990fccb52SAndrzej Pietrasiewicz * 82090fccb52SAndrzej Pietrasiewicz * Endpoint interrupt disable/enable functions 82190fccb52SAndrzej Pietrasiewicz * 82290fccb52SAndrzej Pietrasiewicz */ 82390fccb52SAndrzej Pietrasiewicz /* Enable one or more USB endpoint interrupts */ 82490fccb52SAndrzej Pietrasiewicz static void uda_enable_hwepint(struct lpc32xx_udc *udc, u32 hwep) 82590fccb52SAndrzej Pietrasiewicz { 82690fccb52SAndrzej Pietrasiewicz udc->enabled_hwepints |= (1 << hwep); 82790fccb52SAndrzej Pietrasiewicz writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr)); 82890fccb52SAndrzej Pietrasiewicz } 82990fccb52SAndrzej Pietrasiewicz 83090fccb52SAndrzej Pietrasiewicz /* Disable one or more USB endpoint interrupts */ 83190fccb52SAndrzej Pietrasiewicz static void uda_disable_hwepint(struct lpc32xx_udc *udc, u32 hwep) 83290fccb52SAndrzej Pietrasiewicz { 83390fccb52SAndrzej Pietrasiewicz udc->enabled_hwepints &= ~(1 << hwep); 83490fccb52SAndrzej Pietrasiewicz writel(udc->enabled_hwepints, USBD_EPINTEN(udc->udp_baseaddr)); 83590fccb52SAndrzej Pietrasiewicz } 83690fccb52SAndrzej Pietrasiewicz 83790fccb52SAndrzej Pietrasiewicz /* Clear one or more USB endpoint interrupts */ 83890fccb52SAndrzej Pietrasiewicz static inline void uda_clear_hwepint(struct lpc32xx_udc *udc, u32 hwep) 83990fccb52SAndrzej Pietrasiewicz { 84090fccb52SAndrzej Pietrasiewicz writel((1 << hwep), USBD_EPINTCLR(udc->udp_baseaddr)); 84190fccb52SAndrzej Pietrasiewicz } 84290fccb52SAndrzej Pietrasiewicz 84390fccb52SAndrzej Pietrasiewicz /* Enable DMA for the HW channel */ 84490fccb52SAndrzej Pietrasiewicz static inline void udc_ep_dma_enable(struct lpc32xx_udc *udc, u32 hwep) 84590fccb52SAndrzej Pietrasiewicz { 84690fccb52SAndrzej Pietrasiewicz writel((1 << hwep), USBD_EPDMAEN(udc->udp_baseaddr)); 84790fccb52SAndrzej Pietrasiewicz } 84890fccb52SAndrzej Pietrasiewicz 84990fccb52SAndrzej Pietrasiewicz /* Disable DMA for the HW channel */ 85090fccb52SAndrzej Pietrasiewicz static inline void udc_ep_dma_disable(struct lpc32xx_udc *udc, u32 hwep) 85190fccb52SAndrzej Pietrasiewicz { 85290fccb52SAndrzej Pietrasiewicz writel((1 << hwep), USBD_EPDMADIS(udc->udp_baseaddr)); 85390fccb52SAndrzej Pietrasiewicz } 85490fccb52SAndrzej Pietrasiewicz 85590fccb52SAndrzej Pietrasiewicz /* 85690fccb52SAndrzej Pietrasiewicz * 85790fccb52SAndrzej Pietrasiewicz * Endpoint realize/unrealize functions 85890fccb52SAndrzej Pietrasiewicz * 85990fccb52SAndrzej Pietrasiewicz */ 86090fccb52SAndrzej Pietrasiewicz /* Before an endpoint can be used, it needs to be realized 86190fccb52SAndrzej Pietrasiewicz * in the USB protocol engine - this realizes the endpoint. 86290fccb52SAndrzej Pietrasiewicz * The interrupt (FIFO or DMA) is not enabled with this function */ 86390fccb52SAndrzej Pietrasiewicz static void udc_realize_hwep(struct lpc32xx_udc *udc, u32 hwep, 86490fccb52SAndrzej Pietrasiewicz u32 maxpacket) 86590fccb52SAndrzej Pietrasiewicz { 86690fccb52SAndrzej Pietrasiewicz int to = 1000; 86790fccb52SAndrzej Pietrasiewicz 86890fccb52SAndrzej Pietrasiewicz writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr)); 86990fccb52SAndrzej Pietrasiewicz writel(hwep, USBD_EPIND(udc->udp_baseaddr)); 87090fccb52SAndrzej Pietrasiewicz udc->realized_eps |= (1 << hwep); 87190fccb52SAndrzej Pietrasiewicz writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr)); 87290fccb52SAndrzej Pietrasiewicz writel(maxpacket, USBD_EPMAXPSIZE(udc->udp_baseaddr)); 87390fccb52SAndrzej Pietrasiewicz 87490fccb52SAndrzej Pietrasiewicz /* Wait until endpoint is realized in hardware */ 87590fccb52SAndrzej Pietrasiewicz while ((!(readl(USBD_DEVINTST(udc->udp_baseaddr)) & 87690fccb52SAndrzej Pietrasiewicz USBD_EP_RLZED)) && (to > 0)) 87790fccb52SAndrzej Pietrasiewicz to--; 87890fccb52SAndrzej Pietrasiewicz if (!to) 87990fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "EP not correctly realized in hardware\n"); 88090fccb52SAndrzej Pietrasiewicz 88190fccb52SAndrzej Pietrasiewicz writel(USBD_EP_RLZED, USBD_DEVINTCLR(udc->udp_baseaddr)); 88290fccb52SAndrzej Pietrasiewicz } 88390fccb52SAndrzej Pietrasiewicz 88490fccb52SAndrzej Pietrasiewicz /* Unrealize an EP */ 88590fccb52SAndrzej Pietrasiewicz static void udc_unrealize_hwep(struct lpc32xx_udc *udc, u32 hwep) 88690fccb52SAndrzej Pietrasiewicz { 88790fccb52SAndrzej Pietrasiewicz udc->realized_eps &= ~(1 << hwep); 88890fccb52SAndrzej Pietrasiewicz writel(udc->realized_eps, USBD_REEP(udc->udp_baseaddr)); 88990fccb52SAndrzej Pietrasiewicz } 89090fccb52SAndrzej Pietrasiewicz 89190fccb52SAndrzej Pietrasiewicz /* 89290fccb52SAndrzej Pietrasiewicz * 89390fccb52SAndrzej Pietrasiewicz * Endpoint support functions 89490fccb52SAndrzej Pietrasiewicz * 89590fccb52SAndrzej Pietrasiewicz */ 89690fccb52SAndrzej Pietrasiewicz /* Select and clear endpoint interrupt */ 89790fccb52SAndrzej Pietrasiewicz static u32 udc_selep_clrint(struct lpc32xx_udc *udc, u32 hwep) 89890fccb52SAndrzej Pietrasiewicz { 89990fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_SEL_EP_CLRI(hwep)); 90090fccb52SAndrzej Pietrasiewicz return udc_protocol_cmd_r(udc, DAT_SEL_EP_CLRI(hwep)); 90190fccb52SAndrzej Pietrasiewicz } 90290fccb52SAndrzej Pietrasiewicz 90390fccb52SAndrzej Pietrasiewicz /* Disables the endpoint in the USB protocol engine */ 90490fccb52SAndrzej Pietrasiewicz static void udc_disable_hwep(struct lpc32xx_udc *udc, u32 hwep) 90590fccb52SAndrzej Pietrasiewicz { 90690fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), 90790fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(EP_STAT_DA)); 90890fccb52SAndrzej Pietrasiewicz } 90990fccb52SAndrzej Pietrasiewicz 91090fccb52SAndrzej Pietrasiewicz /* Stalls the endpoint - endpoint will return STALL */ 91190fccb52SAndrzej Pietrasiewicz static void udc_stall_hwep(struct lpc32xx_udc *udc, u32 hwep) 91290fccb52SAndrzej Pietrasiewicz { 91390fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), 91490fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(EP_STAT_ST)); 91590fccb52SAndrzej Pietrasiewicz } 91690fccb52SAndrzej Pietrasiewicz 91790fccb52SAndrzej Pietrasiewicz /* Clear stall or reset endpoint */ 91890fccb52SAndrzej Pietrasiewicz static void udc_clrstall_hwep(struct lpc32xx_udc *udc, u32 hwep) 91990fccb52SAndrzej Pietrasiewicz { 92090fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(hwep), 92190fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(0)); 92290fccb52SAndrzej Pietrasiewicz } 92390fccb52SAndrzej Pietrasiewicz 92490fccb52SAndrzej Pietrasiewicz /* Select an endpoint for endpoint status, clear, validate */ 92590fccb52SAndrzej Pietrasiewicz static void udc_select_hwep(struct lpc32xx_udc *udc, u32 hwep) 92690fccb52SAndrzej Pietrasiewicz { 92790fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_SEL_EP(hwep)); 92890fccb52SAndrzej Pietrasiewicz } 92990fccb52SAndrzej Pietrasiewicz 93090fccb52SAndrzej Pietrasiewicz /* 93190fccb52SAndrzej Pietrasiewicz * 93290fccb52SAndrzej Pietrasiewicz * Endpoint buffer management functions 93390fccb52SAndrzej Pietrasiewicz * 93490fccb52SAndrzej Pietrasiewicz */ 93590fccb52SAndrzej Pietrasiewicz /* Clear the current endpoint's buffer */ 93690fccb52SAndrzej Pietrasiewicz static void udc_clr_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep) 93790fccb52SAndrzej Pietrasiewicz { 93890fccb52SAndrzej Pietrasiewicz udc_select_hwep(udc, hwep); 93990fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_CLR_BUF); 94090fccb52SAndrzej Pietrasiewicz } 94190fccb52SAndrzej Pietrasiewicz 94290fccb52SAndrzej Pietrasiewicz /* Validate the current endpoint's buffer */ 94390fccb52SAndrzej Pietrasiewicz static void udc_val_buffer_hwep(struct lpc32xx_udc *udc, u32 hwep) 94490fccb52SAndrzej Pietrasiewicz { 94590fccb52SAndrzej Pietrasiewicz udc_select_hwep(udc, hwep); 94690fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_VALID_BUF); 94790fccb52SAndrzej Pietrasiewicz } 94890fccb52SAndrzej Pietrasiewicz 94990fccb52SAndrzej Pietrasiewicz static inline u32 udc_clearep_getsts(struct lpc32xx_udc *udc, u32 hwep) 95090fccb52SAndrzej Pietrasiewicz { 95190fccb52SAndrzej Pietrasiewicz /* Clear EP interrupt */ 95290fccb52SAndrzej Pietrasiewicz uda_clear_hwepint(udc, hwep); 95390fccb52SAndrzej Pietrasiewicz return udc_selep_clrint(udc, hwep); 95490fccb52SAndrzej Pietrasiewicz } 95590fccb52SAndrzej Pietrasiewicz 95690fccb52SAndrzej Pietrasiewicz /* 95790fccb52SAndrzej Pietrasiewicz * 95890fccb52SAndrzej Pietrasiewicz * USB EP DMA support 95990fccb52SAndrzej Pietrasiewicz * 96090fccb52SAndrzej Pietrasiewicz */ 96190fccb52SAndrzej Pietrasiewicz /* Allocate a DMA Descriptor */ 96290fccb52SAndrzej Pietrasiewicz static struct lpc32xx_usbd_dd_gad *udc_dd_alloc(struct lpc32xx_udc *udc) 96390fccb52SAndrzej Pietrasiewicz { 96490fccb52SAndrzej Pietrasiewicz dma_addr_t dma; 96590fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_dd_gad *dd; 96690fccb52SAndrzej Pietrasiewicz 96790fccb52SAndrzej Pietrasiewicz dd = (struct lpc32xx_usbd_dd_gad *) dma_pool_alloc( 96890fccb52SAndrzej Pietrasiewicz udc->dd_cache, (GFP_KERNEL | GFP_DMA), &dma); 96990fccb52SAndrzej Pietrasiewicz if (dd) 97090fccb52SAndrzej Pietrasiewicz dd->this_dma = dma; 97190fccb52SAndrzej Pietrasiewicz 97290fccb52SAndrzej Pietrasiewicz return dd; 97390fccb52SAndrzej Pietrasiewicz } 97490fccb52SAndrzej Pietrasiewicz 97590fccb52SAndrzej Pietrasiewicz /* Free a DMA Descriptor */ 97690fccb52SAndrzej Pietrasiewicz static void udc_dd_free(struct lpc32xx_udc *udc, struct lpc32xx_usbd_dd_gad *dd) 97790fccb52SAndrzej Pietrasiewicz { 97890fccb52SAndrzej Pietrasiewicz dma_pool_free(udc->dd_cache, dd, dd->this_dma); 97990fccb52SAndrzej Pietrasiewicz } 98090fccb52SAndrzej Pietrasiewicz 98190fccb52SAndrzej Pietrasiewicz /* 98290fccb52SAndrzej Pietrasiewicz * 98390fccb52SAndrzej Pietrasiewicz * USB setup and shutdown functions 98490fccb52SAndrzej Pietrasiewicz * 98590fccb52SAndrzej Pietrasiewicz */ 98690fccb52SAndrzej Pietrasiewicz /* Enables or disables most of the USB system clocks when low power mode is 98790fccb52SAndrzej Pietrasiewicz * needed. Clocks are typically started on a connection event, and disabled 98890fccb52SAndrzej Pietrasiewicz * when a cable is disconnected */ 98990fccb52SAndrzej Pietrasiewicz static void udc_clk_set(struct lpc32xx_udc *udc, int enable) 99090fccb52SAndrzej Pietrasiewicz { 99190fccb52SAndrzej Pietrasiewicz if (enable != 0) { 99290fccb52SAndrzej Pietrasiewicz if (udc->clocked) 99390fccb52SAndrzej Pietrasiewicz return; 99490fccb52SAndrzej Pietrasiewicz 99590fccb52SAndrzej Pietrasiewicz udc->clocked = 1; 99690fccb52SAndrzej Pietrasiewicz 99790fccb52SAndrzej Pietrasiewicz /* 48MHz PLL up */ 99890fccb52SAndrzej Pietrasiewicz clk_enable(udc->usb_pll_clk); 99990fccb52SAndrzej Pietrasiewicz 100090fccb52SAndrzej Pietrasiewicz /* Enable the USB device clock */ 100190fccb52SAndrzej Pietrasiewicz writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN, 100290fccb52SAndrzej Pietrasiewicz USB_CTRL); 100390fccb52SAndrzej Pietrasiewicz 100490fccb52SAndrzej Pietrasiewicz clk_enable(udc->usb_otg_clk); 100590fccb52SAndrzej Pietrasiewicz } else { 100690fccb52SAndrzej Pietrasiewicz if (!udc->clocked) 100790fccb52SAndrzej Pietrasiewicz return; 100890fccb52SAndrzej Pietrasiewicz 100990fccb52SAndrzej Pietrasiewicz udc->clocked = 0; 101090fccb52SAndrzej Pietrasiewicz 101190fccb52SAndrzej Pietrasiewicz /* Never disable the USB_HCLK during normal operation */ 101290fccb52SAndrzej Pietrasiewicz 101390fccb52SAndrzej Pietrasiewicz /* 48MHz PLL dpwn */ 101490fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_pll_clk); 101590fccb52SAndrzej Pietrasiewicz 101690fccb52SAndrzej Pietrasiewicz /* Disable the USB device clock */ 101790fccb52SAndrzej Pietrasiewicz writel(readl(USB_CTRL) & ~USB_DEV_NEED_CLK_EN, 101890fccb52SAndrzej Pietrasiewicz USB_CTRL); 101990fccb52SAndrzej Pietrasiewicz 102090fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_otg_clk); 102190fccb52SAndrzej Pietrasiewicz } 102290fccb52SAndrzej Pietrasiewicz } 102390fccb52SAndrzej Pietrasiewicz 102490fccb52SAndrzej Pietrasiewicz /* Set/reset USB device address */ 102590fccb52SAndrzej Pietrasiewicz static void udc_set_address(struct lpc32xx_udc *udc, u32 addr) 102690fccb52SAndrzej Pietrasiewicz { 102790fccb52SAndrzej Pietrasiewicz /* Address will be latched at the end of the status phase, or 102890fccb52SAndrzej Pietrasiewicz latched immediately if function is called twice */ 102990fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_ADDR, 103090fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(DEV_EN | addr)); 103190fccb52SAndrzej Pietrasiewicz } 103290fccb52SAndrzej Pietrasiewicz 103390fccb52SAndrzej Pietrasiewicz /* Setup up a IN request for DMA transfer - this consists of determining the 103490fccb52SAndrzej Pietrasiewicz * list of DMA addresses for the transfer, allocating DMA Descriptors, 103590fccb52SAndrzej Pietrasiewicz * installing the DD into the UDCA, and then enabling the DMA for that EP */ 103690fccb52SAndrzej Pietrasiewicz static int udc_ep_in_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 103790fccb52SAndrzej Pietrasiewicz { 103890fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 103990fccb52SAndrzej Pietrasiewicz u32 hwep = ep->hwep_num; 104090fccb52SAndrzej Pietrasiewicz 104190fccb52SAndrzej Pietrasiewicz ep->req_pending = 1; 104290fccb52SAndrzej Pietrasiewicz 104390fccb52SAndrzej Pietrasiewicz /* There will always be a request waiting here */ 104490fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 104590fccb52SAndrzej Pietrasiewicz 104690fccb52SAndrzej Pietrasiewicz /* Place the DD Descriptor into the UDCA */ 104790fccb52SAndrzej Pietrasiewicz udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma; 104890fccb52SAndrzej Pietrasiewicz 104990fccb52SAndrzej Pietrasiewicz /* Enable DMA and interrupt for the HW EP */ 105090fccb52SAndrzej Pietrasiewicz udc_ep_dma_enable(udc, hwep); 105190fccb52SAndrzej Pietrasiewicz 105290fccb52SAndrzej Pietrasiewicz /* Clear ZLP if last packet is not of MAXP size */ 105390fccb52SAndrzej Pietrasiewicz if (req->req.length % ep->ep.maxpacket) 105490fccb52SAndrzej Pietrasiewicz req->send_zlp = 0; 105590fccb52SAndrzej Pietrasiewicz 105690fccb52SAndrzej Pietrasiewicz return 0; 105790fccb52SAndrzej Pietrasiewicz } 105890fccb52SAndrzej Pietrasiewicz 105990fccb52SAndrzej Pietrasiewicz /* Setup up a OUT request for DMA transfer - this consists of determining the 106090fccb52SAndrzej Pietrasiewicz * list of DMA addresses for the transfer, allocating DMA Descriptors, 106190fccb52SAndrzej Pietrasiewicz * installing the DD into the UDCA, and then enabling the DMA for that EP */ 106290fccb52SAndrzej Pietrasiewicz static int udc_ep_out_req_dma(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 106390fccb52SAndrzej Pietrasiewicz { 106490fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 106590fccb52SAndrzej Pietrasiewicz u32 hwep = ep->hwep_num; 106690fccb52SAndrzej Pietrasiewicz 106790fccb52SAndrzej Pietrasiewicz ep->req_pending = 1; 106890fccb52SAndrzej Pietrasiewicz 106990fccb52SAndrzej Pietrasiewicz /* There will always be a request waiting here */ 107090fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 107190fccb52SAndrzej Pietrasiewicz 107290fccb52SAndrzej Pietrasiewicz /* Place the DD Descriptor into the UDCA */ 107390fccb52SAndrzej Pietrasiewicz udc->udca_v_base[hwep] = req->dd_desc_ptr->this_dma; 107490fccb52SAndrzej Pietrasiewicz 107590fccb52SAndrzej Pietrasiewicz /* Enable DMA and interrupt for the HW EP */ 107690fccb52SAndrzej Pietrasiewicz udc_ep_dma_enable(udc, hwep); 107790fccb52SAndrzej Pietrasiewicz return 0; 107890fccb52SAndrzej Pietrasiewicz } 107990fccb52SAndrzej Pietrasiewicz 108090fccb52SAndrzej Pietrasiewicz static void udc_disable(struct lpc32xx_udc *udc) 108190fccb52SAndrzej Pietrasiewicz { 108290fccb52SAndrzej Pietrasiewicz u32 i; 108390fccb52SAndrzej Pietrasiewicz 108490fccb52SAndrzej Pietrasiewicz /* Disable device */ 108590fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0)); 108690fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(0)); 108790fccb52SAndrzej Pietrasiewicz 108890fccb52SAndrzej Pietrasiewicz /* Disable all device interrupts (including EP0) */ 108990fccb52SAndrzej Pietrasiewicz uda_disable_devint(udc, 0x3FF); 109090fccb52SAndrzej Pietrasiewicz 109190fccb52SAndrzej Pietrasiewicz /* Disable and reset all endpoint interrupts */ 109290fccb52SAndrzej Pietrasiewicz for (i = 0; i < 32; i++) { 109390fccb52SAndrzej Pietrasiewicz uda_disable_hwepint(udc, i); 109490fccb52SAndrzej Pietrasiewicz uda_clear_hwepint(udc, i); 109590fccb52SAndrzej Pietrasiewicz udc_disable_hwep(udc, i); 109690fccb52SAndrzej Pietrasiewicz udc_unrealize_hwep(udc, i); 109790fccb52SAndrzej Pietrasiewicz udc->udca_v_base[i] = 0; 109890fccb52SAndrzej Pietrasiewicz 109990fccb52SAndrzej Pietrasiewicz /* Disable and clear all interrupts and DMA */ 110090fccb52SAndrzej Pietrasiewicz udc_ep_dma_disable(udc, i); 110190fccb52SAndrzej Pietrasiewicz writel((1 << i), USBD_EOTINTCLR(udc->udp_baseaddr)); 110290fccb52SAndrzej Pietrasiewicz writel((1 << i), USBD_NDDRTINTCLR(udc->udp_baseaddr)); 110390fccb52SAndrzej Pietrasiewicz writel((1 << i), USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 110490fccb52SAndrzej Pietrasiewicz writel((1 << i), USBD_DMARCLR(udc->udp_baseaddr)); 110590fccb52SAndrzej Pietrasiewicz } 110690fccb52SAndrzej Pietrasiewicz 110790fccb52SAndrzej Pietrasiewicz /* Disable DMA interrupts */ 110890fccb52SAndrzej Pietrasiewicz writel(0, USBD_DMAINTEN(udc->udp_baseaddr)); 110990fccb52SAndrzej Pietrasiewicz 111090fccb52SAndrzej Pietrasiewicz writel(0, USBD_UDCAH(udc->udp_baseaddr)); 111190fccb52SAndrzej Pietrasiewicz } 111290fccb52SAndrzej Pietrasiewicz 111390fccb52SAndrzej Pietrasiewicz static void udc_enable(struct lpc32xx_udc *udc) 111490fccb52SAndrzej Pietrasiewicz { 111590fccb52SAndrzej Pietrasiewicz u32 i; 111690fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = &udc->ep[0]; 111790fccb52SAndrzej Pietrasiewicz 111890fccb52SAndrzej Pietrasiewicz /* Start with known state */ 111990fccb52SAndrzej Pietrasiewicz udc_disable(udc); 112090fccb52SAndrzej Pietrasiewicz 112190fccb52SAndrzej Pietrasiewicz /* Enable device */ 112290fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_DEV_STAT, DAT_WR_BYTE(DEV_CON)); 112390fccb52SAndrzej Pietrasiewicz 112490fccb52SAndrzej Pietrasiewicz /* EP interrupts on high priority, FRAME interrupt on low priority */ 112590fccb52SAndrzej Pietrasiewicz writel(USBD_EP_FAST, USBD_DEVINTPRI(udc->udp_baseaddr)); 112690fccb52SAndrzej Pietrasiewicz writel(0xFFFF, USBD_EPINTPRI(udc->udp_baseaddr)); 112790fccb52SAndrzej Pietrasiewicz 112890fccb52SAndrzej Pietrasiewicz /* Clear any pending device interrupts */ 112990fccb52SAndrzej Pietrasiewicz writel(0x3FF, USBD_DEVINTCLR(udc->udp_baseaddr)); 113090fccb52SAndrzej Pietrasiewicz 113190fccb52SAndrzej Pietrasiewicz /* Setup UDCA - not yet used (DMA) */ 113290fccb52SAndrzej Pietrasiewicz writel(udc->udca_p_base, USBD_UDCAH(udc->udp_baseaddr)); 113390fccb52SAndrzej Pietrasiewicz 113490fccb52SAndrzej Pietrasiewicz /* Only enable EP0 in and out for now, EP0 only works in FIFO mode */ 113590fccb52SAndrzej Pietrasiewicz for (i = 0; i <= 1; i++) { 113690fccb52SAndrzej Pietrasiewicz udc_realize_hwep(udc, i, ep->ep.maxpacket); 113790fccb52SAndrzej Pietrasiewicz uda_enable_hwepint(udc, i); 113890fccb52SAndrzej Pietrasiewicz udc_select_hwep(udc, i); 113990fccb52SAndrzej Pietrasiewicz udc_clrstall_hwep(udc, i); 114090fccb52SAndrzej Pietrasiewicz udc_clr_buffer_hwep(udc, i); 114190fccb52SAndrzej Pietrasiewicz } 114290fccb52SAndrzej Pietrasiewicz 114390fccb52SAndrzej Pietrasiewicz /* Device interrupt setup */ 114490fccb52SAndrzej Pietrasiewicz uda_clear_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW | 114590fccb52SAndrzej Pietrasiewicz USBD_EP_FAST)); 114690fccb52SAndrzej Pietrasiewicz uda_enable_devint(udc, (USBD_ERR_INT | USBD_DEV_STAT | USBD_EP_SLOW | 114790fccb52SAndrzej Pietrasiewicz USBD_EP_FAST)); 114890fccb52SAndrzej Pietrasiewicz 114990fccb52SAndrzej Pietrasiewicz /* Set device address to 0 - called twice to force a latch in the USB 115090fccb52SAndrzej Pietrasiewicz engine without the need of a setup packet status closure */ 115190fccb52SAndrzej Pietrasiewicz udc_set_address(udc, 0); 115290fccb52SAndrzej Pietrasiewicz udc_set_address(udc, 0); 115390fccb52SAndrzej Pietrasiewicz 115490fccb52SAndrzej Pietrasiewicz /* Enable master DMA interrupts */ 115590fccb52SAndrzej Pietrasiewicz writel((USBD_SYS_ERR_INT | USBD_EOT_INT), 115690fccb52SAndrzej Pietrasiewicz USBD_DMAINTEN(udc->udp_baseaddr)); 115790fccb52SAndrzej Pietrasiewicz 115890fccb52SAndrzej Pietrasiewicz udc->dev_status = 0; 115990fccb52SAndrzej Pietrasiewicz } 116090fccb52SAndrzej Pietrasiewicz 116190fccb52SAndrzej Pietrasiewicz /* 116290fccb52SAndrzej Pietrasiewicz * 116390fccb52SAndrzej Pietrasiewicz * USB device board specific events handled via callbacks 116490fccb52SAndrzej Pietrasiewicz * 116590fccb52SAndrzej Pietrasiewicz */ 116690fccb52SAndrzej Pietrasiewicz /* Connection change event - notify board function of change */ 116790fccb52SAndrzej Pietrasiewicz static void uda_power_event(struct lpc32xx_udc *udc, u32 conn) 116890fccb52SAndrzej Pietrasiewicz { 116990fccb52SAndrzej Pietrasiewicz /* Just notify of a connection change event (optional) */ 117090fccb52SAndrzej Pietrasiewicz if (udc->board->conn_chgb != NULL) 117190fccb52SAndrzej Pietrasiewicz udc->board->conn_chgb(conn); 117290fccb52SAndrzej Pietrasiewicz } 117390fccb52SAndrzej Pietrasiewicz 117490fccb52SAndrzej Pietrasiewicz /* Suspend/resume event - notify board function of change */ 117590fccb52SAndrzej Pietrasiewicz static void uda_resm_susp_event(struct lpc32xx_udc *udc, u32 conn) 117690fccb52SAndrzej Pietrasiewicz { 117790fccb52SAndrzej Pietrasiewicz /* Just notify of a Suspend/resume change event (optional) */ 117890fccb52SAndrzej Pietrasiewicz if (udc->board->susp_chgb != NULL) 117990fccb52SAndrzej Pietrasiewicz udc->board->susp_chgb(conn); 118090fccb52SAndrzej Pietrasiewicz 118190fccb52SAndrzej Pietrasiewicz if (conn) 118290fccb52SAndrzej Pietrasiewicz udc->suspended = 0; 118390fccb52SAndrzej Pietrasiewicz else 118490fccb52SAndrzej Pietrasiewicz udc->suspended = 1; 118590fccb52SAndrzej Pietrasiewicz } 118690fccb52SAndrzej Pietrasiewicz 118790fccb52SAndrzej Pietrasiewicz /* Remote wakeup enable/disable - notify board function of change */ 118890fccb52SAndrzej Pietrasiewicz static void uda_remwkp_cgh(struct lpc32xx_udc *udc) 118990fccb52SAndrzej Pietrasiewicz { 119090fccb52SAndrzej Pietrasiewicz if (udc->board->rmwk_chgb != NULL) 119190fccb52SAndrzej Pietrasiewicz udc->board->rmwk_chgb(udc->dev_status & 119290fccb52SAndrzej Pietrasiewicz (1 << USB_DEVICE_REMOTE_WAKEUP)); 119390fccb52SAndrzej Pietrasiewicz } 119490fccb52SAndrzej Pietrasiewicz 119590fccb52SAndrzej Pietrasiewicz /* Reads data from FIFO, adjusts for alignment and data size */ 119690fccb52SAndrzej Pietrasiewicz static void udc_pop_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) 119790fccb52SAndrzej Pietrasiewicz { 119890fccb52SAndrzej Pietrasiewicz int n, i, bl; 119990fccb52SAndrzej Pietrasiewicz u16 *p16; 120090fccb52SAndrzej Pietrasiewicz u32 *p32, tmp, cbytes; 120190fccb52SAndrzej Pietrasiewicz 120290fccb52SAndrzej Pietrasiewicz /* Use optimal data transfer method based on source address and size */ 120390fccb52SAndrzej Pietrasiewicz switch (((u32) data) & 0x3) { 120490fccb52SAndrzej Pietrasiewicz case 0: /* 32-bit aligned */ 120590fccb52SAndrzej Pietrasiewicz p32 = (u32 *) data; 120690fccb52SAndrzej Pietrasiewicz cbytes = (bytes & ~0x3); 120790fccb52SAndrzej Pietrasiewicz 120890fccb52SAndrzej Pietrasiewicz /* Copy 32-bit aligned data first */ 120990fccb52SAndrzej Pietrasiewicz for (n = 0; n < cbytes; n += 4) 121090fccb52SAndrzej Pietrasiewicz *p32++ = readl(USBD_RXDATA(udc->udp_baseaddr)); 121190fccb52SAndrzej Pietrasiewicz 121290fccb52SAndrzej Pietrasiewicz /* Handle any remaining bytes */ 121390fccb52SAndrzej Pietrasiewicz bl = bytes - cbytes; 121490fccb52SAndrzej Pietrasiewicz if (bl) { 121590fccb52SAndrzej Pietrasiewicz tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 121690fccb52SAndrzej Pietrasiewicz for (n = 0; n < bl; n++) 121790fccb52SAndrzej Pietrasiewicz data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF); 121890fccb52SAndrzej Pietrasiewicz 121990fccb52SAndrzej Pietrasiewicz } 122090fccb52SAndrzej Pietrasiewicz break; 122190fccb52SAndrzej Pietrasiewicz 122290fccb52SAndrzej Pietrasiewicz case 1: /* 8-bit aligned */ 122390fccb52SAndrzej Pietrasiewicz case 3: 122490fccb52SAndrzej Pietrasiewicz /* Each byte has to be handled independently */ 122590fccb52SAndrzej Pietrasiewicz for (n = 0; n < bytes; n += 4) { 122690fccb52SAndrzej Pietrasiewicz tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 122790fccb52SAndrzej Pietrasiewicz 122890fccb52SAndrzej Pietrasiewicz bl = bytes - n; 122990fccb52SAndrzej Pietrasiewicz if (bl > 3) 123090fccb52SAndrzej Pietrasiewicz bl = 3; 123190fccb52SAndrzej Pietrasiewicz 123290fccb52SAndrzej Pietrasiewicz for (i = 0; i < bl; i++) 123390fccb52SAndrzej Pietrasiewicz data[n + i] = (u8) ((tmp >> (n * 8)) & 0xFF); 123490fccb52SAndrzej Pietrasiewicz } 123590fccb52SAndrzej Pietrasiewicz break; 123690fccb52SAndrzej Pietrasiewicz 123790fccb52SAndrzej Pietrasiewicz case 2: /* 16-bit aligned */ 123890fccb52SAndrzej Pietrasiewicz p16 = (u16 *) data; 123990fccb52SAndrzej Pietrasiewicz cbytes = (bytes & ~0x3); 124090fccb52SAndrzej Pietrasiewicz 124190fccb52SAndrzej Pietrasiewicz /* Copy 32-bit sized objects first with 16-bit alignment */ 124290fccb52SAndrzej Pietrasiewicz for (n = 0; n < cbytes; n += 4) { 124390fccb52SAndrzej Pietrasiewicz tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 124490fccb52SAndrzej Pietrasiewicz *p16++ = (u16)(tmp & 0xFFFF); 124590fccb52SAndrzej Pietrasiewicz *p16++ = (u16)((tmp >> 16) & 0xFFFF); 124690fccb52SAndrzej Pietrasiewicz } 124790fccb52SAndrzej Pietrasiewicz 124890fccb52SAndrzej Pietrasiewicz /* Handle any remaining bytes */ 124990fccb52SAndrzej Pietrasiewicz bl = bytes - cbytes; 125090fccb52SAndrzej Pietrasiewicz if (bl) { 125190fccb52SAndrzej Pietrasiewicz tmp = readl(USBD_RXDATA(udc->udp_baseaddr)); 125290fccb52SAndrzej Pietrasiewicz for (n = 0; n < bl; n++) 125390fccb52SAndrzej Pietrasiewicz data[cbytes + n] = ((tmp >> (n * 8)) & 0xFF); 125490fccb52SAndrzej Pietrasiewicz } 125590fccb52SAndrzej Pietrasiewicz break; 125690fccb52SAndrzej Pietrasiewicz } 125790fccb52SAndrzej Pietrasiewicz } 125890fccb52SAndrzej Pietrasiewicz 125990fccb52SAndrzej Pietrasiewicz /* Read data from the FIFO for an endpoint. This function is for endpoints (such 126090fccb52SAndrzej Pietrasiewicz * as EP0) that don't use DMA. This function should only be called if a packet 126190fccb52SAndrzej Pietrasiewicz * is known to be ready to read for the endpoint. Note that the endpoint must 126290fccb52SAndrzej Pietrasiewicz * be selected in the protocol engine prior to this call. */ 126390fccb52SAndrzej Pietrasiewicz static u32 udc_read_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data, 126490fccb52SAndrzej Pietrasiewicz u32 bytes) 126590fccb52SAndrzej Pietrasiewicz { 126690fccb52SAndrzej Pietrasiewicz u32 tmpv; 126790fccb52SAndrzej Pietrasiewicz int to = 1000; 126890fccb52SAndrzej Pietrasiewicz u32 tmp, hwrep = ((hwep & 0x1E) << 1) | CTRL_RD_EN; 126990fccb52SAndrzej Pietrasiewicz 127090fccb52SAndrzej Pietrasiewicz /* Setup read of endpoint */ 127190fccb52SAndrzej Pietrasiewicz writel(hwrep, USBD_CTRL(udc->udp_baseaddr)); 127290fccb52SAndrzej Pietrasiewicz 127390fccb52SAndrzej Pietrasiewicz /* Wait until packet is ready */ 127490fccb52SAndrzej Pietrasiewicz while ((((tmpv = readl(USBD_RXPLEN(udc->udp_baseaddr))) & 127590fccb52SAndrzej Pietrasiewicz PKT_RDY) == 0) && (to > 0)) 127690fccb52SAndrzej Pietrasiewicz to--; 127790fccb52SAndrzej Pietrasiewicz if (!to) 127890fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "No packet ready on FIFO EP read\n"); 127990fccb52SAndrzej Pietrasiewicz 128090fccb52SAndrzej Pietrasiewicz /* Mask out count */ 128190fccb52SAndrzej Pietrasiewicz tmp = tmpv & PKT_LNGTH_MASK; 128290fccb52SAndrzej Pietrasiewicz if (bytes < tmp) 128390fccb52SAndrzej Pietrasiewicz tmp = bytes; 128490fccb52SAndrzej Pietrasiewicz 128590fccb52SAndrzej Pietrasiewicz if ((tmp > 0) && (data != NULL)) 128690fccb52SAndrzej Pietrasiewicz udc_pop_fifo(udc, (u8 *) data, tmp); 128790fccb52SAndrzej Pietrasiewicz 128890fccb52SAndrzej Pietrasiewicz writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr)); 128990fccb52SAndrzej Pietrasiewicz 129090fccb52SAndrzej Pietrasiewicz /* Clear the buffer */ 129190fccb52SAndrzej Pietrasiewicz udc_clr_buffer_hwep(udc, hwep); 129290fccb52SAndrzej Pietrasiewicz 129390fccb52SAndrzej Pietrasiewicz return tmp; 129490fccb52SAndrzej Pietrasiewicz } 129590fccb52SAndrzej Pietrasiewicz 129690fccb52SAndrzej Pietrasiewicz /* Stuffs data into the FIFO, adjusts for alignment and data size */ 129790fccb52SAndrzej Pietrasiewicz static void udc_stuff_fifo(struct lpc32xx_udc *udc, u8 *data, u32 bytes) 129890fccb52SAndrzej Pietrasiewicz { 129990fccb52SAndrzej Pietrasiewicz int n, i, bl; 130090fccb52SAndrzej Pietrasiewicz u16 *p16; 130190fccb52SAndrzej Pietrasiewicz u32 *p32, tmp, cbytes; 130290fccb52SAndrzej Pietrasiewicz 130390fccb52SAndrzej Pietrasiewicz /* Use optimal data transfer method based on source address and size */ 130490fccb52SAndrzej Pietrasiewicz switch (((u32) data) & 0x3) { 130590fccb52SAndrzej Pietrasiewicz case 0: /* 32-bit aligned */ 130690fccb52SAndrzej Pietrasiewicz p32 = (u32 *) data; 130790fccb52SAndrzej Pietrasiewicz cbytes = (bytes & ~0x3); 130890fccb52SAndrzej Pietrasiewicz 130990fccb52SAndrzej Pietrasiewicz /* Copy 32-bit aligned data first */ 131090fccb52SAndrzej Pietrasiewicz for (n = 0; n < cbytes; n += 4) 131190fccb52SAndrzej Pietrasiewicz writel(*p32++, USBD_TXDATA(udc->udp_baseaddr)); 131290fccb52SAndrzej Pietrasiewicz 131390fccb52SAndrzej Pietrasiewicz /* Handle any remaining bytes */ 131490fccb52SAndrzej Pietrasiewicz bl = bytes - cbytes; 131590fccb52SAndrzej Pietrasiewicz if (bl) { 131690fccb52SAndrzej Pietrasiewicz tmp = 0; 131790fccb52SAndrzej Pietrasiewicz for (n = 0; n < bl; n++) 131890fccb52SAndrzej Pietrasiewicz tmp |= data[cbytes + n] << (n * 8); 131990fccb52SAndrzej Pietrasiewicz 132090fccb52SAndrzej Pietrasiewicz writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 132190fccb52SAndrzej Pietrasiewicz } 132290fccb52SAndrzej Pietrasiewicz break; 132390fccb52SAndrzej Pietrasiewicz 132490fccb52SAndrzej Pietrasiewicz case 1: /* 8-bit aligned */ 132590fccb52SAndrzej Pietrasiewicz case 3: 132690fccb52SAndrzej Pietrasiewicz /* Each byte has to be handled independently */ 132790fccb52SAndrzej Pietrasiewicz for (n = 0; n < bytes; n += 4) { 132890fccb52SAndrzej Pietrasiewicz bl = bytes - n; 132990fccb52SAndrzej Pietrasiewicz if (bl > 4) 133090fccb52SAndrzej Pietrasiewicz bl = 4; 133190fccb52SAndrzej Pietrasiewicz 133290fccb52SAndrzej Pietrasiewicz tmp = 0; 133390fccb52SAndrzej Pietrasiewicz for (i = 0; i < bl; i++) 133490fccb52SAndrzej Pietrasiewicz tmp |= data[n + i] << (i * 8); 133590fccb52SAndrzej Pietrasiewicz 133690fccb52SAndrzej Pietrasiewicz writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 133790fccb52SAndrzej Pietrasiewicz } 133890fccb52SAndrzej Pietrasiewicz break; 133990fccb52SAndrzej Pietrasiewicz 134090fccb52SAndrzej Pietrasiewicz case 2: /* 16-bit aligned */ 134190fccb52SAndrzej Pietrasiewicz p16 = (u16 *) data; 134290fccb52SAndrzej Pietrasiewicz cbytes = (bytes & ~0x3); 134390fccb52SAndrzej Pietrasiewicz 134490fccb52SAndrzej Pietrasiewicz /* Copy 32-bit aligned data first */ 134590fccb52SAndrzej Pietrasiewicz for (n = 0; n < cbytes; n += 4) { 134690fccb52SAndrzej Pietrasiewicz tmp = *p16++ & 0xFFFF; 134790fccb52SAndrzej Pietrasiewicz tmp |= (*p16++ & 0xFFFF) << 16; 134890fccb52SAndrzej Pietrasiewicz writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 134990fccb52SAndrzej Pietrasiewicz } 135090fccb52SAndrzej Pietrasiewicz 135190fccb52SAndrzej Pietrasiewicz /* Handle any remaining bytes */ 135290fccb52SAndrzej Pietrasiewicz bl = bytes - cbytes; 135390fccb52SAndrzej Pietrasiewicz if (bl) { 135490fccb52SAndrzej Pietrasiewicz tmp = 0; 135590fccb52SAndrzej Pietrasiewicz for (n = 0; n < bl; n++) 135690fccb52SAndrzej Pietrasiewicz tmp |= data[cbytes + n] << (n * 8); 135790fccb52SAndrzej Pietrasiewicz 135890fccb52SAndrzej Pietrasiewicz writel(tmp, USBD_TXDATA(udc->udp_baseaddr)); 135990fccb52SAndrzej Pietrasiewicz } 136090fccb52SAndrzej Pietrasiewicz break; 136190fccb52SAndrzej Pietrasiewicz } 136290fccb52SAndrzej Pietrasiewicz } 136390fccb52SAndrzej Pietrasiewicz 136490fccb52SAndrzej Pietrasiewicz /* Write data to the FIFO for an endpoint. This function is for endpoints (such 136590fccb52SAndrzej Pietrasiewicz * as EP0) that don't use DMA. Note that the endpoint must be selected in the 136690fccb52SAndrzej Pietrasiewicz * protocol engine prior to this call. */ 136790fccb52SAndrzej Pietrasiewicz static void udc_write_hwep(struct lpc32xx_udc *udc, u32 hwep, u32 *data, 136890fccb52SAndrzej Pietrasiewicz u32 bytes) 136990fccb52SAndrzej Pietrasiewicz { 137090fccb52SAndrzej Pietrasiewicz u32 hwwep = ((hwep & 0x1E) << 1) | CTRL_WR_EN; 137190fccb52SAndrzej Pietrasiewicz 137290fccb52SAndrzej Pietrasiewicz if ((bytes > 0) && (data == NULL)) 137390fccb52SAndrzej Pietrasiewicz return; 137490fccb52SAndrzej Pietrasiewicz 137590fccb52SAndrzej Pietrasiewicz /* Setup write of endpoint */ 137690fccb52SAndrzej Pietrasiewicz writel(hwwep, USBD_CTRL(udc->udp_baseaddr)); 137790fccb52SAndrzej Pietrasiewicz 137890fccb52SAndrzej Pietrasiewicz writel(bytes, USBD_TXPLEN(udc->udp_baseaddr)); 137990fccb52SAndrzej Pietrasiewicz 138090fccb52SAndrzej Pietrasiewicz /* Need at least 1 byte to trigger TX */ 138190fccb52SAndrzej Pietrasiewicz if (bytes == 0) 138290fccb52SAndrzej Pietrasiewicz writel(0, USBD_TXDATA(udc->udp_baseaddr)); 138390fccb52SAndrzej Pietrasiewicz else 138490fccb52SAndrzej Pietrasiewicz udc_stuff_fifo(udc, (u8 *) data, bytes); 138590fccb52SAndrzej Pietrasiewicz 138690fccb52SAndrzej Pietrasiewicz writel(((hwep & 0x1E) << 1), USBD_CTRL(udc->udp_baseaddr)); 138790fccb52SAndrzej Pietrasiewicz 138890fccb52SAndrzej Pietrasiewicz udc_val_buffer_hwep(udc, hwep); 138990fccb52SAndrzej Pietrasiewicz } 139090fccb52SAndrzej Pietrasiewicz 139190fccb52SAndrzej Pietrasiewicz /* USB device reset - resets USB to a default state with just EP0 139290fccb52SAndrzej Pietrasiewicz enabled */ 139390fccb52SAndrzej Pietrasiewicz static void uda_usb_reset(struct lpc32xx_udc *udc) 139490fccb52SAndrzej Pietrasiewicz { 139590fccb52SAndrzej Pietrasiewicz u32 i = 0; 139690fccb52SAndrzej Pietrasiewicz /* Re-init device controller and EP0 */ 139790fccb52SAndrzej Pietrasiewicz udc_enable(udc); 139890fccb52SAndrzej Pietrasiewicz udc->gadget.speed = USB_SPEED_FULL; 139990fccb52SAndrzej Pietrasiewicz 140090fccb52SAndrzej Pietrasiewicz for (i = 1; i < NUM_ENDPOINTS; i++) { 140190fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = &udc->ep[i]; 140290fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 140390fccb52SAndrzej Pietrasiewicz } 140490fccb52SAndrzej Pietrasiewicz } 140590fccb52SAndrzej Pietrasiewicz 140690fccb52SAndrzej Pietrasiewicz /* Send a ZLP on EP0 */ 140790fccb52SAndrzej Pietrasiewicz static void udc_ep0_send_zlp(struct lpc32xx_udc *udc) 140890fccb52SAndrzej Pietrasiewicz { 140990fccb52SAndrzej Pietrasiewicz udc_write_hwep(udc, EP_IN, NULL, 0); 141090fccb52SAndrzej Pietrasiewicz } 141190fccb52SAndrzej Pietrasiewicz 141290fccb52SAndrzej Pietrasiewicz /* Get current frame number */ 141390fccb52SAndrzej Pietrasiewicz static u16 udc_get_current_frame(struct lpc32xx_udc *udc) 141490fccb52SAndrzej Pietrasiewicz { 141590fccb52SAndrzej Pietrasiewicz u16 flo, fhi; 141690fccb52SAndrzej Pietrasiewicz 141790fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_RD_FRAME); 141890fccb52SAndrzej Pietrasiewicz flo = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME); 141990fccb52SAndrzej Pietrasiewicz fhi = (u16) udc_protocol_cmd_r(udc, DAT_RD_FRAME); 142090fccb52SAndrzej Pietrasiewicz 142190fccb52SAndrzej Pietrasiewicz return (fhi << 8) | flo; 142290fccb52SAndrzej Pietrasiewicz } 142390fccb52SAndrzej Pietrasiewicz 142490fccb52SAndrzej Pietrasiewicz /* Set the device as configured - enables all endpoints */ 142590fccb52SAndrzej Pietrasiewicz static inline void udc_set_device_configured(struct lpc32xx_udc *udc) 142690fccb52SAndrzej Pietrasiewicz { 142790fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(CONF_DVICE)); 142890fccb52SAndrzej Pietrasiewicz } 142990fccb52SAndrzej Pietrasiewicz 143090fccb52SAndrzej Pietrasiewicz /* Set the device as unconfigured - disables all endpoints */ 143190fccb52SAndrzej Pietrasiewicz static inline void udc_set_device_unconfigured(struct lpc32xx_udc *udc) 143290fccb52SAndrzej Pietrasiewicz { 143390fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_CFG_DEV, DAT_WR_BYTE(0)); 143490fccb52SAndrzej Pietrasiewicz } 143590fccb52SAndrzej Pietrasiewicz 143690fccb52SAndrzej Pietrasiewicz /* reinit == restore initial software state */ 143790fccb52SAndrzej Pietrasiewicz static void udc_reinit(struct lpc32xx_udc *udc) 143890fccb52SAndrzej Pietrasiewicz { 143990fccb52SAndrzej Pietrasiewicz u32 i; 144090fccb52SAndrzej Pietrasiewicz 144190fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&udc->gadget.ep_list); 144290fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); 144390fccb52SAndrzej Pietrasiewicz 144490fccb52SAndrzej Pietrasiewicz for (i = 0; i < NUM_ENDPOINTS; i++) { 144590fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = &udc->ep[i]; 144690fccb52SAndrzej Pietrasiewicz 144790fccb52SAndrzej Pietrasiewicz if (i != 0) 144890fccb52SAndrzej Pietrasiewicz list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); 144990fccb52SAndrzej Pietrasiewicz usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket); 145090fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&ep->queue); 145190fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 145290fccb52SAndrzej Pietrasiewicz } 145390fccb52SAndrzej Pietrasiewicz 145490fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 145590fccb52SAndrzej Pietrasiewicz } 145690fccb52SAndrzej Pietrasiewicz 145790fccb52SAndrzej Pietrasiewicz /* Must be called with lock */ 145890fccb52SAndrzej Pietrasiewicz static void done(struct lpc32xx_ep *ep, struct lpc32xx_request *req, int status) 145990fccb52SAndrzej Pietrasiewicz { 146090fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = ep->udc; 146190fccb52SAndrzej Pietrasiewicz 146290fccb52SAndrzej Pietrasiewicz list_del_init(&req->queue); 146390fccb52SAndrzej Pietrasiewicz if (req->req.status == -EINPROGRESS) 146490fccb52SAndrzej Pietrasiewicz req->req.status = status; 146590fccb52SAndrzej Pietrasiewicz else 146690fccb52SAndrzej Pietrasiewicz status = req->req.status; 146790fccb52SAndrzej Pietrasiewicz 146890fccb52SAndrzej Pietrasiewicz if (ep->lep) { 146990fccb52SAndrzej Pietrasiewicz usb_gadget_unmap_request(&udc->gadget, &req->req, ep->is_in); 147090fccb52SAndrzej Pietrasiewicz 147190fccb52SAndrzej Pietrasiewicz /* Free DDs */ 147290fccb52SAndrzej Pietrasiewicz udc_dd_free(udc, req->dd_desc_ptr); 147390fccb52SAndrzej Pietrasiewicz } 147490fccb52SAndrzej Pietrasiewicz 147590fccb52SAndrzej Pietrasiewicz if (status && status != -ESHUTDOWN) 147690fccb52SAndrzej Pietrasiewicz ep_dbg(ep, "%s done %p, status %d\n", ep->ep.name, req, status); 147790fccb52SAndrzej Pietrasiewicz 147890fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 147990fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 1480304f7e5eSMichal Sojka usb_gadget_giveback_request(&ep->ep, &req->req); 148190fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 148290fccb52SAndrzej Pietrasiewicz } 148390fccb52SAndrzej Pietrasiewicz 148490fccb52SAndrzej Pietrasiewicz /* Must be called with lock */ 148590fccb52SAndrzej Pietrasiewicz static void nuke(struct lpc32xx_ep *ep, int status) 148690fccb52SAndrzej Pietrasiewicz { 148790fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 148890fccb52SAndrzej Pietrasiewicz 148990fccb52SAndrzej Pietrasiewicz while (!list_empty(&ep->queue)) { 149090fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 149190fccb52SAndrzej Pietrasiewicz done(ep, req, status); 149290fccb52SAndrzej Pietrasiewicz } 149390fccb52SAndrzej Pietrasiewicz 149490fccb52SAndrzej Pietrasiewicz if (status == -ESHUTDOWN) { 149590fccb52SAndrzej Pietrasiewicz uda_disable_hwepint(ep->udc, ep->hwep_num); 149690fccb52SAndrzej Pietrasiewicz udc_disable_hwep(ep->udc, ep->hwep_num); 149790fccb52SAndrzej Pietrasiewicz } 149890fccb52SAndrzej Pietrasiewicz } 149990fccb52SAndrzej Pietrasiewicz 150090fccb52SAndrzej Pietrasiewicz /* IN endpoint 0 transfer */ 150190fccb52SAndrzej Pietrasiewicz static int udc_ep0_in_req(struct lpc32xx_udc *udc) 150290fccb52SAndrzej Pietrasiewicz { 150390fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 150490fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep0 = &udc->ep[0]; 150590fccb52SAndrzej Pietrasiewicz u32 tsend, ts = 0; 150690fccb52SAndrzej Pietrasiewicz 150790fccb52SAndrzej Pietrasiewicz if (list_empty(&ep0->queue)) 150890fccb52SAndrzej Pietrasiewicz /* Nothing to send */ 150990fccb52SAndrzej Pietrasiewicz return 0; 151090fccb52SAndrzej Pietrasiewicz else 151190fccb52SAndrzej Pietrasiewicz req = list_entry(ep0->queue.next, struct lpc32xx_request, 151290fccb52SAndrzej Pietrasiewicz queue); 151390fccb52SAndrzej Pietrasiewicz 151490fccb52SAndrzej Pietrasiewicz tsend = ts = req->req.length - req->req.actual; 151590fccb52SAndrzej Pietrasiewicz if (ts == 0) { 151690fccb52SAndrzej Pietrasiewicz /* Send a ZLP */ 151790fccb52SAndrzej Pietrasiewicz udc_ep0_send_zlp(udc); 151890fccb52SAndrzej Pietrasiewicz done(ep0, req, 0); 151990fccb52SAndrzej Pietrasiewicz return 1; 152090fccb52SAndrzej Pietrasiewicz } else if (ts > ep0->ep.maxpacket) 152190fccb52SAndrzej Pietrasiewicz ts = ep0->ep.maxpacket; /* Just send what we can */ 152290fccb52SAndrzej Pietrasiewicz 152390fccb52SAndrzej Pietrasiewicz /* Write data to the EP0 FIFO and start transfer */ 152490fccb52SAndrzej Pietrasiewicz udc_write_hwep(udc, EP_IN, (req->req.buf + req->req.actual), ts); 152590fccb52SAndrzej Pietrasiewicz 152690fccb52SAndrzej Pietrasiewicz /* Increment data pointer */ 152790fccb52SAndrzej Pietrasiewicz req->req.actual += ts; 152890fccb52SAndrzej Pietrasiewicz 152990fccb52SAndrzej Pietrasiewicz if (tsend >= ep0->ep.maxpacket) 153090fccb52SAndrzej Pietrasiewicz return 0; /* Stay in data transfer state */ 153190fccb52SAndrzej Pietrasiewicz 153290fccb52SAndrzej Pietrasiewicz /* Transfer request is complete */ 153390fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 153490fccb52SAndrzej Pietrasiewicz done(ep0, req, 0); 153590fccb52SAndrzej Pietrasiewicz return 1; 153690fccb52SAndrzej Pietrasiewicz } 153790fccb52SAndrzej Pietrasiewicz 153890fccb52SAndrzej Pietrasiewicz /* OUT endpoint 0 transfer */ 153990fccb52SAndrzej Pietrasiewicz static int udc_ep0_out_req(struct lpc32xx_udc *udc) 154090fccb52SAndrzej Pietrasiewicz { 154190fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 154290fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep0 = &udc->ep[0]; 154390fccb52SAndrzej Pietrasiewicz u32 tr, bufferspace; 154490fccb52SAndrzej Pietrasiewicz 154590fccb52SAndrzej Pietrasiewicz if (list_empty(&ep0->queue)) 154690fccb52SAndrzej Pietrasiewicz return 0; 154790fccb52SAndrzej Pietrasiewicz else 154890fccb52SAndrzej Pietrasiewicz req = list_entry(ep0->queue.next, struct lpc32xx_request, 154990fccb52SAndrzej Pietrasiewicz queue); 155090fccb52SAndrzej Pietrasiewicz 155190fccb52SAndrzej Pietrasiewicz if (req) { 155290fccb52SAndrzej Pietrasiewicz if (req->req.length == 0) { 155390fccb52SAndrzej Pietrasiewicz /* Just dequeue request */ 155490fccb52SAndrzej Pietrasiewicz done(ep0, req, 0); 155590fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 155690fccb52SAndrzej Pietrasiewicz return 1; 155790fccb52SAndrzej Pietrasiewicz } 155890fccb52SAndrzej Pietrasiewicz 155990fccb52SAndrzej Pietrasiewicz /* Get data from FIFO */ 156090fccb52SAndrzej Pietrasiewicz bufferspace = req->req.length - req->req.actual; 156190fccb52SAndrzej Pietrasiewicz if (bufferspace > ep0->ep.maxpacket) 156290fccb52SAndrzej Pietrasiewicz bufferspace = ep0->ep.maxpacket; 156390fccb52SAndrzej Pietrasiewicz 156490fccb52SAndrzej Pietrasiewicz /* Copy data to buffer */ 156590fccb52SAndrzej Pietrasiewicz prefetchw(req->req.buf + req->req.actual); 156690fccb52SAndrzej Pietrasiewicz tr = udc_read_hwep(udc, EP_OUT, req->req.buf + req->req.actual, 156790fccb52SAndrzej Pietrasiewicz bufferspace); 156890fccb52SAndrzej Pietrasiewicz req->req.actual += bufferspace; 156990fccb52SAndrzej Pietrasiewicz 157090fccb52SAndrzej Pietrasiewicz if (tr < ep0->ep.maxpacket) { 157190fccb52SAndrzej Pietrasiewicz /* This is the last packet */ 157290fccb52SAndrzej Pietrasiewicz done(ep0, req, 0); 157390fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 157490fccb52SAndrzej Pietrasiewicz return 1; 157590fccb52SAndrzej Pietrasiewicz } 157690fccb52SAndrzej Pietrasiewicz } 157790fccb52SAndrzej Pietrasiewicz 157890fccb52SAndrzej Pietrasiewicz return 0; 157990fccb52SAndrzej Pietrasiewicz } 158090fccb52SAndrzej Pietrasiewicz 158190fccb52SAndrzej Pietrasiewicz /* Must be called with lock */ 158290fccb52SAndrzej Pietrasiewicz static void stop_activity(struct lpc32xx_udc *udc) 158390fccb52SAndrzej Pietrasiewicz { 158490fccb52SAndrzej Pietrasiewicz struct usb_gadget_driver *driver = udc->driver; 158590fccb52SAndrzej Pietrasiewicz int i; 158690fccb52SAndrzej Pietrasiewicz 158790fccb52SAndrzej Pietrasiewicz if (udc->gadget.speed == USB_SPEED_UNKNOWN) 158890fccb52SAndrzej Pietrasiewicz driver = NULL; 158990fccb52SAndrzej Pietrasiewicz 159090fccb52SAndrzej Pietrasiewicz udc->gadget.speed = USB_SPEED_UNKNOWN; 159190fccb52SAndrzej Pietrasiewicz udc->suspended = 0; 159290fccb52SAndrzej Pietrasiewicz 159390fccb52SAndrzej Pietrasiewicz for (i = 0; i < NUM_ENDPOINTS; i++) { 159490fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = &udc->ep[i]; 159590fccb52SAndrzej Pietrasiewicz nuke(ep, -ESHUTDOWN); 159690fccb52SAndrzej Pietrasiewicz } 159790fccb52SAndrzej Pietrasiewicz if (driver) { 159890fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 159990fccb52SAndrzej Pietrasiewicz driver->disconnect(&udc->gadget); 160090fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 160190fccb52SAndrzej Pietrasiewicz } 160290fccb52SAndrzej Pietrasiewicz 160390fccb52SAndrzej Pietrasiewicz isp1301_pullup_enable(udc, 0, 0); 160490fccb52SAndrzej Pietrasiewicz udc_disable(udc); 160590fccb52SAndrzej Pietrasiewicz udc_reinit(udc); 160690fccb52SAndrzej Pietrasiewicz } 160790fccb52SAndrzej Pietrasiewicz 160890fccb52SAndrzej Pietrasiewicz /* 160990fccb52SAndrzej Pietrasiewicz * Activate or kill host pullup 161090fccb52SAndrzej Pietrasiewicz * Can be called with or without lock 161190fccb52SAndrzej Pietrasiewicz */ 161290fccb52SAndrzej Pietrasiewicz static void pullup(struct lpc32xx_udc *udc, int is_on) 161390fccb52SAndrzej Pietrasiewicz { 161490fccb52SAndrzej Pietrasiewicz if (!udc->clocked) 161590fccb52SAndrzej Pietrasiewicz return; 161690fccb52SAndrzej Pietrasiewicz 161790fccb52SAndrzej Pietrasiewicz if (!udc->enabled || !udc->vbus) 161890fccb52SAndrzej Pietrasiewicz is_on = 0; 161990fccb52SAndrzej Pietrasiewicz 162090fccb52SAndrzej Pietrasiewicz if (is_on != udc->pullup) 162190fccb52SAndrzej Pietrasiewicz isp1301_pullup_enable(udc, is_on, 0); 162290fccb52SAndrzej Pietrasiewicz } 162390fccb52SAndrzej Pietrasiewicz 162490fccb52SAndrzej Pietrasiewicz /* Must be called without lock */ 162590fccb52SAndrzej Pietrasiewicz static int lpc32xx_ep_disable(struct usb_ep *_ep) 162690fccb52SAndrzej Pietrasiewicz { 162790fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 162890fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = ep->udc; 162990fccb52SAndrzej Pietrasiewicz unsigned long flags; 163090fccb52SAndrzej Pietrasiewicz 163190fccb52SAndrzej Pietrasiewicz if ((ep->hwep_num_base == 0) || (ep->hwep_num == 0)) 163290fccb52SAndrzej Pietrasiewicz return -EINVAL; 163390fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 163490fccb52SAndrzej Pietrasiewicz 163590fccb52SAndrzej Pietrasiewicz nuke(ep, -ESHUTDOWN); 163690fccb52SAndrzej Pietrasiewicz 163790fccb52SAndrzej Pietrasiewicz /* Clear all DMA statuses for this EP */ 163890fccb52SAndrzej Pietrasiewicz udc_ep_dma_disable(udc, ep->hwep_num); 163990fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr)); 164090fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr)); 164190fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 164290fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr)); 164390fccb52SAndrzej Pietrasiewicz 164490fccb52SAndrzej Pietrasiewicz /* Remove the DD pointer in the UDCA */ 164590fccb52SAndrzej Pietrasiewicz udc->udca_v_base[ep->hwep_num] = 0; 164690fccb52SAndrzej Pietrasiewicz 164790fccb52SAndrzej Pietrasiewicz /* Disable and reset endpoint and interrupt */ 164890fccb52SAndrzej Pietrasiewicz uda_clear_hwepint(udc, ep->hwep_num); 164990fccb52SAndrzej Pietrasiewicz udc_unrealize_hwep(udc, ep->hwep_num); 165090fccb52SAndrzej Pietrasiewicz 165190fccb52SAndrzej Pietrasiewicz ep->hwep_num = 0; 165290fccb52SAndrzej Pietrasiewicz 165390fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 165490fccb52SAndrzej Pietrasiewicz 165590fccb52SAndrzej Pietrasiewicz atomic_dec(&udc->enabled_ep_cnt); 165690fccb52SAndrzej Pietrasiewicz wake_up(&udc->ep_disable_wait_queue); 165790fccb52SAndrzej Pietrasiewicz 165890fccb52SAndrzej Pietrasiewicz return 0; 165990fccb52SAndrzej Pietrasiewicz } 166090fccb52SAndrzej Pietrasiewicz 166190fccb52SAndrzej Pietrasiewicz /* Must be called without lock */ 166290fccb52SAndrzej Pietrasiewicz static int lpc32xx_ep_enable(struct usb_ep *_ep, 166390fccb52SAndrzej Pietrasiewicz const struct usb_endpoint_descriptor *desc) 166490fccb52SAndrzej Pietrasiewicz { 166590fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 166690fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = ep->udc; 166790fccb52SAndrzej Pietrasiewicz u16 maxpacket; 166890fccb52SAndrzej Pietrasiewicz u32 tmp; 166990fccb52SAndrzej Pietrasiewicz unsigned long flags; 167090fccb52SAndrzej Pietrasiewicz 167190fccb52SAndrzej Pietrasiewicz /* Verify EP data */ 167290fccb52SAndrzej Pietrasiewicz if ((!_ep) || (!ep) || (!desc) || 167390fccb52SAndrzej Pietrasiewicz (desc->bDescriptorType != USB_DT_ENDPOINT)) { 167490fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "bad ep or descriptor\n"); 167590fccb52SAndrzej Pietrasiewicz return -EINVAL; 167690fccb52SAndrzej Pietrasiewicz } 167790fccb52SAndrzej Pietrasiewicz maxpacket = usb_endpoint_maxp(desc); 167890fccb52SAndrzej Pietrasiewicz if ((maxpacket == 0) || (maxpacket > ep->maxpacket)) { 167990fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "bad ep descriptor's packet size\n"); 168090fccb52SAndrzej Pietrasiewicz return -EINVAL; 168190fccb52SAndrzej Pietrasiewicz } 168290fccb52SAndrzej Pietrasiewicz 168390fccb52SAndrzej Pietrasiewicz /* Don't touch EP0 */ 168490fccb52SAndrzej Pietrasiewicz if (ep->hwep_num_base == 0) { 168590fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "Can't re-enable EP0!!!\n"); 168690fccb52SAndrzej Pietrasiewicz return -EINVAL; 168790fccb52SAndrzej Pietrasiewicz } 168890fccb52SAndrzej Pietrasiewicz 168990fccb52SAndrzej Pietrasiewicz /* Is driver ready? */ 169090fccb52SAndrzej Pietrasiewicz if ((!udc->driver) || (udc->gadget.speed == USB_SPEED_UNKNOWN)) { 169190fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "bogus device state\n"); 169290fccb52SAndrzej Pietrasiewicz return -ESHUTDOWN; 169390fccb52SAndrzej Pietrasiewicz } 169490fccb52SAndrzej Pietrasiewicz 169590fccb52SAndrzej Pietrasiewicz tmp = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; 169690fccb52SAndrzej Pietrasiewicz switch (tmp) { 169790fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_CONTROL: 169890fccb52SAndrzej Pietrasiewicz return -EINVAL; 169990fccb52SAndrzej Pietrasiewicz 170090fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_INT: 170190fccb52SAndrzej Pietrasiewicz if (maxpacket > ep->maxpacket) { 170290fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, 170390fccb52SAndrzej Pietrasiewicz "Bad INT endpoint maxpacket %d\n", maxpacket); 170490fccb52SAndrzej Pietrasiewicz return -EINVAL; 170590fccb52SAndrzej Pietrasiewicz } 170690fccb52SAndrzej Pietrasiewicz break; 170790fccb52SAndrzej Pietrasiewicz 170890fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_BULK: 170990fccb52SAndrzej Pietrasiewicz switch (maxpacket) { 171090fccb52SAndrzej Pietrasiewicz case 8: 171190fccb52SAndrzej Pietrasiewicz case 16: 171290fccb52SAndrzej Pietrasiewicz case 32: 171390fccb52SAndrzej Pietrasiewicz case 64: 171490fccb52SAndrzej Pietrasiewicz break; 171590fccb52SAndrzej Pietrasiewicz 171690fccb52SAndrzej Pietrasiewicz default: 171790fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, 171890fccb52SAndrzej Pietrasiewicz "Bad BULK endpoint maxpacket %d\n", maxpacket); 171990fccb52SAndrzej Pietrasiewicz return -EINVAL; 172090fccb52SAndrzej Pietrasiewicz } 172190fccb52SAndrzej Pietrasiewicz break; 172290fccb52SAndrzej Pietrasiewicz 172390fccb52SAndrzej Pietrasiewicz case USB_ENDPOINT_XFER_ISOC: 172490fccb52SAndrzej Pietrasiewicz break; 172590fccb52SAndrzej Pietrasiewicz } 172690fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 172790fccb52SAndrzej Pietrasiewicz 172890fccb52SAndrzej Pietrasiewicz /* Initialize endpoint to match the selected descriptor */ 172990fccb52SAndrzej Pietrasiewicz ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0; 173090fccb52SAndrzej Pietrasiewicz ep->ep.maxpacket = maxpacket; 173190fccb52SAndrzej Pietrasiewicz 173290fccb52SAndrzej Pietrasiewicz /* Map hardware endpoint from base and direction */ 173390fccb52SAndrzej Pietrasiewicz if (ep->is_in) 173490fccb52SAndrzej Pietrasiewicz /* IN endpoints are offset 1 from the OUT endpoint */ 173590fccb52SAndrzej Pietrasiewicz ep->hwep_num = ep->hwep_num_base + EP_IN; 173690fccb52SAndrzej Pietrasiewicz else 173790fccb52SAndrzej Pietrasiewicz ep->hwep_num = ep->hwep_num_base; 173890fccb52SAndrzej Pietrasiewicz 173990fccb52SAndrzej Pietrasiewicz ep_dbg(ep, "EP enabled: %s, HW:%d, MP:%d IN:%d\n", ep->ep.name, 174090fccb52SAndrzej Pietrasiewicz ep->hwep_num, maxpacket, (ep->is_in == 1)); 174190fccb52SAndrzej Pietrasiewicz 174290fccb52SAndrzej Pietrasiewicz /* Realize the endpoint, interrupt is enabled later when 174390fccb52SAndrzej Pietrasiewicz * buffers are queued, IN EPs will NAK until buffers are ready */ 174490fccb52SAndrzej Pietrasiewicz udc_realize_hwep(udc, ep->hwep_num, ep->ep.maxpacket); 174590fccb52SAndrzej Pietrasiewicz udc_clr_buffer_hwep(udc, ep->hwep_num); 174690fccb52SAndrzej Pietrasiewicz uda_disable_hwepint(udc, ep->hwep_num); 174790fccb52SAndrzej Pietrasiewicz udc_clrstall_hwep(udc, ep->hwep_num); 174890fccb52SAndrzej Pietrasiewicz 174990fccb52SAndrzej Pietrasiewicz /* Clear all DMA statuses for this EP */ 175090fccb52SAndrzej Pietrasiewicz udc_ep_dma_disable(udc, ep->hwep_num); 175190fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr)); 175290fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_NDDRTINTCLR(udc->udp_baseaddr)); 175390fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 175490fccb52SAndrzej Pietrasiewicz writel(1 << ep->hwep_num, USBD_DMARCLR(udc->udp_baseaddr)); 175590fccb52SAndrzej Pietrasiewicz 175690fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 175790fccb52SAndrzej Pietrasiewicz 175890fccb52SAndrzej Pietrasiewicz atomic_inc(&udc->enabled_ep_cnt); 175990fccb52SAndrzej Pietrasiewicz return 0; 176090fccb52SAndrzej Pietrasiewicz } 176190fccb52SAndrzej Pietrasiewicz 176290fccb52SAndrzej Pietrasiewicz /* 176390fccb52SAndrzej Pietrasiewicz * Allocate a USB request list 176490fccb52SAndrzej Pietrasiewicz * Can be called with or without lock 176590fccb52SAndrzej Pietrasiewicz */ 176690fccb52SAndrzej Pietrasiewicz static struct usb_request *lpc32xx_ep_alloc_request(struct usb_ep *_ep, 176790fccb52SAndrzej Pietrasiewicz gfp_t gfp_flags) 176890fccb52SAndrzej Pietrasiewicz { 176990fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 177090fccb52SAndrzej Pietrasiewicz 177190fccb52SAndrzej Pietrasiewicz req = kzalloc(sizeof(struct lpc32xx_request), gfp_flags); 177290fccb52SAndrzej Pietrasiewicz if (!req) 177390fccb52SAndrzej Pietrasiewicz return NULL; 177490fccb52SAndrzej Pietrasiewicz 177590fccb52SAndrzej Pietrasiewicz INIT_LIST_HEAD(&req->queue); 177690fccb52SAndrzej Pietrasiewicz return &req->req; 177790fccb52SAndrzej Pietrasiewicz } 177890fccb52SAndrzej Pietrasiewicz 177990fccb52SAndrzej Pietrasiewicz /* 178090fccb52SAndrzej Pietrasiewicz * De-allocate a USB request list 178190fccb52SAndrzej Pietrasiewicz * Can be called with or without lock 178290fccb52SAndrzej Pietrasiewicz */ 178390fccb52SAndrzej Pietrasiewicz static void lpc32xx_ep_free_request(struct usb_ep *_ep, 178490fccb52SAndrzej Pietrasiewicz struct usb_request *_req) 178590fccb52SAndrzej Pietrasiewicz { 178690fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 178790fccb52SAndrzej Pietrasiewicz 178890fccb52SAndrzej Pietrasiewicz req = container_of(_req, struct lpc32xx_request, req); 178990fccb52SAndrzej Pietrasiewicz BUG_ON(!list_empty(&req->queue)); 179090fccb52SAndrzej Pietrasiewicz kfree(req); 179190fccb52SAndrzej Pietrasiewicz } 179290fccb52SAndrzej Pietrasiewicz 179390fccb52SAndrzej Pietrasiewicz /* Must be called without lock */ 179490fccb52SAndrzej Pietrasiewicz static int lpc32xx_ep_queue(struct usb_ep *_ep, 179590fccb52SAndrzej Pietrasiewicz struct usb_request *_req, gfp_t gfp_flags) 179690fccb52SAndrzej Pietrasiewicz { 179790fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 179890fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep; 179990fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc; 180090fccb52SAndrzej Pietrasiewicz unsigned long flags; 180190fccb52SAndrzej Pietrasiewicz int status = 0; 180290fccb52SAndrzej Pietrasiewicz 180390fccb52SAndrzej Pietrasiewicz req = container_of(_req, struct lpc32xx_request, req); 180490fccb52SAndrzej Pietrasiewicz ep = container_of(_ep, struct lpc32xx_ep, ep); 180590fccb52SAndrzej Pietrasiewicz 1806005a6430SPeter Chen if (!_ep || !_req || !_req->complete || !_req->buf || 180790fccb52SAndrzej Pietrasiewicz !list_empty(&req->queue)) 180890fccb52SAndrzej Pietrasiewicz return -EINVAL; 180990fccb52SAndrzej Pietrasiewicz 181090fccb52SAndrzej Pietrasiewicz udc = ep->udc; 181190fccb52SAndrzej Pietrasiewicz 1812005a6430SPeter Chen if (udc->gadget.speed == USB_SPEED_UNKNOWN) 1813005a6430SPeter Chen return -EPIPE; 181490fccb52SAndrzej Pietrasiewicz 181590fccb52SAndrzej Pietrasiewicz if (ep->lep) { 181690fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_dd_gad *dd; 181790fccb52SAndrzej Pietrasiewicz 181890fccb52SAndrzej Pietrasiewicz status = usb_gadget_map_request(&udc->gadget, _req, ep->is_in); 181990fccb52SAndrzej Pietrasiewicz if (status) 182090fccb52SAndrzej Pietrasiewicz return status; 182190fccb52SAndrzej Pietrasiewicz 182290fccb52SAndrzej Pietrasiewicz /* For the request, build a list of DDs */ 182390fccb52SAndrzej Pietrasiewicz dd = udc_dd_alloc(udc); 182490fccb52SAndrzej Pietrasiewicz if (!dd) { 182590fccb52SAndrzej Pietrasiewicz /* Error allocating DD */ 182690fccb52SAndrzej Pietrasiewicz return -ENOMEM; 182790fccb52SAndrzej Pietrasiewicz } 182890fccb52SAndrzej Pietrasiewicz req->dd_desc_ptr = dd; 182990fccb52SAndrzej Pietrasiewicz 183090fccb52SAndrzej Pietrasiewicz /* Setup the DMA descriptor */ 183190fccb52SAndrzej Pietrasiewicz dd->dd_next_phy = dd->dd_next_v = 0; 183290fccb52SAndrzej Pietrasiewicz dd->dd_buffer_addr = req->req.dma; 183390fccb52SAndrzej Pietrasiewicz dd->dd_status = 0; 183490fccb52SAndrzej Pietrasiewicz 183590fccb52SAndrzej Pietrasiewicz /* Special handling for ISO EPs */ 183690fccb52SAndrzej Pietrasiewicz if (ep->eptype == EP_ISO_TYPE) { 183790fccb52SAndrzej Pietrasiewicz dd->dd_setup = DD_SETUP_ISO_EP | 183890fccb52SAndrzej Pietrasiewicz DD_SETUP_PACKETLEN(0) | 183990fccb52SAndrzej Pietrasiewicz DD_SETUP_DMALENBYTES(1); 184090fccb52SAndrzej Pietrasiewicz dd->dd_iso_ps_mem_addr = dd->this_dma + 24; 184190fccb52SAndrzej Pietrasiewicz if (ep->is_in) 184290fccb52SAndrzej Pietrasiewicz dd->iso_status[0] = req->req.length; 184390fccb52SAndrzej Pietrasiewicz else 184490fccb52SAndrzej Pietrasiewicz dd->iso_status[0] = 0; 184590fccb52SAndrzej Pietrasiewicz } else 184690fccb52SAndrzej Pietrasiewicz dd->dd_setup = DD_SETUP_PACKETLEN(ep->ep.maxpacket) | 184790fccb52SAndrzej Pietrasiewicz DD_SETUP_DMALENBYTES(req->req.length); 184890fccb52SAndrzej Pietrasiewicz } 184990fccb52SAndrzej Pietrasiewicz 185090fccb52SAndrzej Pietrasiewicz ep_dbg(ep, "%s queue req %p len %d buf %p (in=%d) z=%d\n", _ep->name, 185190fccb52SAndrzej Pietrasiewicz _req, _req->length, _req->buf, ep->is_in, _req->zero); 185290fccb52SAndrzej Pietrasiewicz 185390fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 185490fccb52SAndrzej Pietrasiewicz 185590fccb52SAndrzej Pietrasiewicz _req->status = -EINPROGRESS; 185690fccb52SAndrzej Pietrasiewicz _req->actual = 0; 185790fccb52SAndrzej Pietrasiewicz req->send_zlp = _req->zero; 185890fccb52SAndrzej Pietrasiewicz 185990fccb52SAndrzej Pietrasiewicz /* Kickstart empty queues */ 186090fccb52SAndrzej Pietrasiewicz if (list_empty(&ep->queue)) { 186190fccb52SAndrzej Pietrasiewicz list_add_tail(&req->queue, &ep->queue); 186290fccb52SAndrzej Pietrasiewicz 186390fccb52SAndrzej Pietrasiewicz if (ep->hwep_num_base == 0) { 186490fccb52SAndrzej Pietrasiewicz /* Handle expected data direction */ 186590fccb52SAndrzej Pietrasiewicz if (ep->is_in) { 186690fccb52SAndrzej Pietrasiewicz /* IN packet to host */ 186790fccb52SAndrzej Pietrasiewicz udc->ep0state = DATA_IN; 186890fccb52SAndrzej Pietrasiewicz status = udc_ep0_in_req(udc); 186990fccb52SAndrzej Pietrasiewicz } else { 187090fccb52SAndrzej Pietrasiewicz /* OUT packet from host */ 187190fccb52SAndrzej Pietrasiewicz udc->ep0state = DATA_OUT; 187290fccb52SAndrzej Pietrasiewicz status = udc_ep0_out_req(udc); 187390fccb52SAndrzej Pietrasiewicz } 187490fccb52SAndrzej Pietrasiewicz } else if (ep->is_in) { 187590fccb52SAndrzej Pietrasiewicz /* IN packet to host and kick off transfer */ 187690fccb52SAndrzej Pietrasiewicz if (!ep->req_pending) 187790fccb52SAndrzej Pietrasiewicz udc_ep_in_req_dma(udc, ep); 187890fccb52SAndrzej Pietrasiewicz } else 187990fccb52SAndrzej Pietrasiewicz /* OUT packet from host and kick off list */ 188090fccb52SAndrzej Pietrasiewicz if (!ep->req_pending) 188190fccb52SAndrzej Pietrasiewicz udc_ep_out_req_dma(udc, ep); 188290fccb52SAndrzej Pietrasiewicz } else 188390fccb52SAndrzej Pietrasiewicz list_add_tail(&req->queue, &ep->queue); 188490fccb52SAndrzej Pietrasiewicz 188590fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 188690fccb52SAndrzej Pietrasiewicz 188790fccb52SAndrzej Pietrasiewicz return (status < 0) ? status : 0; 188890fccb52SAndrzej Pietrasiewicz } 188990fccb52SAndrzej Pietrasiewicz 189090fccb52SAndrzej Pietrasiewicz /* Must be called without lock */ 189190fccb52SAndrzej Pietrasiewicz static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) 189290fccb52SAndrzej Pietrasiewicz { 189390fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep; 189490fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 189590fccb52SAndrzej Pietrasiewicz unsigned long flags; 189690fccb52SAndrzej Pietrasiewicz 189790fccb52SAndrzej Pietrasiewicz ep = container_of(_ep, struct lpc32xx_ep, ep); 189890fccb52SAndrzej Pietrasiewicz if (!_ep || ep->hwep_num_base == 0) 189990fccb52SAndrzej Pietrasiewicz return -EINVAL; 190090fccb52SAndrzej Pietrasiewicz 190190fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&ep->udc->lock, flags); 190290fccb52SAndrzej Pietrasiewicz 190390fccb52SAndrzej Pietrasiewicz /* make sure it's actually queued on this endpoint */ 190490fccb52SAndrzej Pietrasiewicz list_for_each_entry(req, &ep->queue, queue) { 190590fccb52SAndrzej Pietrasiewicz if (&req->req == _req) 190690fccb52SAndrzej Pietrasiewicz break; 190790fccb52SAndrzej Pietrasiewicz } 190890fccb52SAndrzej Pietrasiewicz if (&req->req != _req) { 190990fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->udc->lock, flags); 191090fccb52SAndrzej Pietrasiewicz return -EINVAL; 191190fccb52SAndrzej Pietrasiewicz } 191290fccb52SAndrzej Pietrasiewicz 191390fccb52SAndrzej Pietrasiewicz done(ep, req, -ECONNRESET); 191490fccb52SAndrzej Pietrasiewicz 191590fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&ep->udc->lock, flags); 191690fccb52SAndrzej Pietrasiewicz 191790fccb52SAndrzej Pietrasiewicz return 0; 191890fccb52SAndrzej Pietrasiewicz } 191990fccb52SAndrzej Pietrasiewicz 192090fccb52SAndrzej Pietrasiewicz /* Must be called without lock */ 192190fccb52SAndrzej Pietrasiewicz static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value) 192290fccb52SAndrzej Pietrasiewicz { 192390fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 192490fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = ep->udc; 192590fccb52SAndrzej Pietrasiewicz unsigned long flags; 192690fccb52SAndrzej Pietrasiewicz 192790fccb52SAndrzej Pietrasiewicz if ((!ep) || (ep->hwep_num <= 1)) 192890fccb52SAndrzej Pietrasiewicz return -EINVAL; 192990fccb52SAndrzej Pietrasiewicz 193090fccb52SAndrzej Pietrasiewicz /* Don't halt an IN EP */ 193190fccb52SAndrzej Pietrasiewicz if (ep->is_in) 193290fccb52SAndrzej Pietrasiewicz return -EAGAIN; 193390fccb52SAndrzej Pietrasiewicz 193490fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 193590fccb52SAndrzej Pietrasiewicz 193690fccb52SAndrzej Pietrasiewicz if (value == 1) { 193790fccb52SAndrzej Pietrasiewicz /* stall */ 193890fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num), 193990fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(EP_STAT_ST)); 194090fccb52SAndrzej Pietrasiewicz } else { 194190fccb52SAndrzej Pietrasiewicz /* End stall */ 194290fccb52SAndrzej Pietrasiewicz ep->wedge = 0; 194390fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_EP_STAT(ep->hwep_num), 194490fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(0)); 194590fccb52SAndrzej Pietrasiewicz } 194690fccb52SAndrzej Pietrasiewicz 194790fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 194890fccb52SAndrzej Pietrasiewicz 194990fccb52SAndrzej Pietrasiewicz return 0; 195090fccb52SAndrzej Pietrasiewicz } 195190fccb52SAndrzej Pietrasiewicz 195290fccb52SAndrzej Pietrasiewicz /* set the halt feature and ignores clear requests */ 195390fccb52SAndrzej Pietrasiewicz static int lpc32xx_ep_set_wedge(struct usb_ep *_ep) 195490fccb52SAndrzej Pietrasiewicz { 195590fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep); 195690fccb52SAndrzej Pietrasiewicz 195790fccb52SAndrzej Pietrasiewicz if (!_ep || !ep->udc) 195890fccb52SAndrzej Pietrasiewicz return -EINVAL; 195990fccb52SAndrzej Pietrasiewicz 196090fccb52SAndrzej Pietrasiewicz ep->wedge = 1; 196190fccb52SAndrzej Pietrasiewicz 196290fccb52SAndrzej Pietrasiewicz return usb_ep_set_halt(_ep); 196390fccb52SAndrzej Pietrasiewicz } 196490fccb52SAndrzej Pietrasiewicz 196590fccb52SAndrzej Pietrasiewicz static const struct usb_ep_ops lpc32xx_ep_ops = { 196690fccb52SAndrzej Pietrasiewicz .enable = lpc32xx_ep_enable, 196790fccb52SAndrzej Pietrasiewicz .disable = lpc32xx_ep_disable, 196890fccb52SAndrzej Pietrasiewicz .alloc_request = lpc32xx_ep_alloc_request, 196990fccb52SAndrzej Pietrasiewicz .free_request = lpc32xx_ep_free_request, 197090fccb52SAndrzej Pietrasiewicz .queue = lpc32xx_ep_queue, 197190fccb52SAndrzej Pietrasiewicz .dequeue = lpc32xx_ep_dequeue, 197290fccb52SAndrzej Pietrasiewicz .set_halt = lpc32xx_ep_set_halt, 197390fccb52SAndrzej Pietrasiewicz .set_wedge = lpc32xx_ep_set_wedge, 197490fccb52SAndrzej Pietrasiewicz }; 197590fccb52SAndrzej Pietrasiewicz 197690fccb52SAndrzej Pietrasiewicz /* Send a ZLP on a non-0 IN EP */ 197790fccb52SAndrzej Pietrasiewicz void udc_send_in_zlp(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 197890fccb52SAndrzej Pietrasiewicz { 197990fccb52SAndrzej Pietrasiewicz /* Clear EP status */ 198090fccb52SAndrzej Pietrasiewicz udc_clearep_getsts(udc, ep->hwep_num); 198190fccb52SAndrzej Pietrasiewicz 198290fccb52SAndrzej Pietrasiewicz /* Send ZLP via FIFO mechanism */ 198390fccb52SAndrzej Pietrasiewicz udc_write_hwep(udc, ep->hwep_num, NULL, 0); 198490fccb52SAndrzej Pietrasiewicz } 198590fccb52SAndrzej Pietrasiewicz 198690fccb52SAndrzej Pietrasiewicz /* 198790fccb52SAndrzej Pietrasiewicz * Handle EP completion for ZLP 198890fccb52SAndrzej Pietrasiewicz * This function will only be called when a delayed ZLP needs to be sent out 198990fccb52SAndrzej Pietrasiewicz * after a DMA transfer has filled both buffers. 199090fccb52SAndrzej Pietrasiewicz */ 199190fccb52SAndrzej Pietrasiewicz void udc_handle_eps(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 199290fccb52SAndrzej Pietrasiewicz { 199390fccb52SAndrzej Pietrasiewicz u32 epstatus; 199490fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 199590fccb52SAndrzej Pietrasiewicz 199690fccb52SAndrzej Pietrasiewicz if (ep->hwep_num <= 0) 199790fccb52SAndrzej Pietrasiewicz return; 199890fccb52SAndrzej Pietrasiewicz 199990fccb52SAndrzej Pietrasiewicz uda_clear_hwepint(udc, ep->hwep_num); 200090fccb52SAndrzej Pietrasiewicz 200190fccb52SAndrzej Pietrasiewicz /* If this interrupt isn't enabled, return now */ 200290fccb52SAndrzej Pietrasiewicz if (!(udc->enabled_hwepints & (1 << ep->hwep_num))) 200390fccb52SAndrzej Pietrasiewicz return; 200490fccb52SAndrzej Pietrasiewicz 200590fccb52SAndrzej Pietrasiewicz /* Get endpoint status */ 200690fccb52SAndrzej Pietrasiewicz epstatus = udc_clearep_getsts(udc, ep->hwep_num); 200790fccb52SAndrzej Pietrasiewicz 200890fccb52SAndrzej Pietrasiewicz /* 200990fccb52SAndrzej Pietrasiewicz * This should never happen, but protect against writing to the 201090fccb52SAndrzej Pietrasiewicz * buffer when full. 201190fccb52SAndrzej Pietrasiewicz */ 201290fccb52SAndrzej Pietrasiewicz if (epstatus & EP_SEL_F) 201390fccb52SAndrzej Pietrasiewicz return; 201490fccb52SAndrzej Pietrasiewicz 201590fccb52SAndrzej Pietrasiewicz if (ep->is_in) { 201690fccb52SAndrzej Pietrasiewicz udc_send_in_zlp(udc, ep); 201790fccb52SAndrzej Pietrasiewicz uda_disable_hwepint(udc, ep->hwep_num); 201890fccb52SAndrzej Pietrasiewicz } else 201990fccb52SAndrzej Pietrasiewicz return; 202090fccb52SAndrzej Pietrasiewicz 202190fccb52SAndrzej Pietrasiewicz /* If there isn't a request waiting, something went wrong */ 202290fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 202390fccb52SAndrzej Pietrasiewicz if (req) { 202490fccb52SAndrzej Pietrasiewicz done(ep, req, 0); 202590fccb52SAndrzej Pietrasiewicz 202690fccb52SAndrzej Pietrasiewicz /* Start another request if ready */ 202790fccb52SAndrzej Pietrasiewicz if (!list_empty(&ep->queue)) { 202890fccb52SAndrzej Pietrasiewicz if (ep->is_in) 202990fccb52SAndrzej Pietrasiewicz udc_ep_in_req_dma(udc, ep); 203090fccb52SAndrzej Pietrasiewicz else 203190fccb52SAndrzej Pietrasiewicz udc_ep_out_req_dma(udc, ep); 203290fccb52SAndrzej Pietrasiewicz } else 203390fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 203490fccb52SAndrzej Pietrasiewicz } 203590fccb52SAndrzej Pietrasiewicz } 203690fccb52SAndrzej Pietrasiewicz 203790fccb52SAndrzej Pietrasiewicz 203890fccb52SAndrzej Pietrasiewicz /* DMA end of transfer completion */ 203990fccb52SAndrzej Pietrasiewicz static void udc_handle_dma_ep(struct lpc32xx_udc *udc, struct lpc32xx_ep *ep) 204090fccb52SAndrzej Pietrasiewicz { 204190fccb52SAndrzej Pietrasiewicz u32 status, epstatus; 204290fccb52SAndrzej Pietrasiewicz struct lpc32xx_request *req; 204390fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_dd_gad *dd; 204490fccb52SAndrzej Pietrasiewicz 204590fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_USB_GADGET_DEBUG_FILES 204690fccb52SAndrzej Pietrasiewicz ep->totalints++; 204790fccb52SAndrzej Pietrasiewicz #endif 204890fccb52SAndrzej Pietrasiewicz 204990fccb52SAndrzej Pietrasiewicz req = list_entry(ep->queue.next, struct lpc32xx_request, queue); 205090fccb52SAndrzej Pietrasiewicz if (!req) { 205190fccb52SAndrzej Pietrasiewicz ep_err(ep, "DMA interrupt on no req!\n"); 205290fccb52SAndrzej Pietrasiewicz return; 205390fccb52SAndrzej Pietrasiewicz } 205490fccb52SAndrzej Pietrasiewicz dd = req->dd_desc_ptr; 205590fccb52SAndrzej Pietrasiewicz 205690fccb52SAndrzej Pietrasiewicz /* DMA descriptor should always be retired for this call */ 205790fccb52SAndrzej Pietrasiewicz if (!(dd->dd_status & DD_STATUS_DD_RETIRED)) 205890fccb52SAndrzej Pietrasiewicz ep_warn(ep, "DMA descriptor did not retire\n"); 205990fccb52SAndrzej Pietrasiewicz 206090fccb52SAndrzej Pietrasiewicz /* Disable DMA */ 206190fccb52SAndrzej Pietrasiewicz udc_ep_dma_disable(udc, ep->hwep_num); 206290fccb52SAndrzej Pietrasiewicz writel((1 << ep->hwep_num), USBD_EOTINTCLR(udc->udp_baseaddr)); 206390fccb52SAndrzej Pietrasiewicz writel((1 << ep->hwep_num), USBD_NDDRTINTCLR(udc->udp_baseaddr)); 206490fccb52SAndrzej Pietrasiewicz 206590fccb52SAndrzej Pietrasiewicz /* System error? */ 206690fccb52SAndrzej Pietrasiewicz if (readl(USBD_SYSERRTINTST(udc->udp_baseaddr)) & 206790fccb52SAndrzej Pietrasiewicz (1 << ep->hwep_num)) { 206890fccb52SAndrzej Pietrasiewicz writel((1 << ep->hwep_num), 206990fccb52SAndrzej Pietrasiewicz USBD_SYSERRTINTCLR(udc->udp_baseaddr)); 207090fccb52SAndrzej Pietrasiewicz ep_err(ep, "AHB critical error!\n"); 207190fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 207290fccb52SAndrzej Pietrasiewicz 207390fccb52SAndrzej Pietrasiewicz /* The error could have occurred on a packet of a multipacket 207490fccb52SAndrzej Pietrasiewicz * transfer, so recovering the transfer is not possible. Close 207590fccb52SAndrzej Pietrasiewicz * the request with an error */ 207690fccb52SAndrzej Pietrasiewicz done(ep, req, -ECONNABORTED); 207790fccb52SAndrzej Pietrasiewicz return; 207890fccb52SAndrzej Pietrasiewicz } 207990fccb52SAndrzej Pietrasiewicz 208090fccb52SAndrzej Pietrasiewicz /* Handle the current DD's status */ 208190fccb52SAndrzej Pietrasiewicz status = dd->dd_status; 208290fccb52SAndrzej Pietrasiewicz switch (status & DD_STATUS_STS_MASK) { 208390fccb52SAndrzej Pietrasiewicz case DD_STATUS_STS_NS: 208490fccb52SAndrzej Pietrasiewicz /* DD not serviced? This shouldn't happen! */ 208590fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 208690fccb52SAndrzej Pietrasiewicz ep_err(ep, "DMA critical EP error: DD not serviced (0x%x)!\n", 208790fccb52SAndrzej Pietrasiewicz status); 208890fccb52SAndrzej Pietrasiewicz 208990fccb52SAndrzej Pietrasiewicz done(ep, req, -ECONNABORTED); 209090fccb52SAndrzej Pietrasiewicz return; 209190fccb52SAndrzej Pietrasiewicz 209290fccb52SAndrzej Pietrasiewicz case DD_STATUS_STS_BS: 209390fccb52SAndrzej Pietrasiewicz /* Interrupt only fires on EOT - This shouldn't happen! */ 209490fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 209590fccb52SAndrzej Pietrasiewicz ep_err(ep, "DMA critical EP error: EOT prior to service completion (0x%x)!\n", 209690fccb52SAndrzej Pietrasiewicz status); 209790fccb52SAndrzej Pietrasiewicz done(ep, req, -ECONNABORTED); 209890fccb52SAndrzej Pietrasiewicz return; 209990fccb52SAndrzej Pietrasiewicz 210090fccb52SAndrzej Pietrasiewicz case DD_STATUS_STS_NC: 210190fccb52SAndrzej Pietrasiewicz case DD_STATUS_STS_DUR: 210290fccb52SAndrzej Pietrasiewicz /* Really just a short packet, not an underrun */ 210390fccb52SAndrzej Pietrasiewicz /* This is a good status and what we expect */ 210490fccb52SAndrzej Pietrasiewicz break; 210590fccb52SAndrzej Pietrasiewicz 210690fccb52SAndrzej Pietrasiewicz default: 210790fccb52SAndrzej Pietrasiewicz /* Data overrun, system error, or unknown */ 210890fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 210990fccb52SAndrzej Pietrasiewicz ep_err(ep, "DMA critical EP error: System error (0x%x)!\n", 211090fccb52SAndrzej Pietrasiewicz status); 211190fccb52SAndrzej Pietrasiewicz done(ep, req, -ECONNABORTED); 211290fccb52SAndrzej Pietrasiewicz return; 211390fccb52SAndrzej Pietrasiewicz } 211490fccb52SAndrzej Pietrasiewicz 211590fccb52SAndrzej Pietrasiewicz /* ISO endpoints are handled differently */ 211690fccb52SAndrzej Pietrasiewicz if (ep->eptype == EP_ISO_TYPE) { 211790fccb52SAndrzej Pietrasiewicz if (ep->is_in) 211890fccb52SAndrzej Pietrasiewicz req->req.actual = req->req.length; 211990fccb52SAndrzej Pietrasiewicz else 212090fccb52SAndrzej Pietrasiewicz req->req.actual = dd->iso_status[0] & 0xFFFF; 212190fccb52SAndrzej Pietrasiewicz } else 212290fccb52SAndrzej Pietrasiewicz req->req.actual += DD_STATUS_CURDMACNT(status); 212390fccb52SAndrzej Pietrasiewicz 212490fccb52SAndrzej Pietrasiewicz /* Send a ZLP if necessary. This will be done for non-int 212590fccb52SAndrzej Pietrasiewicz * packets which have a size that is a divisor of MAXP */ 212690fccb52SAndrzej Pietrasiewicz if (req->send_zlp) { 212790fccb52SAndrzej Pietrasiewicz /* 212890fccb52SAndrzej Pietrasiewicz * If at least 1 buffer is available, send the ZLP now. 212990fccb52SAndrzej Pietrasiewicz * Otherwise, the ZLP send needs to be deferred until a 213090fccb52SAndrzej Pietrasiewicz * buffer is available. 213190fccb52SAndrzej Pietrasiewicz */ 213290fccb52SAndrzej Pietrasiewicz if (udc_clearep_getsts(udc, ep->hwep_num) & EP_SEL_F) { 213390fccb52SAndrzej Pietrasiewicz udc_clearep_getsts(udc, ep->hwep_num); 213490fccb52SAndrzej Pietrasiewicz uda_enable_hwepint(udc, ep->hwep_num); 213590fccb52SAndrzej Pietrasiewicz epstatus = udc_clearep_getsts(udc, ep->hwep_num); 213690fccb52SAndrzej Pietrasiewicz 213790fccb52SAndrzej Pietrasiewicz /* Let the EP interrupt handle the ZLP */ 213890fccb52SAndrzej Pietrasiewicz return; 213990fccb52SAndrzej Pietrasiewicz } else 214090fccb52SAndrzej Pietrasiewicz udc_send_in_zlp(udc, ep); 214190fccb52SAndrzej Pietrasiewicz } 214290fccb52SAndrzej Pietrasiewicz 214390fccb52SAndrzej Pietrasiewicz /* Transfer request is complete */ 214490fccb52SAndrzej Pietrasiewicz done(ep, req, 0); 214590fccb52SAndrzej Pietrasiewicz 214690fccb52SAndrzej Pietrasiewicz /* Start another request if ready */ 214790fccb52SAndrzej Pietrasiewicz udc_clearep_getsts(udc, ep->hwep_num); 214890fccb52SAndrzej Pietrasiewicz if (!list_empty((&ep->queue))) { 214990fccb52SAndrzej Pietrasiewicz if (ep->is_in) 215090fccb52SAndrzej Pietrasiewicz udc_ep_in_req_dma(udc, ep); 215190fccb52SAndrzej Pietrasiewicz else 215290fccb52SAndrzej Pietrasiewicz udc_ep_out_req_dma(udc, ep); 215390fccb52SAndrzej Pietrasiewicz } else 215490fccb52SAndrzej Pietrasiewicz ep->req_pending = 0; 215590fccb52SAndrzej Pietrasiewicz 215690fccb52SAndrzej Pietrasiewicz } 215790fccb52SAndrzej Pietrasiewicz 215890fccb52SAndrzej Pietrasiewicz /* 215990fccb52SAndrzej Pietrasiewicz * 216090fccb52SAndrzej Pietrasiewicz * Endpoint 0 functions 216190fccb52SAndrzej Pietrasiewicz * 216290fccb52SAndrzej Pietrasiewicz */ 216390fccb52SAndrzej Pietrasiewicz static void udc_handle_dev(struct lpc32xx_udc *udc) 216490fccb52SAndrzej Pietrasiewicz { 216590fccb52SAndrzej Pietrasiewicz u32 tmp; 216690fccb52SAndrzej Pietrasiewicz 216790fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_GET_DEV_STAT); 216890fccb52SAndrzej Pietrasiewicz tmp = udc_protocol_cmd_r(udc, DAT_GET_DEV_STAT); 216990fccb52SAndrzej Pietrasiewicz 217090fccb52SAndrzej Pietrasiewicz if (tmp & DEV_RST) 217190fccb52SAndrzej Pietrasiewicz uda_usb_reset(udc); 217290fccb52SAndrzej Pietrasiewicz else if (tmp & DEV_CON_CH) 217390fccb52SAndrzej Pietrasiewicz uda_power_event(udc, (tmp & DEV_CON)); 217490fccb52SAndrzej Pietrasiewicz else if (tmp & DEV_SUS_CH) { 217590fccb52SAndrzej Pietrasiewicz if (tmp & DEV_SUS) { 217690fccb52SAndrzej Pietrasiewicz if (udc->vbus == 0) 217790fccb52SAndrzej Pietrasiewicz stop_activity(udc); 217890fccb52SAndrzej Pietrasiewicz else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) && 217990fccb52SAndrzej Pietrasiewicz udc->driver) { 218090fccb52SAndrzej Pietrasiewicz /* Power down transceiver */ 218190fccb52SAndrzej Pietrasiewicz udc->poweron = 0; 218290fccb52SAndrzej Pietrasiewicz schedule_work(&udc->pullup_job); 218390fccb52SAndrzej Pietrasiewicz uda_resm_susp_event(udc, 1); 218490fccb52SAndrzej Pietrasiewicz } 218590fccb52SAndrzej Pietrasiewicz } else if ((udc->gadget.speed != USB_SPEED_UNKNOWN) && 218690fccb52SAndrzej Pietrasiewicz udc->driver && udc->vbus) { 218790fccb52SAndrzej Pietrasiewicz uda_resm_susp_event(udc, 0); 218890fccb52SAndrzej Pietrasiewicz /* Power up transceiver */ 218990fccb52SAndrzej Pietrasiewicz udc->poweron = 1; 219090fccb52SAndrzej Pietrasiewicz schedule_work(&udc->pullup_job); 219190fccb52SAndrzej Pietrasiewicz } 219290fccb52SAndrzej Pietrasiewicz } 219390fccb52SAndrzej Pietrasiewicz } 219490fccb52SAndrzej Pietrasiewicz 219590fccb52SAndrzej Pietrasiewicz static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex) 219690fccb52SAndrzej Pietrasiewicz { 219790fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep; 219890fccb52SAndrzej Pietrasiewicz u32 ep0buff = 0, tmp; 219990fccb52SAndrzej Pietrasiewicz 220090fccb52SAndrzej Pietrasiewicz switch (reqtype & USB_RECIP_MASK) { 220190fccb52SAndrzej Pietrasiewicz case USB_RECIP_INTERFACE: 220290fccb52SAndrzej Pietrasiewicz break; /* Not supported */ 220390fccb52SAndrzej Pietrasiewicz 220490fccb52SAndrzej Pietrasiewicz case USB_RECIP_DEVICE: 2205d60d9392SPeter Chen ep0buff = udc->gadget.is_selfpowered; 220690fccb52SAndrzej Pietrasiewicz if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP)) 220790fccb52SAndrzej Pietrasiewicz ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP); 220890fccb52SAndrzej Pietrasiewicz break; 220990fccb52SAndrzej Pietrasiewicz 221090fccb52SAndrzej Pietrasiewicz case USB_RECIP_ENDPOINT: 221190fccb52SAndrzej Pietrasiewicz tmp = wIndex & USB_ENDPOINT_NUMBER_MASK; 221290fccb52SAndrzej Pietrasiewicz ep = &udc->ep[tmp]; 221390fccb52SAndrzej Pietrasiewicz if ((tmp == 0) || (tmp >= NUM_ENDPOINTS)) 221490fccb52SAndrzej Pietrasiewicz return -EOPNOTSUPP; 221590fccb52SAndrzej Pietrasiewicz 221690fccb52SAndrzej Pietrasiewicz if (wIndex & USB_DIR_IN) { 221790fccb52SAndrzej Pietrasiewicz if (!ep->is_in) 221890fccb52SAndrzej Pietrasiewicz return -EOPNOTSUPP; /* Something's wrong */ 221990fccb52SAndrzej Pietrasiewicz } else if (ep->is_in) 222090fccb52SAndrzej Pietrasiewicz return -EOPNOTSUPP; /* Not an IN endpoint */ 222190fccb52SAndrzej Pietrasiewicz 222290fccb52SAndrzej Pietrasiewicz /* Get status of the endpoint */ 222390fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_SEL_EP(ep->hwep_num)); 222490fccb52SAndrzej Pietrasiewicz tmp = udc_protocol_cmd_r(udc, DAT_SEL_EP(ep->hwep_num)); 222590fccb52SAndrzej Pietrasiewicz 222690fccb52SAndrzej Pietrasiewicz if (tmp & EP_SEL_ST) 222790fccb52SAndrzej Pietrasiewicz ep0buff = (1 << USB_ENDPOINT_HALT); 222890fccb52SAndrzej Pietrasiewicz else 222990fccb52SAndrzej Pietrasiewicz ep0buff = 0; 223090fccb52SAndrzej Pietrasiewicz break; 223190fccb52SAndrzej Pietrasiewicz 223290fccb52SAndrzej Pietrasiewicz default: 223390fccb52SAndrzej Pietrasiewicz break; 223490fccb52SAndrzej Pietrasiewicz } 223590fccb52SAndrzej Pietrasiewicz 223690fccb52SAndrzej Pietrasiewicz /* Return data */ 223790fccb52SAndrzej Pietrasiewicz udc_write_hwep(udc, EP_IN, &ep0buff, 2); 223890fccb52SAndrzej Pietrasiewicz 223990fccb52SAndrzej Pietrasiewicz return 0; 224090fccb52SAndrzej Pietrasiewicz } 224190fccb52SAndrzej Pietrasiewicz 224290fccb52SAndrzej Pietrasiewicz static void udc_handle_ep0_setup(struct lpc32xx_udc *udc) 224390fccb52SAndrzej Pietrasiewicz { 224490fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep, *ep0 = &udc->ep[0]; 224590fccb52SAndrzej Pietrasiewicz struct usb_ctrlrequest ctrlpkt; 224690fccb52SAndrzej Pietrasiewicz int i, bytes; 224790fccb52SAndrzej Pietrasiewicz u16 wIndex, wValue, wLength, reqtype, req, tmp; 224890fccb52SAndrzej Pietrasiewicz 224990fccb52SAndrzej Pietrasiewicz /* Nuke previous transfers */ 225090fccb52SAndrzej Pietrasiewicz nuke(ep0, -EPROTO); 225190fccb52SAndrzej Pietrasiewicz 225290fccb52SAndrzej Pietrasiewicz /* Get setup packet */ 225390fccb52SAndrzej Pietrasiewicz bytes = udc_read_hwep(udc, EP_OUT, (u32 *) &ctrlpkt, 8); 225490fccb52SAndrzej Pietrasiewicz if (bytes != 8) { 225590fccb52SAndrzej Pietrasiewicz ep_warn(ep0, "Incorrectly sized setup packet (s/b 8, is %d)!\n", 225690fccb52SAndrzej Pietrasiewicz bytes); 225790fccb52SAndrzej Pietrasiewicz return; 225890fccb52SAndrzej Pietrasiewicz } 225990fccb52SAndrzej Pietrasiewicz 226090fccb52SAndrzej Pietrasiewicz /* Native endianness */ 226190fccb52SAndrzej Pietrasiewicz wIndex = le16_to_cpu(ctrlpkt.wIndex); 226290fccb52SAndrzej Pietrasiewicz wValue = le16_to_cpu(ctrlpkt.wValue); 226390fccb52SAndrzej Pietrasiewicz wLength = le16_to_cpu(ctrlpkt.wLength); 226490fccb52SAndrzej Pietrasiewicz reqtype = le16_to_cpu(ctrlpkt.bRequestType); 226590fccb52SAndrzej Pietrasiewicz 226690fccb52SAndrzej Pietrasiewicz /* Set direction of EP0 */ 226790fccb52SAndrzej Pietrasiewicz if (likely(reqtype & USB_DIR_IN)) 226890fccb52SAndrzej Pietrasiewicz ep0->is_in = 1; 226990fccb52SAndrzej Pietrasiewicz else 227090fccb52SAndrzej Pietrasiewicz ep0->is_in = 0; 227190fccb52SAndrzej Pietrasiewicz 227290fccb52SAndrzej Pietrasiewicz /* Handle SETUP packet */ 227390fccb52SAndrzej Pietrasiewicz req = le16_to_cpu(ctrlpkt.bRequest); 227490fccb52SAndrzej Pietrasiewicz switch (req) { 227590fccb52SAndrzej Pietrasiewicz case USB_REQ_CLEAR_FEATURE: 227690fccb52SAndrzej Pietrasiewicz case USB_REQ_SET_FEATURE: 227790fccb52SAndrzej Pietrasiewicz switch (reqtype) { 227890fccb52SAndrzej Pietrasiewicz case (USB_TYPE_STANDARD | USB_RECIP_DEVICE): 227990fccb52SAndrzej Pietrasiewicz if (wValue != USB_DEVICE_REMOTE_WAKEUP) 228090fccb52SAndrzej Pietrasiewicz goto stall; /* Nothing else handled */ 228190fccb52SAndrzej Pietrasiewicz 228290fccb52SAndrzej Pietrasiewicz /* Tell board about event */ 228390fccb52SAndrzej Pietrasiewicz if (req == USB_REQ_CLEAR_FEATURE) 228490fccb52SAndrzej Pietrasiewicz udc->dev_status &= 228590fccb52SAndrzej Pietrasiewicz ~(1 << USB_DEVICE_REMOTE_WAKEUP); 228690fccb52SAndrzej Pietrasiewicz else 228790fccb52SAndrzej Pietrasiewicz udc->dev_status |= 228890fccb52SAndrzej Pietrasiewicz (1 << USB_DEVICE_REMOTE_WAKEUP); 228990fccb52SAndrzej Pietrasiewicz uda_remwkp_cgh(udc); 229090fccb52SAndrzej Pietrasiewicz goto zlp_send; 229190fccb52SAndrzej Pietrasiewicz 229290fccb52SAndrzej Pietrasiewicz case (USB_TYPE_STANDARD | USB_RECIP_ENDPOINT): 229390fccb52SAndrzej Pietrasiewicz tmp = wIndex & USB_ENDPOINT_NUMBER_MASK; 229490fccb52SAndrzej Pietrasiewicz if ((wValue != USB_ENDPOINT_HALT) || 229590fccb52SAndrzej Pietrasiewicz (tmp >= NUM_ENDPOINTS)) 229690fccb52SAndrzej Pietrasiewicz break; 229790fccb52SAndrzej Pietrasiewicz 229890fccb52SAndrzej Pietrasiewicz /* Find hardware endpoint from logical endpoint */ 229990fccb52SAndrzej Pietrasiewicz ep = &udc->ep[tmp]; 230090fccb52SAndrzej Pietrasiewicz tmp = ep->hwep_num; 230190fccb52SAndrzej Pietrasiewicz if (tmp == 0) 230290fccb52SAndrzej Pietrasiewicz break; 230390fccb52SAndrzej Pietrasiewicz 230490fccb52SAndrzej Pietrasiewicz if (req == USB_REQ_SET_FEATURE) 230590fccb52SAndrzej Pietrasiewicz udc_stall_hwep(udc, tmp); 230690fccb52SAndrzej Pietrasiewicz else if (!ep->wedge) 230790fccb52SAndrzej Pietrasiewicz udc_clrstall_hwep(udc, tmp); 230890fccb52SAndrzej Pietrasiewicz 230990fccb52SAndrzej Pietrasiewicz goto zlp_send; 231090fccb52SAndrzej Pietrasiewicz 231190fccb52SAndrzej Pietrasiewicz default: 231290fccb52SAndrzej Pietrasiewicz break; 231390fccb52SAndrzej Pietrasiewicz } 231490fccb52SAndrzej Pietrasiewicz 231590fccb52SAndrzej Pietrasiewicz 231690fccb52SAndrzej Pietrasiewicz case USB_REQ_SET_ADDRESS: 231790fccb52SAndrzej Pietrasiewicz if (reqtype == (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) { 231890fccb52SAndrzej Pietrasiewicz udc_set_address(udc, wValue); 231990fccb52SAndrzej Pietrasiewicz goto zlp_send; 232090fccb52SAndrzej Pietrasiewicz } 232190fccb52SAndrzej Pietrasiewicz break; 232290fccb52SAndrzej Pietrasiewicz 232390fccb52SAndrzej Pietrasiewicz case USB_REQ_GET_STATUS: 232490fccb52SAndrzej Pietrasiewicz udc_get_status(udc, reqtype, wIndex); 232590fccb52SAndrzej Pietrasiewicz return; 232690fccb52SAndrzej Pietrasiewicz 232790fccb52SAndrzej Pietrasiewicz default: 232890fccb52SAndrzej Pietrasiewicz break; /* Let GadgetFS handle the descriptor instead */ 232990fccb52SAndrzej Pietrasiewicz } 233090fccb52SAndrzej Pietrasiewicz 233190fccb52SAndrzej Pietrasiewicz if (likely(udc->driver)) { 233290fccb52SAndrzej Pietrasiewicz /* device-2-host (IN) or no data setup command, process 233390fccb52SAndrzej Pietrasiewicz * immediately */ 233490fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 233590fccb52SAndrzej Pietrasiewicz i = udc->driver->setup(&udc->gadget, &ctrlpkt); 233690fccb52SAndrzej Pietrasiewicz 233790fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 233890fccb52SAndrzej Pietrasiewicz if (req == USB_REQ_SET_CONFIGURATION) { 233990fccb52SAndrzej Pietrasiewicz /* Configuration is set after endpoints are realized */ 234090fccb52SAndrzej Pietrasiewicz if (wValue) { 234190fccb52SAndrzej Pietrasiewicz /* Set configuration */ 234290fccb52SAndrzej Pietrasiewicz udc_set_device_configured(udc); 234390fccb52SAndrzej Pietrasiewicz 234490fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_MODE, 234590fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(AP_CLK | 234690fccb52SAndrzej Pietrasiewicz INAK_BI | INAK_II)); 234790fccb52SAndrzej Pietrasiewicz } else { 234890fccb52SAndrzej Pietrasiewicz /* Clear configuration */ 234990fccb52SAndrzej Pietrasiewicz udc_set_device_unconfigured(udc); 235090fccb52SAndrzej Pietrasiewicz 235190fccb52SAndrzej Pietrasiewicz /* Disable NAK interrupts */ 235290fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_data_w(udc, CMD_SET_MODE, 235390fccb52SAndrzej Pietrasiewicz DAT_WR_BYTE(AP_CLK)); 235490fccb52SAndrzej Pietrasiewicz } 235590fccb52SAndrzej Pietrasiewicz } 235690fccb52SAndrzej Pietrasiewicz 235790fccb52SAndrzej Pietrasiewicz if (i < 0) { 235890fccb52SAndrzej Pietrasiewicz /* setup processing failed, force stall */ 235990fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, 236090fccb52SAndrzej Pietrasiewicz "req %02x.%02x protocol STALL; stat %d\n", 236190fccb52SAndrzej Pietrasiewicz reqtype, req, i); 236290fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 236390fccb52SAndrzej Pietrasiewicz goto stall; 236490fccb52SAndrzej Pietrasiewicz } 236590fccb52SAndrzej Pietrasiewicz } 236690fccb52SAndrzej Pietrasiewicz 236790fccb52SAndrzej Pietrasiewicz if (!ep0->is_in) 236890fccb52SAndrzej Pietrasiewicz udc_ep0_send_zlp(udc); /* ZLP IN packet on data phase */ 236990fccb52SAndrzej Pietrasiewicz 237090fccb52SAndrzej Pietrasiewicz return; 237190fccb52SAndrzej Pietrasiewicz 237290fccb52SAndrzej Pietrasiewicz stall: 237390fccb52SAndrzej Pietrasiewicz udc_stall_hwep(udc, EP_IN); 237490fccb52SAndrzej Pietrasiewicz return; 237590fccb52SAndrzej Pietrasiewicz 237690fccb52SAndrzej Pietrasiewicz zlp_send: 237790fccb52SAndrzej Pietrasiewicz udc_ep0_send_zlp(udc); 237890fccb52SAndrzej Pietrasiewicz return; 237990fccb52SAndrzej Pietrasiewicz } 238090fccb52SAndrzej Pietrasiewicz 238190fccb52SAndrzej Pietrasiewicz /* IN endpoint 0 transfer */ 238290fccb52SAndrzej Pietrasiewicz static void udc_handle_ep0_in(struct lpc32xx_udc *udc) 238390fccb52SAndrzej Pietrasiewicz { 238490fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep0 = &udc->ep[0]; 238590fccb52SAndrzej Pietrasiewicz u32 epstatus; 238690fccb52SAndrzej Pietrasiewicz 238790fccb52SAndrzej Pietrasiewicz /* Clear EP interrupt */ 238890fccb52SAndrzej Pietrasiewicz epstatus = udc_clearep_getsts(udc, EP_IN); 238990fccb52SAndrzej Pietrasiewicz 239090fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_USB_GADGET_DEBUG_FILES 239190fccb52SAndrzej Pietrasiewicz ep0->totalints++; 239290fccb52SAndrzej Pietrasiewicz #endif 239390fccb52SAndrzej Pietrasiewicz 239490fccb52SAndrzej Pietrasiewicz /* Stalled? Clear stall and reset buffers */ 239590fccb52SAndrzej Pietrasiewicz if (epstatus & EP_SEL_ST) { 239690fccb52SAndrzej Pietrasiewicz udc_clrstall_hwep(udc, EP_IN); 239790fccb52SAndrzej Pietrasiewicz nuke(ep0, -ECONNABORTED); 239890fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 239990fccb52SAndrzej Pietrasiewicz return; 240090fccb52SAndrzej Pietrasiewicz } 240190fccb52SAndrzej Pietrasiewicz 240290fccb52SAndrzej Pietrasiewicz /* Is a buffer available? */ 240390fccb52SAndrzej Pietrasiewicz if (!(epstatus & EP_SEL_F)) { 240490fccb52SAndrzej Pietrasiewicz /* Handle based on current state */ 240590fccb52SAndrzej Pietrasiewicz if (udc->ep0state == DATA_IN) 240690fccb52SAndrzej Pietrasiewicz udc_ep0_in_req(udc); 240790fccb52SAndrzej Pietrasiewicz else { 240890fccb52SAndrzej Pietrasiewicz /* Unknown state for EP0 oe end of DATA IN phase */ 240990fccb52SAndrzej Pietrasiewicz nuke(ep0, -ECONNABORTED); 241090fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 241190fccb52SAndrzej Pietrasiewicz } 241290fccb52SAndrzej Pietrasiewicz } 241390fccb52SAndrzej Pietrasiewicz } 241490fccb52SAndrzej Pietrasiewicz 241590fccb52SAndrzej Pietrasiewicz /* OUT endpoint 0 transfer */ 241690fccb52SAndrzej Pietrasiewicz static void udc_handle_ep0_out(struct lpc32xx_udc *udc) 241790fccb52SAndrzej Pietrasiewicz { 241890fccb52SAndrzej Pietrasiewicz struct lpc32xx_ep *ep0 = &udc->ep[0]; 241990fccb52SAndrzej Pietrasiewicz u32 epstatus; 242090fccb52SAndrzej Pietrasiewicz 242190fccb52SAndrzej Pietrasiewicz /* Clear EP interrupt */ 242290fccb52SAndrzej Pietrasiewicz epstatus = udc_clearep_getsts(udc, EP_OUT); 242390fccb52SAndrzej Pietrasiewicz 242490fccb52SAndrzej Pietrasiewicz 242590fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_USB_GADGET_DEBUG_FILES 242690fccb52SAndrzej Pietrasiewicz ep0->totalints++; 242790fccb52SAndrzej Pietrasiewicz #endif 242890fccb52SAndrzej Pietrasiewicz 242990fccb52SAndrzej Pietrasiewicz /* Stalled? */ 243090fccb52SAndrzej Pietrasiewicz if (epstatus & EP_SEL_ST) { 243190fccb52SAndrzej Pietrasiewicz udc_clrstall_hwep(udc, EP_OUT); 243290fccb52SAndrzej Pietrasiewicz nuke(ep0, -ECONNABORTED); 243390fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 243490fccb52SAndrzej Pietrasiewicz return; 243590fccb52SAndrzej Pietrasiewicz } 243690fccb52SAndrzej Pietrasiewicz 243790fccb52SAndrzej Pietrasiewicz /* A NAK may occur if a packet couldn't be received yet */ 243890fccb52SAndrzej Pietrasiewicz if (epstatus & EP_SEL_EPN) 243990fccb52SAndrzej Pietrasiewicz return; 244090fccb52SAndrzej Pietrasiewicz /* Setup packet incoming? */ 244190fccb52SAndrzej Pietrasiewicz if (epstatus & EP_SEL_STP) { 244290fccb52SAndrzej Pietrasiewicz nuke(ep0, 0); 244390fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 244490fccb52SAndrzej Pietrasiewicz } 244590fccb52SAndrzej Pietrasiewicz 244690fccb52SAndrzej Pietrasiewicz /* Data available? */ 244790fccb52SAndrzej Pietrasiewicz if (epstatus & EP_SEL_F) 244890fccb52SAndrzej Pietrasiewicz /* Handle based on current state */ 244990fccb52SAndrzej Pietrasiewicz switch (udc->ep0state) { 245090fccb52SAndrzej Pietrasiewicz case WAIT_FOR_SETUP: 245190fccb52SAndrzej Pietrasiewicz udc_handle_ep0_setup(udc); 245290fccb52SAndrzej Pietrasiewicz break; 245390fccb52SAndrzej Pietrasiewicz 245490fccb52SAndrzej Pietrasiewicz case DATA_OUT: 245590fccb52SAndrzej Pietrasiewicz udc_ep0_out_req(udc); 245690fccb52SAndrzej Pietrasiewicz break; 245790fccb52SAndrzej Pietrasiewicz 245890fccb52SAndrzej Pietrasiewicz default: 245990fccb52SAndrzej Pietrasiewicz /* Unknown state for EP0 */ 246090fccb52SAndrzej Pietrasiewicz nuke(ep0, -ECONNABORTED); 246190fccb52SAndrzej Pietrasiewicz udc->ep0state = WAIT_FOR_SETUP; 246290fccb52SAndrzej Pietrasiewicz } 246390fccb52SAndrzej Pietrasiewicz } 246490fccb52SAndrzej Pietrasiewicz 246590fccb52SAndrzej Pietrasiewicz /* Must be called without lock */ 246690fccb52SAndrzej Pietrasiewicz static int lpc32xx_get_frame(struct usb_gadget *gadget) 246790fccb52SAndrzej Pietrasiewicz { 246890fccb52SAndrzej Pietrasiewicz int frame; 246990fccb52SAndrzej Pietrasiewicz unsigned long flags; 247090fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = to_udc(gadget); 247190fccb52SAndrzej Pietrasiewicz 247290fccb52SAndrzej Pietrasiewicz if (!udc->clocked) 247390fccb52SAndrzej Pietrasiewicz return -EINVAL; 247490fccb52SAndrzej Pietrasiewicz 247590fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 247690fccb52SAndrzej Pietrasiewicz 247790fccb52SAndrzej Pietrasiewicz frame = (int) udc_get_current_frame(udc); 247890fccb52SAndrzej Pietrasiewicz 247990fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 248090fccb52SAndrzej Pietrasiewicz 248190fccb52SAndrzej Pietrasiewicz return frame; 248290fccb52SAndrzej Pietrasiewicz } 248390fccb52SAndrzej Pietrasiewicz 248490fccb52SAndrzej Pietrasiewicz static int lpc32xx_wakeup(struct usb_gadget *gadget) 248590fccb52SAndrzej Pietrasiewicz { 248690fccb52SAndrzej Pietrasiewicz return -ENOTSUPP; 248790fccb52SAndrzej Pietrasiewicz } 248890fccb52SAndrzej Pietrasiewicz 248990fccb52SAndrzej Pietrasiewicz static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on) 249090fccb52SAndrzej Pietrasiewicz { 2491d60d9392SPeter Chen gadget->is_selfpowered = (is_on != 0); 249290fccb52SAndrzej Pietrasiewicz 249390fccb52SAndrzej Pietrasiewicz return 0; 249490fccb52SAndrzej Pietrasiewicz } 249590fccb52SAndrzej Pietrasiewicz 249690fccb52SAndrzej Pietrasiewicz /* 249790fccb52SAndrzej Pietrasiewicz * vbus is here! turn everything on that's ready 249890fccb52SAndrzej Pietrasiewicz * Must be called without lock 249990fccb52SAndrzej Pietrasiewicz */ 250090fccb52SAndrzej Pietrasiewicz static int lpc32xx_vbus_session(struct usb_gadget *gadget, int is_active) 250190fccb52SAndrzej Pietrasiewicz { 250290fccb52SAndrzej Pietrasiewicz unsigned long flags; 250390fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = to_udc(gadget); 250490fccb52SAndrzej Pietrasiewicz 250590fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 250690fccb52SAndrzej Pietrasiewicz 250790fccb52SAndrzej Pietrasiewicz /* Doesn't need lock */ 250890fccb52SAndrzej Pietrasiewicz if (udc->driver) { 250990fccb52SAndrzej Pietrasiewicz udc_clk_set(udc, 1); 251090fccb52SAndrzej Pietrasiewicz udc_enable(udc); 251190fccb52SAndrzej Pietrasiewicz pullup(udc, is_active); 251290fccb52SAndrzej Pietrasiewicz } else { 251390fccb52SAndrzej Pietrasiewicz stop_activity(udc); 251490fccb52SAndrzej Pietrasiewicz pullup(udc, 0); 251590fccb52SAndrzej Pietrasiewicz 251690fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 251790fccb52SAndrzej Pietrasiewicz /* 251890fccb52SAndrzej Pietrasiewicz * Wait for all the endpoints to disable, 251990fccb52SAndrzej Pietrasiewicz * before disabling clocks. Don't wait if 252090fccb52SAndrzej Pietrasiewicz * endpoints are not enabled. 252190fccb52SAndrzej Pietrasiewicz */ 252290fccb52SAndrzej Pietrasiewicz if (atomic_read(&udc->enabled_ep_cnt)) 252390fccb52SAndrzej Pietrasiewicz wait_event_interruptible(udc->ep_disable_wait_queue, 252490fccb52SAndrzej Pietrasiewicz (atomic_read(&udc->enabled_ep_cnt) == 0)); 252590fccb52SAndrzej Pietrasiewicz 252690fccb52SAndrzej Pietrasiewicz spin_lock_irqsave(&udc->lock, flags); 252790fccb52SAndrzej Pietrasiewicz 252890fccb52SAndrzej Pietrasiewicz udc_clk_set(udc, 0); 252990fccb52SAndrzej Pietrasiewicz } 253090fccb52SAndrzej Pietrasiewicz 253190fccb52SAndrzej Pietrasiewicz spin_unlock_irqrestore(&udc->lock, flags); 253290fccb52SAndrzej Pietrasiewicz 253390fccb52SAndrzej Pietrasiewicz return 0; 253490fccb52SAndrzej Pietrasiewicz } 253590fccb52SAndrzej Pietrasiewicz 253690fccb52SAndrzej Pietrasiewicz /* Can be called with or without lock */ 253790fccb52SAndrzej Pietrasiewicz static int lpc32xx_pullup(struct usb_gadget *gadget, int is_on) 253890fccb52SAndrzej Pietrasiewicz { 253990fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = to_udc(gadget); 254090fccb52SAndrzej Pietrasiewicz 254190fccb52SAndrzej Pietrasiewicz /* Doesn't need lock */ 254290fccb52SAndrzej Pietrasiewicz pullup(udc, is_on); 254390fccb52SAndrzej Pietrasiewicz 254490fccb52SAndrzej Pietrasiewicz return 0; 254590fccb52SAndrzej Pietrasiewicz } 254690fccb52SAndrzej Pietrasiewicz 254790fccb52SAndrzej Pietrasiewicz static int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *); 254822835b80SFelipe Balbi static int lpc32xx_stop(struct usb_gadget *); 254990fccb52SAndrzej Pietrasiewicz 255090fccb52SAndrzej Pietrasiewicz static const struct usb_gadget_ops lpc32xx_udc_ops = { 255190fccb52SAndrzej Pietrasiewicz .get_frame = lpc32xx_get_frame, 255290fccb52SAndrzej Pietrasiewicz .wakeup = lpc32xx_wakeup, 255390fccb52SAndrzej Pietrasiewicz .set_selfpowered = lpc32xx_set_selfpowered, 255490fccb52SAndrzej Pietrasiewicz .vbus_session = lpc32xx_vbus_session, 255590fccb52SAndrzej Pietrasiewicz .pullup = lpc32xx_pullup, 255690fccb52SAndrzej Pietrasiewicz .udc_start = lpc32xx_start, 255790fccb52SAndrzej Pietrasiewicz .udc_stop = lpc32xx_stop, 255890fccb52SAndrzej Pietrasiewicz }; 255990fccb52SAndrzej Pietrasiewicz 256090fccb52SAndrzej Pietrasiewicz static void nop_release(struct device *dev) 256190fccb52SAndrzej Pietrasiewicz { 256290fccb52SAndrzej Pietrasiewicz /* nothing to free */ 256390fccb52SAndrzej Pietrasiewicz } 256490fccb52SAndrzej Pietrasiewicz 256590fccb52SAndrzej Pietrasiewicz static const struct lpc32xx_udc controller_template = { 256690fccb52SAndrzej Pietrasiewicz .gadget = { 256790fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_udc_ops, 256890fccb52SAndrzej Pietrasiewicz .name = driver_name, 256990fccb52SAndrzej Pietrasiewicz .dev = { 257090fccb52SAndrzej Pietrasiewicz .init_name = "gadget", 257190fccb52SAndrzej Pietrasiewicz .release = nop_release, 257290fccb52SAndrzej Pietrasiewicz } 257390fccb52SAndrzej Pietrasiewicz }, 257490fccb52SAndrzej Pietrasiewicz .ep[0] = { 257590fccb52SAndrzej Pietrasiewicz .ep = { 257690fccb52SAndrzej Pietrasiewicz .name = "ep0", 257790fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 25784d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_CONTROL, 25794d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 258090fccb52SAndrzej Pietrasiewicz }, 258190fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 258290fccb52SAndrzej Pietrasiewicz .hwep_num_base = 0, 258390fccb52SAndrzej Pietrasiewicz .hwep_num = 0, /* Can be 0 or 1, has special handling */ 258490fccb52SAndrzej Pietrasiewicz .lep = 0, 258590fccb52SAndrzej Pietrasiewicz .eptype = EP_CTL_TYPE, 258690fccb52SAndrzej Pietrasiewicz }, 258790fccb52SAndrzej Pietrasiewicz .ep[1] = { 258890fccb52SAndrzej Pietrasiewicz .ep = { 258990fccb52SAndrzej Pietrasiewicz .name = "ep1-int", 259090fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 25914d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 25924d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 259390fccb52SAndrzej Pietrasiewicz }, 259490fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 259590fccb52SAndrzej Pietrasiewicz .hwep_num_base = 2, 259690fccb52SAndrzej Pietrasiewicz .hwep_num = 0, /* 2 or 3, will be set later */ 259790fccb52SAndrzej Pietrasiewicz .lep = 1, 259890fccb52SAndrzej Pietrasiewicz .eptype = EP_INT_TYPE, 259990fccb52SAndrzej Pietrasiewicz }, 260090fccb52SAndrzej Pietrasiewicz .ep[2] = { 260190fccb52SAndrzej Pietrasiewicz .ep = { 260290fccb52SAndrzej Pietrasiewicz .name = "ep2-bulk", 260390fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26044d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 26054d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 260690fccb52SAndrzej Pietrasiewicz }, 260790fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 260890fccb52SAndrzej Pietrasiewicz .hwep_num_base = 4, 260990fccb52SAndrzej Pietrasiewicz .hwep_num = 0, /* 4 or 5, will be set later */ 261090fccb52SAndrzej Pietrasiewicz .lep = 2, 261190fccb52SAndrzej Pietrasiewicz .eptype = EP_BLK_TYPE, 261290fccb52SAndrzej Pietrasiewicz }, 261390fccb52SAndrzej Pietrasiewicz .ep[3] = { 261490fccb52SAndrzej Pietrasiewicz .ep = { 261590fccb52SAndrzej Pietrasiewicz .name = "ep3-iso", 261690fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26174d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 26184d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 261990fccb52SAndrzej Pietrasiewicz }, 262090fccb52SAndrzej Pietrasiewicz .maxpacket = 1023, 262190fccb52SAndrzej Pietrasiewicz .hwep_num_base = 6, 262290fccb52SAndrzej Pietrasiewicz .hwep_num = 0, /* 6 or 7, will be set later */ 262390fccb52SAndrzej Pietrasiewicz .lep = 3, 262490fccb52SAndrzej Pietrasiewicz .eptype = EP_ISO_TYPE, 262590fccb52SAndrzej Pietrasiewicz }, 262690fccb52SAndrzej Pietrasiewicz .ep[4] = { 262790fccb52SAndrzej Pietrasiewicz .ep = { 262890fccb52SAndrzej Pietrasiewicz .name = "ep4-int", 262990fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26304d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 26314d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 263290fccb52SAndrzej Pietrasiewicz }, 263390fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 263490fccb52SAndrzej Pietrasiewicz .hwep_num_base = 8, 263590fccb52SAndrzej Pietrasiewicz .hwep_num = 0, /* 8 or 9, will be set later */ 263690fccb52SAndrzej Pietrasiewicz .lep = 4, 263790fccb52SAndrzej Pietrasiewicz .eptype = EP_INT_TYPE, 263890fccb52SAndrzej Pietrasiewicz }, 263990fccb52SAndrzej Pietrasiewicz .ep[5] = { 264090fccb52SAndrzej Pietrasiewicz .ep = { 264190fccb52SAndrzej Pietrasiewicz .name = "ep5-bulk", 264290fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26434d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 26444d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 264590fccb52SAndrzej Pietrasiewicz }, 264690fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 264790fccb52SAndrzej Pietrasiewicz .hwep_num_base = 10, 264890fccb52SAndrzej Pietrasiewicz .hwep_num = 0, /* 10 or 11, will be set later */ 264990fccb52SAndrzej Pietrasiewicz .lep = 5, 265090fccb52SAndrzej Pietrasiewicz .eptype = EP_BLK_TYPE, 265190fccb52SAndrzej Pietrasiewicz }, 265290fccb52SAndrzej Pietrasiewicz .ep[6] = { 265390fccb52SAndrzej Pietrasiewicz .ep = { 265490fccb52SAndrzej Pietrasiewicz .name = "ep6-iso", 265590fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26564d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 26574d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 265890fccb52SAndrzej Pietrasiewicz }, 265990fccb52SAndrzej Pietrasiewicz .maxpacket = 1023, 266090fccb52SAndrzej Pietrasiewicz .hwep_num_base = 12, 266190fccb52SAndrzej Pietrasiewicz .hwep_num = 0, /* 12 or 13, will be set later */ 266290fccb52SAndrzej Pietrasiewicz .lep = 6, 266390fccb52SAndrzej Pietrasiewicz .eptype = EP_ISO_TYPE, 266490fccb52SAndrzej Pietrasiewicz }, 266590fccb52SAndrzej Pietrasiewicz .ep[7] = { 266690fccb52SAndrzej Pietrasiewicz .ep = { 266790fccb52SAndrzej Pietrasiewicz .name = "ep7-int", 266890fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26694d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 26704d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 267190fccb52SAndrzej Pietrasiewicz }, 267290fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 267390fccb52SAndrzej Pietrasiewicz .hwep_num_base = 14, 267490fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 267590fccb52SAndrzej Pietrasiewicz .lep = 7, 267690fccb52SAndrzej Pietrasiewicz .eptype = EP_INT_TYPE, 267790fccb52SAndrzej Pietrasiewicz }, 267890fccb52SAndrzej Pietrasiewicz .ep[8] = { 267990fccb52SAndrzej Pietrasiewicz .ep = { 268090fccb52SAndrzej Pietrasiewicz .name = "ep8-bulk", 268190fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26824d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 26834d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 268490fccb52SAndrzej Pietrasiewicz }, 268590fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 268690fccb52SAndrzej Pietrasiewicz .hwep_num_base = 16, 268790fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 268890fccb52SAndrzej Pietrasiewicz .lep = 8, 268990fccb52SAndrzej Pietrasiewicz .eptype = EP_BLK_TYPE, 269090fccb52SAndrzej Pietrasiewicz }, 269190fccb52SAndrzej Pietrasiewicz .ep[9] = { 269290fccb52SAndrzej Pietrasiewicz .ep = { 269390fccb52SAndrzej Pietrasiewicz .name = "ep9-iso", 269490fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 26954d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 26964d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 269790fccb52SAndrzej Pietrasiewicz }, 269890fccb52SAndrzej Pietrasiewicz .maxpacket = 1023, 269990fccb52SAndrzej Pietrasiewicz .hwep_num_base = 18, 270090fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 270190fccb52SAndrzej Pietrasiewicz .lep = 9, 270290fccb52SAndrzej Pietrasiewicz .eptype = EP_ISO_TYPE, 270390fccb52SAndrzej Pietrasiewicz }, 270490fccb52SAndrzej Pietrasiewicz .ep[10] = { 270590fccb52SAndrzej Pietrasiewicz .ep = { 270690fccb52SAndrzej Pietrasiewicz .name = "ep10-int", 270790fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 27084d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 27094d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 271090fccb52SAndrzej Pietrasiewicz }, 271190fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 271290fccb52SAndrzej Pietrasiewicz .hwep_num_base = 20, 271390fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 271490fccb52SAndrzej Pietrasiewicz .lep = 10, 271590fccb52SAndrzej Pietrasiewicz .eptype = EP_INT_TYPE, 271690fccb52SAndrzej Pietrasiewicz }, 271790fccb52SAndrzej Pietrasiewicz .ep[11] = { 271890fccb52SAndrzej Pietrasiewicz .ep = { 271990fccb52SAndrzej Pietrasiewicz .name = "ep11-bulk", 272090fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 27214d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 27224d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 272390fccb52SAndrzej Pietrasiewicz }, 272490fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 272590fccb52SAndrzej Pietrasiewicz .hwep_num_base = 22, 272690fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 272790fccb52SAndrzej Pietrasiewicz .lep = 11, 272890fccb52SAndrzej Pietrasiewicz .eptype = EP_BLK_TYPE, 272990fccb52SAndrzej Pietrasiewicz }, 273090fccb52SAndrzej Pietrasiewicz .ep[12] = { 273190fccb52SAndrzej Pietrasiewicz .ep = { 273290fccb52SAndrzej Pietrasiewicz .name = "ep12-iso", 273390fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 27344d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_ISO, 27354d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 273690fccb52SAndrzej Pietrasiewicz }, 273790fccb52SAndrzej Pietrasiewicz .maxpacket = 1023, 273890fccb52SAndrzej Pietrasiewicz .hwep_num_base = 24, 273990fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 274090fccb52SAndrzej Pietrasiewicz .lep = 12, 274190fccb52SAndrzej Pietrasiewicz .eptype = EP_ISO_TYPE, 274290fccb52SAndrzej Pietrasiewicz }, 274390fccb52SAndrzej Pietrasiewicz .ep[13] = { 274490fccb52SAndrzej Pietrasiewicz .ep = { 274590fccb52SAndrzej Pietrasiewicz .name = "ep13-int", 274690fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 27474d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_INT, 27484d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 274990fccb52SAndrzej Pietrasiewicz }, 275090fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 275190fccb52SAndrzej Pietrasiewicz .hwep_num_base = 26, 275290fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 275390fccb52SAndrzej Pietrasiewicz .lep = 13, 275490fccb52SAndrzej Pietrasiewicz .eptype = EP_INT_TYPE, 275590fccb52SAndrzej Pietrasiewicz }, 275690fccb52SAndrzej Pietrasiewicz .ep[14] = { 275790fccb52SAndrzej Pietrasiewicz .ep = { 275890fccb52SAndrzej Pietrasiewicz .name = "ep14-bulk", 275990fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 27604d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 27614d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 276290fccb52SAndrzej Pietrasiewicz }, 276390fccb52SAndrzej Pietrasiewicz .maxpacket = 64, 276490fccb52SAndrzej Pietrasiewicz .hwep_num_base = 28, 276590fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 276690fccb52SAndrzej Pietrasiewicz .lep = 14, 276790fccb52SAndrzej Pietrasiewicz .eptype = EP_BLK_TYPE, 276890fccb52SAndrzej Pietrasiewicz }, 276990fccb52SAndrzej Pietrasiewicz .ep[15] = { 277090fccb52SAndrzej Pietrasiewicz .ep = { 277190fccb52SAndrzej Pietrasiewicz .name = "ep15-bulk", 277290fccb52SAndrzej Pietrasiewicz .ops = &lpc32xx_ep_ops, 27734d75c8bdSRobert Baldyga .caps = USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, 27744d75c8bdSRobert Baldyga USB_EP_CAPS_DIR_ALL), 277590fccb52SAndrzej Pietrasiewicz }, 277690fccb52SAndrzej Pietrasiewicz .maxpacket = 1023, 277790fccb52SAndrzej Pietrasiewicz .hwep_num_base = 30, 277890fccb52SAndrzej Pietrasiewicz .hwep_num = 0, 277990fccb52SAndrzej Pietrasiewicz .lep = 15, 278090fccb52SAndrzej Pietrasiewicz .eptype = EP_BLK_TYPE, 278190fccb52SAndrzej Pietrasiewicz }, 278290fccb52SAndrzej Pietrasiewicz }; 278390fccb52SAndrzej Pietrasiewicz 278490fccb52SAndrzej Pietrasiewicz /* ISO and status interrupts */ 278590fccb52SAndrzej Pietrasiewicz static irqreturn_t lpc32xx_usb_lp_irq(int irq, void *_udc) 278690fccb52SAndrzej Pietrasiewicz { 278790fccb52SAndrzej Pietrasiewicz u32 tmp, devstat; 278890fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = _udc; 278990fccb52SAndrzej Pietrasiewicz 279090fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 279190fccb52SAndrzej Pietrasiewicz 279290fccb52SAndrzej Pietrasiewicz /* Read the device status register */ 279390fccb52SAndrzej Pietrasiewicz devstat = readl(USBD_DEVINTST(udc->udp_baseaddr)); 279490fccb52SAndrzej Pietrasiewicz 279590fccb52SAndrzej Pietrasiewicz devstat &= ~USBD_EP_FAST; 279690fccb52SAndrzej Pietrasiewicz writel(devstat, USBD_DEVINTCLR(udc->udp_baseaddr)); 279790fccb52SAndrzej Pietrasiewicz devstat = devstat & udc->enabled_devints; 279890fccb52SAndrzej Pietrasiewicz 279990fccb52SAndrzej Pietrasiewicz /* Device specific handling needed? */ 280090fccb52SAndrzej Pietrasiewicz if (devstat & USBD_DEV_STAT) 280190fccb52SAndrzej Pietrasiewicz udc_handle_dev(udc); 280290fccb52SAndrzej Pietrasiewicz 280390fccb52SAndrzej Pietrasiewicz /* Start of frame? (devstat & FRAME_INT): 280490fccb52SAndrzej Pietrasiewicz * The frame interrupt isn't really needed for ISO support, 280590fccb52SAndrzej Pietrasiewicz * as the driver will queue the necessary packets */ 280690fccb52SAndrzej Pietrasiewicz 280790fccb52SAndrzej Pietrasiewicz /* Error? */ 280890fccb52SAndrzej Pietrasiewicz if (devstat & ERR_INT) { 280990fccb52SAndrzej Pietrasiewicz /* All types of errors, from cable removal during transfer to 281090fccb52SAndrzej Pietrasiewicz * misc protocol and bit errors. These are mostly for just info, 281190fccb52SAndrzej Pietrasiewicz * as the USB hardware will work around these. If these errors 281290fccb52SAndrzej Pietrasiewicz * happen alot, something is wrong. */ 281390fccb52SAndrzej Pietrasiewicz udc_protocol_cmd_w(udc, CMD_RD_ERR_STAT); 281490fccb52SAndrzej Pietrasiewicz tmp = udc_protocol_cmd_r(udc, DAT_RD_ERR_STAT); 281590fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "Device error (0x%x)!\n", tmp); 281690fccb52SAndrzej Pietrasiewicz } 281790fccb52SAndrzej Pietrasiewicz 281890fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 281990fccb52SAndrzej Pietrasiewicz 282090fccb52SAndrzej Pietrasiewicz return IRQ_HANDLED; 282190fccb52SAndrzej Pietrasiewicz } 282290fccb52SAndrzej Pietrasiewicz 282390fccb52SAndrzej Pietrasiewicz /* EP interrupts */ 282490fccb52SAndrzej Pietrasiewicz static irqreturn_t lpc32xx_usb_hp_irq(int irq, void *_udc) 282590fccb52SAndrzej Pietrasiewicz { 282690fccb52SAndrzej Pietrasiewicz u32 tmp; 282790fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = _udc; 282890fccb52SAndrzej Pietrasiewicz 282990fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 283090fccb52SAndrzej Pietrasiewicz 283190fccb52SAndrzej Pietrasiewicz /* Read the device status register */ 283290fccb52SAndrzej Pietrasiewicz writel(USBD_EP_FAST, USBD_DEVINTCLR(udc->udp_baseaddr)); 283390fccb52SAndrzej Pietrasiewicz 283490fccb52SAndrzej Pietrasiewicz /* Endpoints */ 283590fccb52SAndrzej Pietrasiewicz tmp = readl(USBD_EPINTST(udc->udp_baseaddr)); 283690fccb52SAndrzej Pietrasiewicz 283790fccb52SAndrzej Pietrasiewicz /* Special handling for EP0 */ 283890fccb52SAndrzej Pietrasiewicz if (tmp & (EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) { 283990fccb52SAndrzej Pietrasiewicz /* Handle EP0 IN */ 284090fccb52SAndrzej Pietrasiewicz if (tmp & (EP_MASK_SEL(0, EP_IN))) 284190fccb52SAndrzej Pietrasiewicz udc_handle_ep0_in(udc); 284290fccb52SAndrzej Pietrasiewicz 284390fccb52SAndrzej Pietrasiewicz /* Handle EP0 OUT */ 284490fccb52SAndrzej Pietrasiewicz if (tmp & (EP_MASK_SEL(0, EP_OUT))) 284590fccb52SAndrzej Pietrasiewicz udc_handle_ep0_out(udc); 284690fccb52SAndrzej Pietrasiewicz } 284790fccb52SAndrzej Pietrasiewicz 284890fccb52SAndrzej Pietrasiewicz /* All other EPs */ 284990fccb52SAndrzej Pietrasiewicz if (tmp & ~(EP_MASK_SEL(0, EP_OUT) | EP_MASK_SEL(0, EP_IN))) { 285090fccb52SAndrzej Pietrasiewicz int i; 285190fccb52SAndrzej Pietrasiewicz 285290fccb52SAndrzej Pietrasiewicz /* Handle other EP interrupts */ 285390fccb52SAndrzej Pietrasiewicz for (i = 1; i < NUM_ENDPOINTS; i++) { 285490fccb52SAndrzej Pietrasiewicz if (tmp & (1 << udc->ep[i].hwep_num)) 285590fccb52SAndrzej Pietrasiewicz udc_handle_eps(udc, &udc->ep[i]); 285690fccb52SAndrzej Pietrasiewicz } 285790fccb52SAndrzej Pietrasiewicz } 285890fccb52SAndrzej Pietrasiewicz 285990fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 286090fccb52SAndrzej Pietrasiewicz 286190fccb52SAndrzej Pietrasiewicz return IRQ_HANDLED; 286290fccb52SAndrzej Pietrasiewicz } 286390fccb52SAndrzej Pietrasiewicz 286490fccb52SAndrzej Pietrasiewicz static irqreturn_t lpc32xx_usb_devdma_irq(int irq, void *_udc) 286590fccb52SAndrzej Pietrasiewicz { 286690fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = _udc; 286790fccb52SAndrzej Pietrasiewicz 286890fccb52SAndrzej Pietrasiewicz int i; 286990fccb52SAndrzej Pietrasiewicz u32 tmp; 287090fccb52SAndrzej Pietrasiewicz 287190fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 287290fccb52SAndrzej Pietrasiewicz 287390fccb52SAndrzej Pietrasiewicz /* Handle EP DMA EOT interrupts */ 287490fccb52SAndrzej Pietrasiewicz tmp = readl(USBD_EOTINTST(udc->udp_baseaddr)) | 287590fccb52SAndrzej Pietrasiewicz (readl(USBD_EPDMAST(udc->udp_baseaddr)) & 287690fccb52SAndrzej Pietrasiewicz readl(USBD_NDDRTINTST(udc->udp_baseaddr))) | 287790fccb52SAndrzej Pietrasiewicz readl(USBD_SYSERRTINTST(udc->udp_baseaddr)); 287890fccb52SAndrzej Pietrasiewicz for (i = 1; i < NUM_ENDPOINTS; i++) { 287990fccb52SAndrzej Pietrasiewicz if (tmp & (1 << udc->ep[i].hwep_num)) 288090fccb52SAndrzej Pietrasiewicz udc_handle_dma_ep(udc, &udc->ep[i]); 288190fccb52SAndrzej Pietrasiewicz } 288290fccb52SAndrzej Pietrasiewicz 288390fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 288490fccb52SAndrzej Pietrasiewicz 288590fccb52SAndrzej Pietrasiewicz return IRQ_HANDLED; 288690fccb52SAndrzej Pietrasiewicz } 288790fccb52SAndrzej Pietrasiewicz 288890fccb52SAndrzej Pietrasiewicz /* 288990fccb52SAndrzej Pietrasiewicz * 289090fccb52SAndrzej Pietrasiewicz * VBUS detection, pullup handler, and Gadget cable state notification 289190fccb52SAndrzej Pietrasiewicz * 289290fccb52SAndrzej Pietrasiewicz */ 289390fccb52SAndrzej Pietrasiewicz static void vbus_work(struct work_struct *work) 289490fccb52SAndrzej Pietrasiewicz { 289590fccb52SAndrzej Pietrasiewicz u8 value; 289690fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = container_of(work, struct lpc32xx_udc, 289790fccb52SAndrzej Pietrasiewicz vbus_job); 289890fccb52SAndrzej Pietrasiewicz 289990fccb52SAndrzej Pietrasiewicz if (udc->enabled != 0) { 290090fccb52SAndrzej Pietrasiewicz /* Discharge VBUS real quick */ 290190fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 290290fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1, OTG1_VBUS_DISCHRG); 290390fccb52SAndrzej Pietrasiewicz 290490fccb52SAndrzej Pietrasiewicz /* Give VBUS some time (100mS) to discharge */ 290590fccb52SAndrzej Pietrasiewicz msleep(100); 290690fccb52SAndrzej Pietrasiewicz 290790fccb52SAndrzej Pietrasiewicz /* Disable VBUS discharge resistor */ 290890fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 290990fccb52SAndrzej Pietrasiewicz ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR, 291090fccb52SAndrzej Pietrasiewicz OTG1_VBUS_DISCHRG); 291190fccb52SAndrzej Pietrasiewicz 291290fccb52SAndrzej Pietrasiewicz /* Clear interrupt */ 291390fccb52SAndrzej Pietrasiewicz i2c_smbus_write_byte_data(udc->isp1301_i2c_client, 291490fccb52SAndrzej Pietrasiewicz ISP1301_I2C_INTERRUPT_LATCH | 291590fccb52SAndrzej Pietrasiewicz ISP1301_I2C_REG_CLEAR_ADDR, ~0); 291690fccb52SAndrzej Pietrasiewicz 291790fccb52SAndrzej Pietrasiewicz /* Get the VBUS status from the transceiver */ 291890fccb52SAndrzej Pietrasiewicz value = i2c_smbus_read_byte_data(udc->isp1301_i2c_client, 291990fccb52SAndrzej Pietrasiewicz ISP1301_I2C_INTERRUPT_SOURCE); 292090fccb52SAndrzej Pietrasiewicz 292190fccb52SAndrzej Pietrasiewicz /* VBUS on or off? */ 292290fccb52SAndrzej Pietrasiewicz if (value & INT_SESS_VLD) 292390fccb52SAndrzej Pietrasiewicz udc->vbus = 1; 292490fccb52SAndrzej Pietrasiewicz else 292590fccb52SAndrzej Pietrasiewicz udc->vbus = 0; 292690fccb52SAndrzej Pietrasiewicz 292790fccb52SAndrzej Pietrasiewicz /* VBUS changed? */ 292890fccb52SAndrzej Pietrasiewicz if (udc->last_vbus != udc->vbus) { 292990fccb52SAndrzej Pietrasiewicz udc->last_vbus = udc->vbus; 293090fccb52SAndrzej Pietrasiewicz lpc32xx_vbus_session(&udc->gadget, udc->vbus); 293190fccb52SAndrzej Pietrasiewicz } 293290fccb52SAndrzej Pietrasiewicz } 293390fccb52SAndrzej Pietrasiewicz 293490fccb52SAndrzej Pietrasiewicz /* Re-enable after completion */ 293590fccb52SAndrzej Pietrasiewicz enable_irq(udc->udp_irq[IRQ_USB_ATX]); 293690fccb52SAndrzej Pietrasiewicz } 293790fccb52SAndrzej Pietrasiewicz 293890fccb52SAndrzej Pietrasiewicz static irqreturn_t lpc32xx_usb_vbus_irq(int irq, void *_udc) 293990fccb52SAndrzej Pietrasiewicz { 294090fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = _udc; 294190fccb52SAndrzej Pietrasiewicz 294290fccb52SAndrzej Pietrasiewicz /* Defer handling of VBUS IRQ to work queue */ 294390fccb52SAndrzej Pietrasiewicz disable_irq_nosync(udc->udp_irq[IRQ_USB_ATX]); 294490fccb52SAndrzej Pietrasiewicz schedule_work(&udc->vbus_job); 294590fccb52SAndrzej Pietrasiewicz 294690fccb52SAndrzej Pietrasiewicz return IRQ_HANDLED; 294790fccb52SAndrzej Pietrasiewicz } 294890fccb52SAndrzej Pietrasiewicz 294990fccb52SAndrzej Pietrasiewicz static int lpc32xx_start(struct usb_gadget *gadget, 295090fccb52SAndrzej Pietrasiewicz struct usb_gadget_driver *driver) 295190fccb52SAndrzej Pietrasiewicz { 295290fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = to_udc(gadget); 295390fccb52SAndrzej Pietrasiewicz int i; 295490fccb52SAndrzej Pietrasiewicz 295590fccb52SAndrzej Pietrasiewicz if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) { 295690fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "bad parameter.\n"); 295790fccb52SAndrzej Pietrasiewicz return -EINVAL; 295890fccb52SAndrzej Pietrasiewicz } 295990fccb52SAndrzej Pietrasiewicz 296090fccb52SAndrzej Pietrasiewicz if (udc->driver) { 296190fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "UDC already has a gadget driver\n"); 296290fccb52SAndrzej Pietrasiewicz return -EBUSY; 296390fccb52SAndrzej Pietrasiewicz } 296490fccb52SAndrzej Pietrasiewicz 296590fccb52SAndrzej Pietrasiewicz udc->driver = driver; 296690fccb52SAndrzej Pietrasiewicz udc->gadget.dev.of_node = udc->dev->of_node; 296790fccb52SAndrzej Pietrasiewicz udc->enabled = 1; 2968d60d9392SPeter Chen udc->gadget.is_selfpowered = 1; 296990fccb52SAndrzej Pietrasiewicz udc->vbus = 0; 297090fccb52SAndrzej Pietrasiewicz 297190fccb52SAndrzej Pietrasiewicz /* Force VBUS process once to check for cable insertion */ 297290fccb52SAndrzej Pietrasiewicz udc->last_vbus = udc->vbus = 0; 297390fccb52SAndrzej Pietrasiewicz schedule_work(&udc->vbus_job); 297490fccb52SAndrzej Pietrasiewicz 297590fccb52SAndrzej Pietrasiewicz /* Do not re-enable ATX IRQ (3) */ 297690fccb52SAndrzej Pietrasiewicz for (i = IRQ_USB_LP; i < IRQ_USB_ATX; i++) 297790fccb52SAndrzej Pietrasiewicz enable_irq(udc->udp_irq[i]); 297890fccb52SAndrzej Pietrasiewicz 297990fccb52SAndrzej Pietrasiewicz return 0; 298090fccb52SAndrzej Pietrasiewicz } 298190fccb52SAndrzej Pietrasiewicz 298222835b80SFelipe Balbi static int lpc32xx_stop(struct usb_gadget *gadget) 298390fccb52SAndrzej Pietrasiewicz { 298490fccb52SAndrzej Pietrasiewicz int i; 298590fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = to_udc(gadget); 298690fccb52SAndrzej Pietrasiewicz 298790fccb52SAndrzej Pietrasiewicz for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++) 298890fccb52SAndrzej Pietrasiewicz disable_irq(udc->udp_irq[i]); 298990fccb52SAndrzej Pietrasiewicz 299090fccb52SAndrzej Pietrasiewicz if (udc->clocked) { 299190fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 299290fccb52SAndrzej Pietrasiewicz stop_activity(udc); 299390fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 299490fccb52SAndrzej Pietrasiewicz 299590fccb52SAndrzej Pietrasiewicz /* 299690fccb52SAndrzej Pietrasiewicz * Wait for all the endpoints to disable, 299790fccb52SAndrzej Pietrasiewicz * before disabling clocks. Don't wait if 299890fccb52SAndrzej Pietrasiewicz * endpoints are not enabled. 299990fccb52SAndrzej Pietrasiewicz */ 300090fccb52SAndrzej Pietrasiewicz if (atomic_read(&udc->enabled_ep_cnt)) 300190fccb52SAndrzej Pietrasiewicz wait_event_interruptible(udc->ep_disable_wait_queue, 300290fccb52SAndrzej Pietrasiewicz (atomic_read(&udc->enabled_ep_cnt) == 0)); 300390fccb52SAndrzej Pietrasiewicz 300490fccb52SAndrzej Pietrasiewicz spin_lock(&udc->lock); 300590fccb52SAndrzej Pietrasiewicz udc_clk_set(udc, 0); 300690fccb52SAndrzej Pietrasiewicz spin_unlock(&udc->lock); 300790fccb52SAndrzej Pietrasiewicz } 300890fccb52SAndrzej Pietrasiewicz 300990fccb52SAndrzej Pietrasiewicz udc->enabled = 0; 301090fccb52SAndrzej Pietrasiewicz udc->driver = NULL; 301190fccb52SAndrzej Pietrasiewicz 301290fccb52SAndrzej Pietrasiewicz return 0; 301390fccb52SAndrzej Pietrasiewicz } 301490fccb52SAndrzej Pietrasiewicz 301590fccb52SAndrzej Pietrasiewicz static void lpc32xx_udc_shutdown(struct platform_device *dev) 301690fccb52SAndrzej Pietrasiewicz { 301790fccb52SAndrzej Pietrasiewicz /* Force disconnect on reboot */ 301890fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = platform_get_drvdata(dev); 301990fccb52SAndrzej Pietrasiewicz 302090fccb52SAndrzej Pietrasiewicz pullup(udc, 0); 302190fccb52SAndrzej Pietrasiewicz } 302290fccb52SAndrzej Pietrasiewicz 302390fccb52SAndrzej Pietrasiewicz /* 302490fccb52SAndrzej Pietrasiewicz * Callbacks to be overridden by options passed via OF (TODO) 302590fccb52SAndrzej Pietrasiewicz */ 302690fccb52SAndrzej Pietrasiewicz 302790fccb52SAndrzej Pietrasiewicz static void lpc32xx_usbd_conn_chg(int conn) 302890fccb52SAndrzej Pietrasiewicz { 302990fccb52SAndrzej Pietrasiewicz /* Do nothing, it might be nice to enable an LED 303090fccb52SAndrzej Pietrasiewicz * based on conn state being !0 */ 303190fccb52SAndrzej Pietrasiewicz } 303290fccb52SAndrzej Pietrasiewicz 303390fccb52SAndrzej Pietrasiewicz static void lpc32xx_usbd_susp_chg(int susp) 303490fccb52SAndrzej Pietrasiewicz { 303590fccb52SAndrzej Pietrasiewicz /* Device suspend if susp != 0 */ 303690fccb52SAndrzej Pietrasiewicz } 303790fccb52SAndrzej Pietrasiewicz 303890fccb52SAndrzej Pietrasiewicz static void lpc32xx_rmwkup_chg(int remote_wakup_enable) 303990fccb52SAndrzej Pietrasiewicz { 304090fccb52SAndrzej Pietrasiewicz /* Enable or disable USB remote wakeup */ 304190fccb52SAndrzej Pietrasiewicz } 304290fccb52SAndrzej Pietrasiewicz 304390fccb52SAndrzej Pietrasiewicz struct lpc32xx_usbd_cfg lpc32xx_usbddata = { 304490fccb52SAndrzej Pietrasiewicz .vbus_drv_pol = 0, 304590fccb52SAndrzej Pietrasiewicz .conn_chgb = &lpc32xx_usbd_conn_chg, 304690fccb52SAndrzej Pietrasiewicz .susp_chgb = &lpc32xx_usbd_susp_chg, 304790fccb52SAndrzej Pietrasiewicz .rmwk_chgb = &lpc32xx_rmwkup_chg, 304890fccb52SAndrzej Pietrasiewicz }; 304990fccb52SAndrzej Pietrasiewicz 305090fccb52SAndrzej Pietrasiewicz 305190fccb52SAndrzej Pietrasiewicz static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F; 305290fccb52SAndrzej Pietrasiewicz 305390fccb52SAndrzej Pietrasiewicz static int lpc32xx_udc_probe(struct platform_device *pdev) 305490fccb52SAndrzej Pietrasiewicz { 305590fccb52SAndrzej Pietrasiewicz struct device *dev = &pdev->dev; 305690fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc; 305790fccb52SAndrzej Pietrasiewicz int retval, i; 305890fccb52SAndrzej Pietrasiewicz struct resource *res; 305990fccb52SAndrzej Pietrasiewicz dma_addr_t dma_handle; 306090fccb52SAndrzej Pietrasiewicz struct device_node *isp1301_node; 306190fccb52SAndrzej Pietrasiewicz 306290fccb52SAndrzej Pietrasiewicz udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL); 306390fccb52SAndrzej Pietrasiewicz if (!udc) 306490fccb52SAndrzej Pietrasiewicz return -ENOMEM; 306590fccb52SAndrzej Pietrasiewicz 306690fccb52SAndrzej Pietrasiewicz for (i = 0; i <= 15; i++) 306790fccb52SAndrzej Pietrasiewicz udc->ep[i].udc = udc; 306890fccb52SAndrzej Pietrasiewicz udc->gadget.ep0 = &udc->ep[0].ep; 306990fccb52SAndrzej Pietrasiewicz 307090fccb52SAndrzej Pietrasiewicz /* init software state */ 307190fccb52SAndrzej Pietrasiewicz udc->gadget.dev.parent = dev; 307290fccb52SAndrzej Pietrasiewicz udc->pdev = pdev; 307390fccb52SAndrzej Pietrasiewicz udc->dev = &pdev->dev; 307490fccb52SAndrzej Pietrasiewicz udc->enabled = 0; 307590fccb52SAndrzej Pietrasiewicz 307690fccb52SAndrzej Pietrasiewicz if (pdev->dev.of_node) { 307790fccb52SAndrzej Pietrasiewicz isp1301_node = of_parse_phandle(pdev->dev.of_node, 307890fccb52SAndrzej Pietrasiewicz "transceiver", 0); 307990fccb52SAndrzej Pietrasiewicz } else { 308090fccb52SAndrzej Pietrasiewicz isp1301_node = NULL; 308190fccb52SAndrzej Pietrasiewicz } 308290fccb52SAndrzej Pietrasiewicz 308390fccb52SAndrzej Pietrasiewicz udc->isp1301_i2c_client = isp1301_get_client(isp1301_node); 308490fccb52SAndrzej Pietrasiewicz if (!udc->isp1301_i2c_client) { 308590fccb52SAndrzej Pietrasiewicz retval = -EPROBE_DEFER; 308690fccb52SAndrzej Pietrasiewicz goto phy_fail; 308790fccb52SAndrzej Pietrasiewicz } 308890fccb52SAndrzej Pietrasiewicz 308990fccb52SAndrzej Pietrasiewicz dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n", 309090fccb52SAndrzej Pietrasiewicz udc->isp1301_i2c_client->addr); 309190fccb52SAndrzej Pietrasiewicz 309290fccb52SAndrzej Pietrasiewicz pdev->dev.dma_mask = &lpc32xx_usbd_dmamask; 309390fccb52SAndrzej Pietrasiewicz retval = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); 309490fccb52SAndrzej Pietrasiewicz if (retval) 309590fccb52SAndrzej Pietrasiewicz goto resource_fail; 309690fccb52SAndrzej Pietrasiewicz 309790fccb52SAndrzej Pietrasiewicz udc->board = &lpc32xx_usbddata; 309890fccb52SAndrzej Pietrasiewicz 309990fccb52SAndrzej Pietrasiewicz /* 310090fccb52SAndrzej Pietrasiewicz * Resources are mapped as follows: 310190fccb52SAndrzej Pietrasiewicz * IORESOURCE_MEM, base address and size of USB space 310290fccb52SAndrzej Pietrasiewicz * IORESOURCE_IRQ, USB device low priority interrupt number 310390fccb52SAndrzej Pietrasiewicz * IORESOURCE_IRQ, USB device high priority interrupt number 310490fccb52SAndrzej Pietrasiewicz * IORESOURCE_IRQ, USB device interrupt number 310590fccb52SAndrzej Pietrasiewicz * IORESOURCE_IRQ, USB transceiver interrupt number 310690fccb52SAndrzej Pietrasiewicz */ 310790fccb52SAndrzej Pietrasiewicz res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 310890fccb52SAndrzej Pietrasiewicz if (!res) { 310990fccb52SAndrzej Pietrasiewicz retval = -ENXIO; 311090fccb52SAndrzej Pietrasiewicz goto resource_fail; 311190fccb52SAndrzej Pietrasiewicz } 311290fccb52SAndrzej Pietrasiewicz 311390fccb52SAndrzej Pietrasiewicz spin_lock_init(&udc->lock); 311490fccb52SAndrzej Pietrasiewicz 311590fccb52SAndrzej Pietrasiewicz /* Get IRQs */ 311690fccb52SAndrzej Pietrasiewicz for (i = 0; i < 4; i++) { 311790fccb52SAndrzej Pietrasiewicz udc->udp_irq[i] = platform_get_irq(pdev, i); 311890fccb52SAndrzej Pietrasiewicz if (udc->udp_irq[i] < 0) { 311990fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, 312090fccb52SAndrzej Pietrasiewicz "irq resource %d not available!\n", i); 312190fccb52SAndrzej Pietrasiewicz retval = udc->udp_irq[i]; 312290fccb52SAndrzej Pietrasiewicz goto irq_fail; 312390fccb52SAndrzej Pietrasiewicz } 312490fccb52SAndrzej Pietrasiewicz } 312590fccb52SAndrzej Pietrasiewicz 312690fccb52SAndrzej Pietrasiewicz udc->io_p_start = res->start; 312790fccb52SAndrzej Pietrasiewicz udc->io_p_size = resource_size(res); 312890fccb52SAndrzej Pietrasiewicz if (!request_mem_region(udc->io_p_start, udc->io_p_size, driver_name)) { 312990fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "someone's using UDC memory\n"); 313090fccb52SAndrzej Pietrasiewicz retval = -EBUSY; 313190fccb52SAndrzej Pietrasiewicz goto request_mem_region_fail; 313290fccb52SAndrzej Pietrasiewicz } 313390fccb52SAndrzej Pietrasiewicz 313490fccb52SAndrzej Pietrasiewicz udc->udp_baseaddr = ioremap(udc->io_p_start, udc->io_p_size); 313590fccb52SAndrzej Pietrasiewicz if (!udc->udp_baseaddr) { 313690fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 313790fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "IO map failure\n"); 313890fccb52SAndrzej Pietrasiewicz goto io_map_fail; 313990fccb52SAndrzej Pietrasiewicz } 314090fccb52SAndrzej Pietrasiewicz 314190fccb52SAndrzej Pietrasiewicz /* Enable AHB slave USB clock, needed for further USB clock control */ 314290fccb52SAndrzej Pietrasiewicz writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL); 314390fccb52SAndrzej Pietrasiewicz 314490fccb52SAndrzej Pietrasiewicz /* Get required clocks */ 314590fccb52SAndrzej Pietrasiewicz udc->usb_pll_clk = clk_get(&pdev->dev, "ck_pll5"); 314690fccb52SAndrzej Pietrasiewicz if (IS_ERR(udc->usb_pll_clk)) { 314790fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "failed to acquire USB PLL\n"); 314890fccb52SAndrzej Pietrasiewicz retval = PTR_ERR(udc->usb_pll_clk); 314990fccb52SAndrzej Pietrasiewicz goto pll_get_fail; 315090fccb52SAndrzej Pietrasiewicz } 315190fccb52SAndrzej Pietrasiewicz udc->usb_slv_clk = clk_get(&pdev->dev, "ck_usbd"); 315290fccb52SAndrzej Pietrasiewicz if (IS_ERR(udc->usb_slv_clk)) { 315390fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "failed to acquire USB device clock\n"); 315490fccb52SAndrzej Pietrasiewicz retval = PTR_ERR(udc->usb_slv_clk); 315590fccb52SAndrzej Pietrasiewicz goto usb_clk_get_fail; 315690fccb52SAndrzej Pietrasiewicz } 315790fccb52SAndrzej Pietrasiewicz udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg"); 315890fccb52SAndrzej Pietrasiewicz if (IS_ERR(udc->usb_otg_clk)) { 315990fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "failed to acquire USB otg clock\n"); 316090fccb52SAndrzej Pietrasiewicz retval = PTR_ERR(udc->usb_otg_clk); 316190fccb52SAndrzej Pietrasiewicz goto usb_otg_clk_get_fail; 316290fccb52SAndrzej Pietrasiewicz } 316390fccb52SAndrzej Pietrasiewicz 316490fccb52SAndrzej Pietrasiewicz /* Setup PLL clock to 48MHz */ 316590fccb52SAndrzej Pietrasiewicz retval = clk_enable(udc->usb_pll_clk); 316690fccb52SAndrzej Pietrasiewicz if (retval < 0) { 316790fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "failed to start USB PLL\n"); 316890fccb52SAndrzej Pietrasiewicz goto pll_enable_fail; 316990fccb52SAndrzej Pietrasiewicz } 317090fccb52SAndrzej Pietrasiewicz 317190fccb52SAndrzej Pietrasiewicz retval = clk_set_rate(udc->usb_pll_clk, 48000); 317290fccb52SAndrzej Pietrasiewicz if (retval < 0) { 317390fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "failed to set USB clock rate\n"); 317490fccb52SAndrzej Pietrasiewicz goto pll_set_fail; 317590fccb52SAndrzej Pietrasiewicz } 317690fccb52SAndrzej Pietrasiewicz 317790fccb52SAndrzej Pietrasiewicz writel(readl(USB_CTRL) | USB_DEV_NEED_CLK_EN, USB_CTRL); 317890fccb52SAndrzej Pietrasiewicz 317990fccb52SAndrzej Pietrasiewicz /* Enable USB device clock */ 318090fccb52SAndrzej Pietrasiewicz retval = clk_enable(udc->usb_slv_clk); 318190fccb52SAndrzej Pietrasiewicz if (retval < 0) { 318290fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "failed to start USB device clock\n"); 318390fccb52SAndrzej Pietrasiewicz goto usb_clk_enable_fail; 318490fccb52SAndrzej Pietrasiewicz } 318590fccb52SAndrzej Pietrasiewicz 318690fccb52SAndrzej Pietrasiewicz /* Enable USB OTG clock */ 318790fccb52SAndrzej Pietrasiewicz retval = clk_enable(udc->usb_otg_clk); 318890fccb52SAndrzej Pietrasiewicz if (retval < 0) { 318990fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "failed to start USB otg clock\n"); 319090fccb52SAndrzej Pietrasiewicz goto usb_otg_clk_enable_fail; 319190fccb52SAndrzej Pietrasiewicz } 319290fccb52SAndrzej Pietrasiewicz 319390fccb52SAndrzej Pietrasiewicz /* Setup deferred workqueue data */ 319490fccb52SAndrzej Pietrasiewicz udc->poweron = udc->pullup = 0; 319590fccb52SAndrzej Pietrasiewicz INIT_WORK(&udc->pullup_job, pullup_work); 319690fccb52SAndrzej Pietrasiewicz INIT_WORK(&udc->vbus_job, vbus_work); 319790fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_PM 319890fccb52SAndrzej Pietrasiewicz INIT_WORK(&udc->power_job, power_work); 319990fccb52SAndrzej Pietrasiewicz #endif 320090fccb52SAndrzej Pietrasiewicz 320190fccb52SAndrzej Pietrasiewicz /* All clocks are now on */ 320290fccb52SAndrzej Pietrasiewicz udc->clocked = 1; 320390fccb52SAndrzej Pietrasiewicz 320490fccb52SAndrzej Pietrasiewicz isp1301_udc_configure(udc); 320590fccb52SAndrzej Pietrasiewicz /* Allocate memory for the UDCA */ 320690fccb52SAndrzej Pietrasiewicz udc->udca_v_base = dma_alloc_coherent(&pdev->dev, UDCA_BUFF_SIZE, 320790fccb52SAndrzej Pietrasiewicz &dma_handle, 320890fccb52SAndrzej Pietrasiewicz (GFP_KERNEL | GFP_DMA)); 320990fccb52SAndrzej Pietrasiewicz if (!udc->udca_v_base) { 321090fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "error getting UDCA region\n"); 321190fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 321290fccb52SAndrzej Pietrasiewicz goto i2c_fail; 321390fccb52SAndrzej Pietrasiewicz } 321490fccb52SAndrzej Pietrasiewicz udc->udca_p_base = dma_handle; 321590fccb52SAndrzej Pietrasiewicz dev_dbg(udc->dev, "DMA buffer(0x%x bytes), P:0x%08x, V:0x%p\n", 321690fccb52SAndrzej Pietrasiewicz UDCA_BUFF_SIZE, udc->udca_p_base, udc->udca_v_base); 321790fccb52SAndrzej Pietrasiewicz 321890fccb52SAndrzej Pietrasiewicz /* Setup the DD DMA memory pool */ 321990fccb52SAndrzej Pietrasiewicz udc->dd_cache = dma_pool_create("udc_dd", udc->dev, 322090fccb52SAndrzej Pietrasiewicz sizeof(struct lpc32xx_usbd_dd_gad), 322190fccb52SAndrzej Pietrasiewicz sizeof(u32), 0); 322290fccb52SAndrzej Pietrasiewicz if (!udc->dd_cache) { 322390fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "error getting DD DMA region\n"); 322490fccb52SAndrzej Pietrasiewicz retval = -ENOMEM; 322590fccb52SAndrzej Pietrasiewicz goto dma_alloc_fail; 322690fccb52SAndrzej Pietrasiewicz } 322790fccb52SAndrzej Pietrasiewicz 322890fccb52SAndrzej Pietrasiewicz /* Clear USB peripheral and initialize gadget endpoints */ 322990fccb52SAndrzej Pietrasiewicz udc_disable(udc); 323090fccb52SAndrzej Pietrasiewicz udc_reinit(udc); 323190fccb52SAndrzej Pietrasiewicz 323290fccb52SAndrzej Pietrasiewicz /* Request IRQs - low and high priority USB device IRQs are routed to 323390fccb52SAndrzej Pietrasiewicz * the same handler, while the DMA interrupt is routed elsewhere */ 323490fccb52SAndrzej Pietrasiewicz retval = request_irq(udc->udp_irq[IRQ_USB_LP], lpc32xx_usb_lp_irq, 323590fccb52SAndrzej Pietrasiewicz 0, "udc_lp", udc); 323690fccb52SAndrzej Pietrasiewicz if (retval < 0) { 323790fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "LP request irq %d failed\n", 323890fccb52SAndrzej Pietrasiewicz udc->udp_irq[IRQ_USB_LP]); 323990fccb52SAndrzej Pietrasiewicz goto irq_lp_fail; 324090fccb52SAndrzej Pietrasiewicz } 324190fccb52SAndrzej Pietrasiewicz retval = request_irq(udc->udp_irq[IRQ_USB_HP], lpc32xx_usb_hp_irq, 324290fccb52SAndrzej Pietrasiewicz 0, "udc_hp", udc); 324390fccb52SAndrzej Pietrasiewicz if (retval < 0) { 324490fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "HP request irq %d failed\n", 324590fccb52SAndrzej Pietrasiewicz udc->udp_irq[IRQ_USB_HP]); 324690fccb52SAndrzej Pietrasiewicz goto irq_hp_fail; 324790fccb52SAndrzej Pietrasiewicz } 324890fccb52SAndrzej Pietrasiewicz 324990fccb52SAndrzej Pietrasiewicz retval = request_irq(udc->udp_irq[IRQ_USB_DEVDMA], 325090fccb52SAndrzej Pietrasiewicz lpc32xx_usb_devdma_irq, 0, "udc_dma", udc); 325190fccb52SAndrzej Pietrasiewicz if (retval < 0) { 325290fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "DEV request irq %d failed\n", 325390fccb52SAndrzej Pietrasiewicz udc->udp_irq[IRQ_USB_DEVDMA]); 325490fccb52SAndrzej Pietrasiewicz goto irq_dev_fail; 325590fccb52SAndrzej Pietrasiewicz } 325690fccb52SAndrzej Pietrasiewicz 325790fccb52SAndrzej Pietrasiewicz /* The transceiver interrupt is used for VBUS detection and will 325890fccb52SAndrzej Pietrasiewicz kick off the VBUS handler function */ 325990fccb52SAndrzej Pietrasiewicz retval = request_irq(udc->udp_irq[IRQ_USB_ATX], lpc32xx_usb_vbus_irq, 326090fccb52SAndrzej Pietrasiewicz 0, "udc_otg", udc); 326190fccb52SAndrzej Pietrasiewicz if (retval < 0) { 326290fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "VBUS request irq %d failed\n", 326390fccb52SAndrzej Pietrasiewicz udc->udp_irq[IRQ_USB_ATX]); 326490fccb52SAndrzej Pietrasiewicz goto irq_xcvr_fail; 326590fccb52SAndrzej Pietrasiewicz } 326690fccb52SAndrzej Pietrasiewicz 326790fccb52SAndrzej Pietrasiewicz /* Initialize wait queue */ 326890fccb52SAndrzej Pietrasiewicz init_waitqueue_head(&udc->ep_disable_wait_queue); 326990fccb52SAndrzej Pietrasiewicz atomic_set(&udc->enabled_ep_cnt, 0); 327090fccb52SAndrzej Pietrasiewicz 327190fccb52SAndrzej Pietrasiewicz /* Keep all IRQs disabled until GadgetFS starts up */ 327290fccb52SAndrzej Pietrasiewicz for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++) 327390fccb52SAndrzej Pietrasiewicz disable_irq(udc->udp_irq[i]); 327490fccb52SAndrzej Pietrasiewicz 327590fccb52SAndrzej Pietrasiewicz retval = usb_add_gadget_udc(dev, &udc->gadget); 327690fccb52SAndrzej Pietrasiewicz if (retval < 0) 327790fccb52SAndrzej Pietrasiewicz goto add_gadget_fail; 327890fccb52SAndrzej Pietrasiewicz 327990fccb52SAndrzej Pietrasiewicz dev_set_drvdata(dev, udc); 328090fccb52SAndrzej Pietrasiewicz device_init_wakeup(dev, 1); 328190fccb52SAndrzej Pietrasiewicz create_debug_file(udc); 328290fccb52SAndrzej Pietrasiewicz 328390fccb52SAndrzej Pietrasiewicz /* Disable clocks for now */ 328490fccb52SAndrzej Pietrasiewicz udc_clk_set(udc, 0); 328590fccb52SAndrzej Pietrasiewicz 328690fccb52SAndrzej Pietrasiewicz dev_info(udc->dev, "%s version %s\n", driver_name, DRIVER_VERSION); 328790fccb52SAndrzej Pietrasiewicz return 0; 328890fccb52SAndrzej Pietrasiewicz 328990fccb52SAndrzej Pietrasiewicz add_gadget_fail: 329090fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_ATX], udc); 329190fccb52SAndrzej Pietrasiewicz irq_xcvr_fail: 329290fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc); 329390fccb52SAndrzej Pietrasiewicz irq_dev_fail: 329490fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_HP], udc); 329590fccb52SAndrzej Pietrasiewicz irq_hp_fail: 329690fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_LP], udc); 329790fccb52SAndrzej Pietrasiewicz irq_lp_fail: 329890fccb52SAndrzej Pietrasiewicz dma_pool_destroy(udc->dd_cache); 329990fccb52SAndrzej Pietrasiewicz dma_alloc_fail: 330090fccb52SAndrzej Pietrasiewicz dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, 330190fccb52SAndrzej Pietrasiewicz udc->udca_v_base, udc->udca_p_base); 330290fccb52SAndrzej Pietrasiewicz i2c_fail: 330390fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_otg_clk); 330490fccb52SAndrzej Pietrasiewicz usb_otg_clk_enable_fail: 330590fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_slv_clk); 330690fccb52SAndrzej Pietrasiewicz usb_clk_enable_fail: 330790fccb52SAndrzej Pietrasiewicz pll_set_fail: 330890fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_pll_clk); 330990fccb52SAndrzej Pietrasiewicz pll_enable_fail: 331090fccb52SAndrzej Pietrasiewicz clk_put(udc->usb_otg_clk); 331190fccb52SAndrzej Pietrasiewicz usb_otg_clk_get_fail: 331290fccb52SAndrzej Pietrasiewicz clk_put(udc->usb_slv_clk); 331390fccb52SAndrzej Pietrasiewicz usb_clk_get_fail: 331490fccb52SAndrzej Pietrasiewicz clk_put(udc->usb_pll_clk); 331590fccb52SAndrzej Pietrasiewicz pll_get_fail: 331690fccb52SAndrzej Pietrasiewicz iounmap(udc->udp_baseaddr); 331790fccb52SAndrzej Pietrasiewicz io_map_fail: 331890fccb52SAndrzej Pietrasiewicz release_mem_region(udc->io_p_start, udc->io_p_size); 331990fccb52SAndrzej Pietrasiewicz dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval); 332090fccb52SAndrzej Pietrasiewicz request_mem_region_fail: 332190fccb52SAndrzej Pietrasiewicz irq_fail: 332290fccb52SAndrzej Pietrasiewicz resource_fail: 332390fccb52SAndrzej Pietrasiewicz phy_fail: 332490fccb52SAndrzej Pietrasiewicz kfree(udc); 332590fccb52SAndrzej Pietrasiewicz return retval; 332690fccb52SAndrzej Pietrasiewicz } 332790fccb52SAndrzej Pietrasiewicz 332890fccb52SAndrzej Pietrasiewicz static int lpc32xx_udc_remove(struct platform_device *pdev) 332990fccb52SAndrzej Pietrasiewicz { 333090fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = platform_get_drvdata(pdev); 333190fccb52SAndrzej Pietrasiewicz 333290fccb52SAndrzej Pietrasiewicz usb_del_gadget_udc(&udc->gadget); 333390fccb52SAndrzej Pietrasiewicz if (udc->driver) 333490fccb52SAndrzej Pietrasiewicz return -EBUSY; 333590fccb52SAndrzej Pietrasiewicz 333690fccb52SAndrzej Pietrasiewicz udc_clk_set(udc, 1); 333790fccb52SAndrzej Pietrasiewicz udc_disable(udc); 333890fccb52SAndrzej Pietrasiewicz pullup(udc, 0); 333990fccb52SAndrzej Pietrasiewicz 334090fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_ATX], udc); 334190fccb52SAndrzej Pietrasiewicz 334290fccb52SAndrzej Pietrasiewicz device_init_wakeup(&pdev->dev, 0); 334390fccb52SAndrzej Pietrasiewicz remove_debug_file(udc); 334490fccb52SAndrzej Pietrasiewicz 334590fccb52SAndrzej Pietrasiewicz dma_pool_destroy(udc->dd_cache); 334690fccb52SAndrzej Pietrasiewicz dma_free_coherent(&pdev->dev, UDCA_BUFF_SIZE, 334790fccb52SAndrzej Pietrasiewicz udc->udca_v_base, udc->udca_p_base); 334890fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_DEVDMA], udc); 334990fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_HP], udc); 335090fccb52SAndrzej Pietrasiewicz free_irq(udc->udp_irq[IRQ_USB_LP], udc); 335190fccb52SAndrzej Pietrasiewicz 335290fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_otg_clk); 335390fccb52SAndrzej Pietrasiewicz clk_put(udc->usb_otg_clk); 335490fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_slv_clk); 335590fccb52SAndrzej Pietrasiewicz clk_put(udc->usb_slv_clk); 335690fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_pll_clk); 335790fccb52SAndrzej Pietrasiewicz clk_put(udc->usb_pll_clk); 335890fccb52SAndrzej Pietrasiewicz iounmap(udc->udp_baseaddr); 335990fccb52SAndrzej Pietrasiewicz release_mem_region(udc->io_p_start, udc->io_p_size); 336090fccb52SAndrzej Pietrasiewicz kfree(udc); 336190fccb52SAndrzej Pietrasiewicz 336290fccb52SAndrzej Pietrasiewicz return 0; 336390fccb52SAndrzej Pietrasiewicz } 336490fccb52SAndrzej Pietrasiewicz 336590fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_PM 336690fccb52SAndrzej Pietrasiewicz static int lpc32xx_udc_suspend(struct platform_device *pdev, pm_message_t mesg) 336790fccb52SAndrzej Pietrasiewicz { 336890fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = platform_get_drvdata(pdev); 336990fccb52SAndrzej Pietrasiewicz 337090fccb52SAndrzej Pietrasiewicz if (udc->clocked) { 337190fccb52SAndrzej Pietrasiewicz /* Power down ISP */ 337290fccb52SAndrzej Pietrasiewicz udc->poweron = 0; 337390fccb52SAndrzej Pietrasiewicz isp1301_set_powerstate(udc, 0); 337490fccb52SAndrzej Pietrasiewicz 337590fccb52SAndrzej Pietrasiewicz /* Disable clocking */ 337690fccb52SAndrzej Pietrasiewicz udc_clk_set(udc, 0); 337790fccb52SAndrzej Pietrasiewicz 337890fccb52SAndrzej Pietrasiewicz /* Keep clock flag on, so we know to re-enable clocks 337990fccb52SAndrzej Pietrasiewicz on resume */ 338090fccb52SAndrzej Pietrasiewicz udc->clocked = 1; 338190fccb52SAndrzej Pietrasiewicz 338290fccb52SAndrzej Pietrasiewicz /* Kill global USB clock */ 338390fccb52SAndrzej Pietrasiewicz clk_disable(udc->usb_slv_clk); 338490fccb52SAndrzej Pietrasiewicz } 338590fccb52SAndrzej Pietrasiewicz 338690fccb52SAndrzej Pietrasiewicz return 0; 338790fccb52SAndrzej Pietrasiewicz } 338890fccb52SAndrzej Pietrasiewicz 338990fccb52SAndrzej Pietrasiewicz static int lpc32xx_udc_resume(struct platform_device *pdev) 339090fccb52SAndrzej Pietrasiewicz { 339190fccb52SAndrzej Pietrasiewicz struct lpc32xx_udc *udc = platform_get_drvdata(pdev); 339290fccb52SAndrzej Pietrasiewicz 339390fccb52SAndrzej Pietrasiewicz if (udc->clocked) { 339490fccb52SAndrzej Pietrasiewicz /* Enable global USB clock */ 339590fccb52SAndrzej Pietrasiewicz clk_enable(udc->usb_slv_clk); 339690fccb52SAndrzej Pietrasiewicz 339790fccb52SAndrzej Pietrasiewicz /* Enable clocking */ 339890fccb52SAndrzej Pietrasiewicz udc_clk_set(udc, 1); 339990fccb52SAndrzej Pietrasiewicz 340090fccb52SAndrzej Pietrasiewicz /* ISP back to normal power mode */ 340190fccb52SAndrzej Pietrasiewicz udc->poweron = 1; 340290fccb52SAndrzej Pietrasiewicz isp1301_set_powerstate(udc, 1); 340390fccb52SAndrzej Pietrasiewicz } 340490fccb52SAndrzej Pietrasiewicz 340590fccb52SAndrzej Pietrasiewicz return 0; 340690fccb52SAndrzej Pietrasiewicz } 340790fccb52SAndrzej Pietrasiewicz #else 340890fccb52SAndrzej Pietrasiewicz #define lpc32xx_udc_suspend NULL 340990fccb52SAndrzej Pietrasiewicz #define lpc32xx_udc_resume NULL 341090fccb52SAndrzej Pietrasiewicz #endif 341190fccb52SAndrzej Pietrasiewicz 341290fccb52SAndrzej Pietrasiewicz #ifdef CONFIG_OF 341390fccb52SAndrzej Pietrasiewicz static const struct of_device_id lpc32xx_udc_of_match[] = { 341490fccb52SAndrzej Pietrasiewicz { .compatible = "nxp,lpc3220-udc", }, 341590fccb52SAndrzej Pietrasiewicz { }, 341690fccb52SAndrzej Pietrasiewicz }; 341790fccb52SAndrzej Pietrasiewicz MODULE_DEVICE_TABLE(of, lpc32xx_udc_of_match); 341890fccb52SAndrzej Pietrasiewicz #endif 341990fccb52SAndrzej Pietrasiewicz 342090fccb52SAndrzej Pietrasiewicz static struct platform_driver lpc32xx_udc_driver = { 342190fccb52SAndrzej Pietrasiewicz .remove = lpc32xx_udc_remove, 342290fccb52SAndrzej Pietrasiewicz .shutdown = lpc32xx_udc_shutdown, 342390fccb52SAndrzej Pietrasiewicz .suspend = lpc32xx_udc_suspend, 342490fccb52SAndrzej Pietrasiewicz .resume = lpc32xx_udc_resume, 342590fccb52SAndrzej Pietrasiewicz .driver = { 342690fccb52SAndrzej Pietrasiewicz .name = (char *) driver_name, 342790fccb52SAndrzej Pietrasiewicz .of_match_table = of_match_ptr(lpc32xx_udc_of_match), 342890fccb52SAndrzej Pietrasiewicz }, 342990fccb52SAndrzej Pietrasiewicz }; 343090fccb52SAndrzej Pietrasiewicz 343190fccb52SAndrzej Pietrasiewicz module_platform_driver_probe(lpc32xx_udc_driver, lpc32xx_udc_probe); 343290fccb52SAndrzej Pietrasiewicz 343390fccb52SAndrzej Pietrasiewicz MODULE_DESCRIPTION("LPC32XX udc driver"); 343490fccb52SAndrzej Pietrasiewicz MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>"); 343590fccb52SAndrzej Pietrasiewicz MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 343690fccb52SAndrzej Pietrasiewicz MODULE_LICENSE("GPL"); 343790fccb52SAndrzej Pietrasiewicz MODULE_ALIAS("platform:lpc32xx_udc"); 3438