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