| Submitter | Łukasz Majewski |
|---|---|
| Date | Oct. 17, 2011, 2:59 p.m. |
| Message ID | <1318863589-6067-1-git-send-email-l.majewski@samsung.com> |
| Download | mbox | patch |
| Permalink | /patch/120223/ |
| State | Superseded |
| Headers | show |
Comments
Hi, 2011/10/17 Lukasz Majewski <l.majewski@samsung.com>: > This commit provides UDC driver support for Samsung's SoC > family of processors. > > Signed-off-by: Lukasz Majewski <l.majewski@samsung.com> > Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> > Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> > Cc: Minkyu Kang <mk7.kang@samsung.com> > Cc: Remy Bohmer <linux@bohmer.net> > --- > Changes for v2: > - Coding cleanups and fixes > - s3c_udc* files history and origination added > - regs-otg.h rewritten to use structs instedad of > #defines > Changes for v3: > - D-cache coherency support > Changes for v4: > - Coding style fixes to achieve checkpatch's: > total: 0 errors, 0 warnings, 2783 lines checked Applied to u-boot-usb. Thanks. Kind regards, Remy > --- > drivers/usb/gadget/Makefile | 4 + > drivers/usb/gadget/regs-otg.h | 209 +++++ > drivers/usb/gadget/s3c_udc_otg.c | 888 ++++++++++++++++++ > drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 1449 +++++++++++++++++++++++++++++ > include/usb/lin_gadget_compat.h | 62 ++ > include/usb/s3c_udc.h | 165 ++++ > 6 files changed, 2777 insertions(+), 0 deletions(-) > create mode 100644 drivers/usb/gadget/regs-otg.h > create mode 100644 drivers/usb/gadget/s3c_udc_otg.c > create mode 100644 drivers/usb/gadget/s3c_udc_otg_xfer_dma.c > create mode 100644 include/usb/lin_gadget_compat.h > create mode 100644 include/usb/s3c_udc.h > > diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile > index 7d5b504..cd22bbe 100644 > --- a/drivers/usb/gadget/Makefile > +++ b/drivers/usb/gadget/Makefile > @@ -26,6 +26,10 @@ include $(TOPDIR)/config.mk > LIB := $(obj)libusb_gadget.o > > # new USB gadget layer dependencies > +ifdef CONFIG_USB_GADGET > +COBJS-y += epautoconf.o config.o usbstring.o > +COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o > +endif > ifdef CONFIG_USB_ETHER > COBJS-y += ether.o epautoconf.o config.o usbstring.o > COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o > diff --git a/drivers/usb/gadget/regs-otg.h b/drivers/usb/gadget/regs-otg.h > new file mode 100644 > index 0000000..2c3ea73 > --- /dev/null > +++ b/drivers/usb/gadget/regs-otg.h > @@ -0,0 +1,209 @@ > +/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h > + * > + * Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at> > + * > + * Registers remapping: > + * Lukasz Majewski <l.majewski@samsumg.com> > + * > + * This include file is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation; either version 2 of > + * the License, or (at your option) any later version. > + */ > + > +#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H > +#define __ASM_ARCH_REGS_USB_OTG_HS_H > + > +/* USB2.0 OTG Controller register */ > +struct s3c_usbotg_phy { > + u32 phypwr; > + u32 phyclk; > + u32 rstcon; > +}; > + > +/* Device Logical IN Endpoint-Specific Registers */ > +struct s3c_dev_in_endp { > + u32 diepctl; > + u8 res1[4]; > + u32 diepint; > + u8 res2[4]; > + u32 dieptsiz; > + u32 diepdma; > + u8 res3[4]; > + u32 diepdmab; > +}; > + > +/* Device Logical OUT Endpoint-Specific Registers */ > +struct s3c_dev_out_endp { > + u32 doepctl; > + u8 res1[4]; > + u32 doepint; > + u8 res2[4]; > + u32 doeptsiz; > + u32 doepdma; > + u8 res3[4]; > + u32 doepdmab; > +}; > + > +struct ep_fifo { > + u32 fifo; > + u8 res[4092]; > +}; > + > +/* USB2.0 OTG Controller register */ > +struct s3c_usbotg_reg { > + /* Core Global Registers */ > + u32 gotgctl; /* OTG Control & Status */ > + u32 gotgint; /* OTG Interrupt */ > + u32 gahbcfg; /* Core AHB Configuration */ > + u32 gusbcfg; /* Core USB Configuration */ > + u32 grstctl; /* Core Reset */ > + u32 gintsts; /* Core Interrupt */ > + u32 gintmsk; /* Core Interrupt Mask */ > + u32 grxstsr; /* Receive Status Debug Read/Status Read */ > + u32 grxstsp; /* Receive Status Debug Pop/Status Pop */ > + u32 grxfsiz; /* Receive FIFO Size */ > + u32 gnptxfsiz; /* Non-Periodic Transmit FIFO Size */ > + u8 res1[216]; > + u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */ > + u8 res2[1728]; > + /* Device Configuration */ > + u32 dcfg; /* Device Configuration Register */ > + u32 dctl; /* Device Control */ > + u32 dsts; /* Device Status */ > + u8 res3[4]; > + u32 diepmsk; /* Device IN Endpoint Common Interrupt Mask */ > + u32 doepmsk; /* Device OUT Endpoint Common Interrupt Mask */ > + u32 daint; /* Device All Endpoints Interrupt */ > + u32 daintmsk; /* Device All Endpoints Interrupt Mask */ > + u8 res4[224]; > + struct s3c_dev_in_endp in_endp[16]; > + struct s3c_dev_out_endp out_endp[16]; > + u8 res5[768]; > + struct ep_fifo ep[16]; > +}; > + > +/*===================================================================== */ > +/*definitions related to CSR setting */ > + > +/* S3C_UDC_OTG_GOTGCTL */ > +#define B_SESSION_VALID (0x1<<19) > +#define A_SESSION_VALID (0x1<<18) > + > +/* S3C_UDC_OTG_GAHBCFG */ > +#define PTXFE_HALF (0<<8) > +#define PTXFE_ZERO (1<<8) > +#define NPTXFE_HALF (0<<7) > +#define NPTXFE_ZERO (1<<7) > +#define MODE_SLAVE (0<<5) > +#define MODE_DMA (1<<5) > +#define BURST_SINGLE (0<<1) > +#define BURST_INCR (1<<1) > +#define BURST_INCR4 (3<<1) > +#define BURST_INCR8 (5<<1) > +#define BURST_INCR16 (7<<1) > +#define GBL_INT_UNMASK (1<<0) > +#define GBL_INT_MASK (0<<0) > + > +/* S3C_UDC_OTG_GRSTCTL */ > +#define AHB_MASTER_IDLE (1u<<31) > +#define CORE_SOFT_RESET (0x1<<0) > + > +/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */ > +#define INT_RESUME (1u<<31) > +#define INT_DISCONN (0x1<<29) > +#define INT_CONN_ID_STS_CNG (0x1<<28) > +#define INT_OUT_EP (0x1<<19) > +#define INT_IN_EP (0x1<<18) > +#define INT_ENUMDONE (0x1<<13) > +#define INT_RESET (0x1<<12) > +#define INT_SUSPEND (0x1<<11) > +#define INT_EARLY_SUSPEND (0x1<<10) > +#define INT_NP_TX_FIFO_EMPTY (0x1<<5) > +#define INT_RX_FIFO_NOT_EMPTY (0x1<<4) > +#define INT_SOF (0x1<<3) > +#define INT_DEV_MODE (0x0<<0) > +#define INT_HOST_MODE (0x1<<1) > +#define INT_GOUTNakEff (0x01<<7) > +#define INT_GINNakEff (0x01<<6) > + > +#define FULL_SPEED_CONTROL_PKT_SIZE 8 > +#define FULL_SPEED_BULK_PKT_SIZE 64 > + > +#define HIGH_SPEED_CONTROL_PKT_SIZE 64 > +#define HIGH_SPEED_BULK_PKT_SIZE 512 > + > +#define RX_FIFO_SIZE (1024*4) > +#define NPTX_FIFO_SIZE (1024*4) > +#define PTX_FIFO_SIZE (1536*1) > + > +#define DEPCTL_TXFNUM_0 (0x0<<22) > +#define DEPCTL_TXFNUM_1 (0x1<<22) > +#define DEPCTL_TXFNUM_2 (0x2<<22) > +#define DEPCTL_TXFNUM_3 (0x3<<22) > +#define DEPCTL_TXFNUM_4 (0x4<<22) > + > +/* Enumeration speed */ > +#define USB_HIGH_30_60MHZ (0x0<<1) > +#define USB_FULL_30_60MHZ (0x1<<1) > +#define USB_LOW_6MHZ (0x2<<1) > +#define USB_FULL_48MHZ (0x3<<1) > + > +/* S3C_UDC_OTG_GRXSTSP STATUS */ > +#define OUT_PKT_RECEIVED (0x2<<17) > +#define OUT_TRANSFER_COMPLELTED (0x3<<17) > +#define SETUP_TRANSACTION_COMPLETED (0x4<<17) > +#define SETUP_PKT_RECEIVED (0x6<<17) > +#define GLOBAL_OUT_NAK (0x1<<17) > + > +/* S3C_UDC_OTG_DCTL device control register */ > +#define NORMAL_OPERATION (0x1<<0) > +#define SOFT_DISCONNECT (0x1<<1) > + > +/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */ > +#define DAINT_OUT_BIT (16) > +#define DAINT_MASK (0xFFFF) > + > +/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device > + control IN/OUT endpoint 0 control register */ > +#define DEPCTL_EPENA (0x1<<31) > +#define DEPCTL_EPDIS (0x1<<30) > +#define DEPCTL_SETD1PID (0x1<<29) > +#define DEPCTL_SETD0PID (0x1<<28) > +#define DEPCTL_SNAK (0x1<<27) > +#define DEPCTL_CNAK (0x1<<26) > +#define DEPCTL_STALL (0x1<<21) > +#define DEPCTL_TYPE_BIT (18) > +#define DEPCTL_TYPE_MASK (0x3<<18) > +#define DEPCTL_CTRL_TYPE (0x0<<18) > +#define DEPCTL_ISO_TYPE (0x1<<18) > +#define DEPCTL_BULK_TYPE (0x2<<18) > +#define DEPCTL_INTR_TYPE (0x3<<18) > +#define DEPCTL_USBACTEP (0x1<<15) > +#define DEPCTL_NEXT_EP_BIT (11) > +#define DEPCTL_MPS_BIT (0) > +#define DEPCTL_MPS_MASK (0x7FF) > + > +#define DEPCTL0_MPS_64 (0x0<<0) > +#define DEPCTL0_MPS_32 (0x1<<0) > +#define DEPCTL0_MPS_16 (0x2<<0) > +#define DEPCTL0_MPS_8 (0x3<<0) > +#define DEPCTL_MPS_BULK_512 (512<<0) > +#define DEPCTL_MPS_INT_MPS_16 (16<<0) > + > +#define DIEPCTL0_NEXT_EP_BIT (11) > + > + > +/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint > + common interrupt mask register */ > +/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */ > +#define BACK2BACK_SETUP_RECEIVED (0x1<<6) > +#define INTKNEPMIS (0x1<<5) > +#define INTKN_TXFEMP (0x1<<4) > +#define NON_ISO_IN_EP_TIMEOUT (0x1<<3) > +#define CTRL_OUT_EP_SETUP_PHASE_DONE (0x1<<3) > +#define AHB_ERROR (0x1<<2) > +#define EPDISBLD (0x1<<1) > +#define TRANSFER_DONE (0x1<<0) > + > +#endif > diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c > new file mode 100644 > index 0000000..182b519 > --- /dev/null > +++ b/drivers/usb/gadget/s3c_udc_otg.c > @@ -0,0 +1,888 @@ > +/* > + * drivers/usb/gadget/s3c_udc_otg.c > + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers > + * > + * Copyright (C) 2008 for Samsung Electronics > + * > + * BSP Support for Samsung's UDC driver > + * available at: > + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git > + * > + * State machine bugfixes: > + * Marek Szyprowski <m.szyprowski@samsung.com> > + * > + * Ported to u-boot: > + * Marek Szyprowski <m.szyprowski@samsung.com> > + * Lukasz Majewski <l.majewski@samsumg.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + */ > + > +#include <common.h> > +#include <asm/errno.h> > +#include <linux/list.h> > +#include <malloc.h> > + > +#include <linux/usb/ch9.h> > +#include <linux/usb/gadget.h> > + > +#include <asm/byteorder.h> > +#include <asm/io.h> > + > +#include <asm/mach-types.h> > +#include <asm/arch/gpio.h> > + > +#include "regs-otg.h" > +#include <usb/s3c_udc.h> > +#include <asm/arch/hs_otg.h> > +#include <asm/arch/power.h> > +#include <usb/lin_gadget_compat.h> > + > +/***********************************************************/ > + > +#define OTG_DMA_MODE 1 > + > +#undef DEBUG_S3C_UDC_SETUP > +#undef DEBUG_S3C_UDC_EP0 > +#undef DEBUG_S3C_UDC_ISR > +#undef DEBUG_S3C_UDC_OUT_EP > +#undef DEBUG_S3C_UDC_IN_EP > +#undef DEBUG_S3C_UDC > + > +/* #define DEBUG_S3C_UDC_SETUP */ > +/* #define DEBUG_S3C_UDC_EP0 */ > +/* #define DEBUG_S3C_UDC_ISR */ > +/* #define DEBUG_S3C_UDC_OUT_EP */ > +/* #define DEBUG_S3C_UDC_IN_EP */ > +/* #define DEBUG_S3C_UDC */ > + > +#include <usb/s3c_udc.h> > + > +#define EP0_CON 0 > +#define EP_MASK 0xF > + > +#if defined(DEBUG_S3C_UDC_SETUP) || defined(DEBUG_S3C_UDC_ISR) \ > + || defined(DEBUG_S3C_UDC_OUT_EP) > + > +static char *state_names[] = { > + "WAIT_FOR_SETUP", > + "DATA_STATE_XMIT", > + "DATA_STATE_NEED_ZLP", > + "WAIT_FOR_OUT_STATUS", > + "DATA_STATE_RECV", > + "WAIT_FOR_COMPLETE", > + "WAIT_FOR_OUT_COMPLETE", > + "WAIT_FOR_IN_COMPLETE", > + "WAIT_FOR_NULL_COMPLETE", > +}; > +#endif > + > +#define DRIVER_DESC "S3C HS USB OTG Device Driver, (c) Samsung Electronics" > +#define DRIVER_VERSION "15 March 2009" > + > +struct s3c_udc *the_controller; > + > +static const char driver_name[] = "s3c-udc"; > +static const char driver_desc[] = DRIVER_DESC; > +static const char ep0name[] = "ep0-control"; > + > +/* Max packet size*/ > +static unsigned int ep0_fifo_size = 64; > +static unsigned int ep_fifo_size = 512; > +static unsigned int ep_fifo_size2 = 1024; > +static int reset_available = 1; > + > +static struct usb_ctrlrequest *usb_ctrl; > +static dma_addr_t usb_ctrl_dma_addr; > + > +/* > + Local declarations. > +*/ > +static int s3c_ep_enable(struct usb_ep *ep, > + const struct usb_endpoint_descriptor *); > +static int s3c_ep_disable(struct usb_ep *ep); > +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, > + gfp_t gfp_flags); > +static void s3c_free_request(struct usb_ep *ep, struct usb_request *); > + > +static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags); > +static int s3c_dequeue(struct usb_ep *ep, struct usb_request *); > +static int s3c_fifo_status(struct usb_ep *ep); > +static void s3c_fifo_flush(struct usb_ep *ep); > +static void s3c_ep0_read(struct s3c_udc *dev); > +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep); > +static void s3c_handle_ep0(struct s3c_udc *dev); > +static int s3c_ep0_write(struct s3c_udc *dev); > +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req); > +static void done(struct s3c_ep *ep, struct s3c_request *req, int status); > +static void stop_activity(struct s3c_udc *dev, > + struct usb_gadget_driver *driver); > +static int udc_enable(struct s3c_udc *dev); > +static void udc_set_address(struct s3c_udc *dev, unsigned char address); > +static void reconfig_usbd(void); > +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed); > +static void nuke(struct s3c_ep *ep, int status); > +static int s3c_udc_set_halt(struct usb_ep *_ep, int value); > +static void s3c_udc_set_nak(struct s3c_ep *ep); > + > +static struct usb_ep_ops s3c_ep_ops = { > + .enable = s3c_ep_enable, > + .disable = s3c_ep_disable, > + > + .alloc_request = s3c_alloc_request, > + .free_request = s3c_free_request, > + > + .queue = s3c_queue, > + .dequeue = s3c_dequeue, > + > + .set_halt = s3c_udc_set_halt, > + .fifo_status = s3c_fifo_status, > + .fifo_flush = s3c_fifo_flush, > +}; > + > +#define create_proc_files() do {} while (0) > +#define remove_proc_files() do {} while (0) > + > +/***********************************************************/ > + > +void __iomem *regs_otg; > +struct s3c_usbotg_reg *reg; > +struct s3c_usbotg_phy *phy; > + > + > +void otg_phy_init(void) > +{ > + the_controller->pdata->phy_control(1); > + > + /*USB PHY0 Enable */ > + printf("USB PHY0 Enable\n"); > + > + /* Enable PHY */ > + writel(readl(S5PC110_USB_PHY_CONTROL)|(0x1<<0), > + S5PC110_USB_PHY_CONTROL); > + > + writel((readl(&phy->phypwr) > + &~(0x3<<3)&~(0x1<<0)), &phy->phypwr); > + > + writel((readl(&phy->phyclk) > + &~(0x5<<2))|(0x3<<0), &phy->phyclk); /* PLL 24Mhz */ > + writel((readl(&phy->rstcon) > + &~(0x3<<1))|(0x1<<0), &phy->rstcon); > + udelay(10); > + writel(readl(&phy->rstcon) > + &~(0x7<<0), &phy->rstcon); > + udelay(10); > +} > + > +void otg_phy_off(void) > +{ > + /* reset controller just in case */ > + writel(0x1, &phy->rstcon); > + udelay(20); > + writel(0x0, &phy->rstcon); > + udelay(20); > + > + writel(readl(&phy->phypwr)|(0x3<<3)|(0x1), &phy->phypwr); > + writel(readl(S5PC110_USB_PHY_CONTROL)&~(1<<0), > + S5PC110_USB_PHY_CONTROL); > + > + writel((readl(&phy->phyclk) & ~(0x5 << 2)), &phy->phyclk); > + > + udelay(10000); > + > + the_controller->pdata->phy_control(0); > +} > + > +/***********************************************************/ > + > +#include "s3c_udc_otg_xfer_dma.c" > + > +/* > + * udc_disable - disable USB device controller > + */ > +static void udc_disable(struct s3c_udc *dev) > +{ > + DEBUG_SETUP("%s: %p\n", __func__, dev); > + > + udc_set_address(dev, 0); > + > + dev->ep0state = WAIT_FOR_SETUP; > + dev->gadget.speed = USB_SPEED_UNKNOWN; > + dev->usb_address = 0; > + > + otg_phy_off(); > +} > + > +/* > + * udc_reinit - initialize software state > + */ > +static void udc_reinit(struct s3c_udc *dev) > +{ > + unsigned int i; > + > + DEBUG_SETUP("%s: %p\n", __func__, dev); > + > + /* device/ep0 records init */ > + INIT_LIST_HEAD(&dev->gadget.ep_list); > + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); > + dev->ep0state = WAIT_FOR_SETUP; > + > + /* basic endpoint records init */ > + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { > + struct s3c_ep *ep = &dev->ep[i]; > + > + if (i != 0) > + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); > + > + ep->desc = 0; > + ep->stopped = 0; > + INIT_LIST_HEAD(&ep->queue); > + ep->pio_irqs = 0; > + } > + > + /* the rest was statically initialized, and is read-only */ > +} > + > +#define BYTES2MAXP(x) (x / 8) > +#define MAXP2BYTES(x) (x * 8) > + > +/* until it's enabled, this UDC should be completely invisible > + * to any USB host. > + */ > +static int udc_enable(struct s3c_udc *dev) > +{ > + DEBUG_SETUP("%s: %p\n", __func__, dev); > + > + otg_phy_init(); > + reconfig_usbd(); > + > + DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", > + readl(®->gintmsk)); > + > + dev->gadget.speed = USB_SPEED_UNKNOWN; > + > + return 0; > +} > + > +/* > + Register entry point for the peripheral controller driver. > +*/ > +int usb_gadget_register_driver(struct usb_gadget_driver *driver) > +{ > + struct s3c_udc *dev = the_controller; > + int retval = 0; > + unsigned long flags; > + > + DEBUG_SETUP("%s: %s\n", __func__, "no name"); > + > + if (!driver > + || (driver->speed != USB_SPEED_FULL > + && driver->speed != USB_SPEED_HIGH) > + || !driver->bind || !driver->disconnect || !driver->setup) > + return -EINVAL; > + if (!dev) > + return -ENODEV; > + if (dev->driver) > + return -EBUSY; > + > + spin_lock_irqsave(&dev->lock, flags); > + /* first hook up the driver ... */ > + dev->driver = driver; > + spin_unlock_irqrestore(&dev->lock, flags); > + > + if (retval) { /* TODO */ > + printf("target device_add failed, error %d\n", retval); > + return retval; > + } > + > + retval = driver->bind(&dev->gadget); > + if (retval) { > + DEBUG_SETUP("%s: bind to driver --> error %d\n", > + dev->gadget.name, retval); > + dev->driver = 0; > + return retval; > + } > + > + enable_irq(IRQ_OTG); > + > + DEBUG_SETUP("Registered gadget driver %s\n", dev->gadget.name); > + udc_enable(dev); > + > + return 0; > +} > + > +/* > + * Unregister entry point for the peripheral controller driver. > + */ > +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) > +{ > + struct s3c_udc *dev = the_controller; > + unsigned long flags; > + > + if (!dev) > + return -ENODEV; > + if (!driver || driver != dev->driver) > + return -EINVAL; > + > + spin_lock_irqsave(&dev->lock, flags); > + dev->driver = 0; > + stop_activity(dev, driver); > + spin_unlock_irqrestore(&dev->lock, flags); > + > + driver->unbind(&dev->gadget); > + > + disable_irq(IRQ_OTG); > + > + udc_disable(dev); > + return 0; > +} > + > +/* > + * done - retire a request; caller blocked irqs > + */ > +static void done(struct s3c_ep *ep, struct s3c_request *req, int status) > +{ > + unsigned int stopped = ep->stopped; > + > + DEBUG("%s: %s %p, req = %p, stopped = %d\n", > + __func__, ep->ep.name, ep, &req->req, stopped); > + > + list_del_init(&req->queue); > + > + if (likely(req->req.status == -EINPROGRESS)) > + req->req.status = status; > + else > + status = req->req.status; > + > + if (status && status != -ESHUTDOWN) { > + DEBUG("complete %s req %p stat %d len %u/%u\n", > + ep->ep.name, &req->req, status, > + req->req.actual, req->req.length); > + } > + > + /* don't modify queue heads during completion callback */ > + ep->stopped = 1; > + > +#ifdef DEBUG_S3C_UDC > + printf("calling complete callback\n"); > + { > + int i, len = req->req.length; > + > + printf("pkt[%d] = ", req->req.length); > + if (len > 64) > + len = 64; > + for (i = 0; i < len; i++) { > + printf("%02x", ((u8 *)req->req.buf)[i]); > + if ((i & 7) == 7) > + printf(" "); > + } > + printf("\n"); > + } > +#endif > + spin_unlock(&ep->dev->lock); > + req->req.complete(&ep->ep, &req->req); > + spin_lock(&ep->dev->lock); > + > + DEBUG("callback completed\n"); > + > + ep->stopped = stopped; > +} > + > +/* > + * nuke - dequeue ALL requests > + */ > +static void nuke(struct s3c_ep *ep, int status) > +{ > + struct s3c_request *req; > + > + DEBUG("%s: %s %p\n", __func__, ep->ep.name, ep); > + > + /* called with irqs blocked */ > + while (!list_empty(&ep->queue)) { > + req = list_entry(ep->queue.next, struct s3c_request, queue); > + done(ep, req, status); > + } > +} > + > +static void stop_activity(struct s3c_udc *dev, > + struct usb_gadget_driver *driver) > +{ > + int i; > + > + /* don't disconnect drivers more than once */ > + if (dev->gadget.speed == USB_SPEED_UNKNOWN) > + driver = 0; > + dev->gadget.speed = USB_SPEED_UNKNOWN; > + > + /* prevent new request submissions, kill any outstanding requests */ > + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { > + struct s3c_ep *ep = &dev->ep[i]; > + ep->stopped = 1; > + nuke(ep, -ESHUTDOWN); > + } > + > + /* report disconnect; the driver is already quiesced */ > + if (driver) { > + spin_unlock(&dev->lock); > + driver->disconnect(&dev->gadget); > + spin_lock(&dev->lock); > + } > + > + /* re-init driver-visible data structures */ > + udc_reinit(dev); > +} > + > +static void reconfig_usbd(void) > +{ > + /* 2. Soft-reset OTG Core and then unreset again. */ > + int i; > + unsigned int uTemp = writel(CORE_SOFT_RESET, ®->grstctl); > + > + DEBUG(2, "Reseting OTG controller\n"); > + > + writel(0<<15 /* PHY Low Power Clock sel*/ > + |1<<14 /* Non-Periodic TxFIFO Rewind Enable*/ > + |0x5<<10 /* Turnaround time*/ > + |0<<9 | 0<<8 /* [0:HNP disable,1:HNP enable][ 0:SRP disable*/ > + /* 1:SRP enable] H1= 1,1*/ > + |0<<7 /* Ulpi DDR sel*/ > + |0<<6 /* 0: high speed utmi+, 1: full speed serial*/ > + |0<<4 /* 0: utmi+, 1:ulpi*/ > + |1<<3 /* phy i/f 0:8bit, 1:16bit*/ > + |0x7<<0, /* HS/FS Timeout**/ > + ®->gusbcfg); > + > + /* 3. Put the OTG device core in the disconnected state.*/ > + uTemp = readl(®->dctl); > + uTemp |= SOFT_DISCONNECT; > + writel(uTemp, ®->dctl); > + > + udelay(20); > + > + /* 4. Make the OTG device core exit from the disconnected state.*/ > + uTemp = readl(®->dctl); > + uTemp = uTemp & ~SOFT_DISCONNECT; > + writel(uTemp, ®->dctl); > + > + /* 5. Configure OTG Core to initial settings of device mode.*/ > + /* [][1: full speed(30Mhz) 0:high speed]*/ > + writel(1<<18 | 0x0<<0, ®->dcfg); > + > + mdelay(1); > + > + /* 6. Unmask the core interrupts*/ > + writel(GINTMSK_INIT, ®->gintmsk); > + > + /* 7. Set NAK bit of EP0, EP1, EP2*/ > + writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), > + ®->out_endp[EP0_CON].doepctl); > + writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), ®->in_endp[EP0_CON].diepctl); > + > + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) { > + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[i].doepctl); > + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[i].diepctl); > + } > + > + /* 8. Unmask EPO interrupts*/ > + writel(((1 << EP0_CON) << DAINT_OUT_BIT) > + | (1 << EP0_CON), ®->daintmsk); > + > + /* 9. Unmask device OUT EP common interrupts*/ > + writel(DOEPMSK_INIT, ®->doepmsk); > + > + /* 10. Unmask device IN EP common interrupts*/ > + writel(DIEPMSK_INIT, ®->diepmsk); > + > + /* 11. Set Rx FIFO Size (in 32-bit words) */ > + writel(RX_FIFO_SIZE >> 2, ®->grxfsiz); > + > + /* 12. Set Non Periodic Tx FIFO Size*/ > + writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0, > + ®->gnptxfsiz); > + > + for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++) > + writel((PTX_FIFO_SIZE >> 2) << 16 | > + ((RX_FIFO_SIZE + NPTX_FIFO_SIZE + > + PTX_FIFO_SIZE*(i-1)) >> 2) << 0, > + ®->dieptxf[i-1]); > + > + /* Flush the RX FIFO */ > + writel(0x10, ®->grstctl); > + while (readl(®->grstctl) & 0x10) > + DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); > + > + /* Flush all the Tx FIFO's */ > + writel(0x10<<6, ®->grstctl); > + writel((0x10<<6)|0x20, ®->grstctl); > + while (readl(®->grstctl) & 0x20) > + DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); > + > + /* 13. Clear NAK bit of EP0, EP1, EP2*/ > + /* For Slave mode*/ > + /* EP0: Control OUT */ > + writel(DEPCTL_EPDIS | DEPCTL_CNAK|(0<<0), > + ®->out_endp[EP0_CON].doepctl); > + > + /* 14. Initialize OTG Link Core.*/ > + writel(GAHBCFG_INIT, ®->gahbcfg); > +} > + > +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed) > +{ > + unsigned int ep_ctrl; > + int i; > + > + if (speed == USB_SPEED_HIGH) { > + ep0_fifo_size = 64; > + ep_fifo_size = 512; > + ep_fifo_size2 = 1024; > + dev->gadget.speed = USB_SPEED_HIGH; > + } else { > + ep0_fifo_size = 64; > + ep_fifo_size = 64; > + ep_fifo_size2 = 64; > + dev->gadget.speed = USB_SPEED_FULL; > + } > + > + dev->ep[0].ep.maxpacket = ep0_fifo_size; > + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) > + dev->ep[i].ep.maxpacket = ep_fifo_size; > + > + /* EP0 - Control IN (64 bytes)*/ > + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); > + writel(ep_ctrl|(0<<0), ®->in_endp[EP0_CON].diepctl); > + > + /* EP0 - Control OUT (64 bytes)*/ > + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); > + writel(ep_ctrl|(0<<0), ®->out_endp[EP0_CON].doepctl); > +} > + > +static int s3c_ep_enable(struct usb_ep *_ep, > + const struct usb_endpoint_descriptor *desc) > +{ > + struct s3c_ep *ep; > + struct s3c_udc *dev; > + unsigned long flags; > + > + DEBUG("%s: %p\n", __func__, _ep); > + > + ep = container_of(_ep, struct s3c_ep, ep); > + if (!_ep || !desc || ep->desc || _ep->name == ep0name > + || desc->bDescriptorType != USB_DT_ENDPOINT > + || ep->bEndpointAddress != desc->bEndpointAddress > + || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { > + > + DEBUG("%s: bad ep or descriptor\n", __func__); > + return -EINVAL; > + } > + > + /* xfer types must match, except that interrupt ~= bulk */ > + if (ep->bmAttributes != desc->bmAttributes > + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK > + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { > + > + DEBUG("%s: %s type mismatch\n", __func__, _ep->name); > + return -EINVAL; > + } > + > + /* hardware _could_ do smaller, but driver doesn't */ > + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK > + && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) > + || !desc->wMaxPacketSize) { > + > + DEBUG("%s: bad %s maxpacket\n", __func__, _ep->name); > + return -ERANGE; > + } > + > + dev = ep->dev; > + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { > + > + DEBUG("%s: bogus device state\n", __func__); > + return -ESHUTDOWN; > + } > + > + ep->stopped = 0; > + ep->desc = desc; > + ep->pio_irqs = 0; > + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); > + > + /* Reset halt state */ > + s3c_udc_set_nak(ep); > + s3c_udc_set_halt(_ep, 0); > + > + spin_lock_irqsave(&ep->dev->lock, flags); > + s3c_udc_ep_activate(ep); > + spin_unlock_irqrestore(&ep->dev->lock, flags); > + > + DEBUG("%s: enabled %s, stopped = %d, maxpacket = %d\n", > + __func__, _ep->name, ep->stopped, ep->ep.maxpacket); > + return 0; > +} > + > +/* > + * Disable EP > + */ > +static int s3c_ep_disable(struct usb_ep *_ep) > +{ > + struct s3c_ep *ep; > + unsigned long flags; > + > + DEBUG("%s: %p\n", __func__, _ep); > + > + ep = container_of(_ep, struct s3c_ep, ep); > + if (!_ep || !ep->desc) { > + DEBUG("%s: %s not enabled\n", __func__, > + _ep ? ep->ep.name : NULL); > + return -EINVAL; > + } > + > + spin_lock_irqsave(&ep->dev->lock, flags); > + > + /* Nuke all pending requests */ > + nuke(ep, -ESHUTDOWN); > + > + ep->desc = 0; > + ep->stopped = 1; > + > + spin_unlock_irqrestore(&ep->dev->lock, flags); > + > + DEBUG("%s: disabled %s\n", __func__, _ep->name); > + return 0; > +} > + > +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, > + gfp_t gfp_flags) > +{ > + struct s3c_request *req; > + > + DEBUG("%s: %s %p\n", __func__, ep->name, ep); > + > + req = kmalloc(sizeof *req, gfp_flags); > + if (!req) > + return 0; > + > + memset(req, 0, sizeof *req); > + INIT_LIST_HEAD(&req->queue); > + > + return &req->req; > +} > + > +static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req) > +{ > + struct s3c_request *req; > + > + DEBUG("%s: %p\n", __func__, ep); > + > + req = container_of(_req, struct s3c_request, req); > + WARN_ON(!list_empty(&req->queue)); > + kfree(req); > +} > + > +/* dequeue JUST ONE request */ > +static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req) > +{ > + struct s3c_ep *ep; > + struct s3c_request *req; > + unsigned long flags; > + > + DEBUG("%s: %p\n", __func__, _ep); > + > + ep = container_of(_ep, struct s3c_ep, ep); > + if (!_ep || ep->ep.name == ep0name) > + return -EINVAL; > + > + spin_lock_irqsave(&ep->dev->lock, flags); > + > + /* make sure it's actually queued on this endpoint */ > + list_for_each_entry(req, &ep->queue, queue) { > + if (&req->req == _req) > + break; > + } > + if (&req->req != _req) { > + spin_unlock_irqrestore(&ep->dev->lock, flags); > + return -EINVAL; > + } > + > + done(ep, req, -ECONNRESET); > + > + spin_unlock_irqrestore(&ep->dev->lock, flags); > + return 0; > +} > + > +/* > + * Return bytes in EP FIFO > + */ > +static int s3c_fifo_status(struct usb_ep *_ep) > +{ > + int count = 0; > + struct s3c_ep *ep; > + > + ep = container_of(_ep, struct s3c_ep, ep); > + if (!_ep) { > + DEBUG("%s: bad ep\n", __func__); > + return -ENODEV; > + } > + > + DEBUG("%s: %d\n", __func__, ep_index(ep)); > + > + /* LPD can't report unclaimed bytes from IN fifos */ > + if (ep_is_in(ep)) > + return -EOPNOTSUPP; > + > + return count; > +} > + > +/* > + * Flush EP FIFO > + */ > +static void s3c_fifo_flush(struct usb_ep *_ep) > +{ > + struct s3c_ep *ep; > + > + ep = container_of(_ep, struct s3c_ep, ep); > + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { > + DEBUG("%s: bad ep\n", __func__); > + return; > + } > + > + DEBUG("%s: %d\n", __func__, ep_index(ep)); > +} > + > +static const struct usb_gadget_ops s3c_udc_ops = { > + /* current versions must always be self-powered */ > +}; > + > +static struct s3c_udc memory = { > + .usb_address = 0, > + .gadget = { > + .ops = &s3c_udc_ops, > + .ep0 = &memory.ep[0].ep, > + .name = driver_name, > + }, > + > + /* control endpoint */ > + .ep[0] = { > + .ep = { > + .name = ep0name, > + .ops = &s3c_ep_ops, > + .maxpacket = EP0_FIFO_SIZE, > + }, > + .dev = &memory, > + > + .bEndpointAddress = 0, > + .bmAttributes = 0, > + > + .ep_type = ep_control, > + }, > + > + /* first group of endpoints */ > + .ep[1] = { > + .ep = { > + .name = "ep1in-bulk", > + .ops = &s3c_ep_ops, > + .maxpacket = EP_FIFO_SIZE, > + }, > + .dev = &memory, > + > + .bEndpointAddress = USB_DIR_IN | 1, > + .bmAttributes = USB_ENDPOINT_XFER_BULK, > + > + .ep_type = ep_bulk_out, > + .fifo_num = 1, > + }, > + > + .ep[2] = { > + .ep = { > + .name = "ep2out-bulk", > + .ops = &s3c_ep_ops, > + .maxpacket = EP_FIFO_SIZE, > + }, > + .dev = &memory, > + > + .bEndpointAddress = USB_DIR_OUT | 2, > + .bmAttributes = USB_ENDPOINT_XFER_BULK, > + > + .ep_type = ep_bulk_in, > + .fifo_num = 2, > + }, > + > + .ep[3] = { > + .ep = { > + .name = "ep3in-int", > + .ops = &s3c_ep_ops, > + .maxpacket = EP_FIFO_SIZE, > + }, > + .dev = &memory, > + > + .bEndpointAddress = USB_DIR_IN | 3, > + .bmAttributes = USB_ENDPOINT_XFER_INT, > + > + .ep_type = ep_interrupt, > + .fifo_num = 3, > + }, > +}; > + > +/* > + * probe - binds to the platform device > + */ > + > +int s3c_udc_probe(struct s3c_plat_otg_data *pdata) > +{ > + struct s3c_udc *dev = &memory; > + int retval = 0, i; > + > + DEBUG("%s: %p\n", __func__, pdata); > + > + dev->pdata = pdata; > + > + phy = (struct s3c_usbotg_phy *)pdata->regs_phy; > + reg = (struct s3c_usbotg_reg *)pdata->regs_otg; > + > + /* regs_otg = (void *)pdata->regs_otg; */ > + > + dev->gadget.is_dualspeed = 1; /* Hack only*/ > + dev->gadget.is_otg = 0; > + dev->gadget.is_a_peripheral = 0; > + dev->gadget.b_hnp_enable = 0; > + dev->gadget.a_hnp_support = 0; > + dev->gadget.a_alt_hnp_support = 0; > + > + the_controller = dev; > + > + for (i = 0; i < S3C_MAX_ENDPOINTS+1; i++) { > + dev->dma_buf[i] = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL); > + dev->dma_addr[i] = (dma_addr_t) dev->dma_buf[i]; > + invalidate_dcache_range((unsigned long) dev->dma_buf[i], > + (unsigned long) (dev->dma_buf[i] > + + DMA_BUFFER_SIZE)); > + } > + usb_ctrl = dev->dma_buf[0]; > + usb_ctrl_dma_addr = dev->dma_addr[0]; > + > + udc_reinit(dev); > + > + return retval; > +} > + > +int usb_gadget_handle_interrupts() > +{ > + u32 intr_status = readl(®->gintsts); > + u32 gintmsk = readl(®->gintmsk); > + > + if (intr_status & gintmsk) > + return s3c_udc_irq(1, (void *)the_controller); > + return 0; > +} > diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c > new file mode 100644 > index 0000000..335440a > --- /dev/null > +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c > @@ -0,0 +1,1449 @@ > +/* > + * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c > + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers > + * > + * Copyright (C) 2009 for Samsung Electronics > + * > + * BSP Support for Samsung's UDC driver > + * available at: > + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git > + * > + * State machine bugfixes: > + * Marek Szyprowski <m.szyprowski@samsung.com> > + * > + * Ported to u-boot: > + * Marek Szyprowski <m.szyprowski@samsung.com> > + * Lukasz Majewski <l.majewski@samsumg.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + */ > + > +#define GINTMSK_INIT (INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\ > + | INT_RESET | INT_SUSPEND) > +#define DOEPMSK_INIT (CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE) > +#define DIEPMSK_INIT (NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE) > +#define GAHBCFG_INIT (PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\ > + | GBL_INT_UNMASK) > + > +static u8 clear_feature_num; > +int clear_feature_flag; > + > +/* Bulk-Only Mass Storage Reset (class-specific request) */ > +#define GET_MAX_LUN_REQUEST 0xFE > +#define BOT_RESET_REQUEST 0xFF > + > +static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev) > +{ > + u32 ep_ctrl; > + > + flush_dcache_range((unsigned long) usb_ctrl_dma_addr, > + (unsigned long) usb_ctrl_dma_addr > + + DMA_BUFFER_SIZE); > + > + writel(usb_ctrl_dma_addr, ®->in_endp[EP0_CON].diepdma); > + writel((1<<19 | 0<<0), ®->in_endp[EP0_CON].dieptsiz); > + > + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); > + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, > + ®->in_endp[EP0_CON].diepctl); > + > + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", > + __func__, readl(®->in_endp[EP0_CON].diepctl)); > + dev->ep0state = WAIT_FOR_IN_COMPLETE; > +} > + > +void s3c_udc_pre_setup(void) > +{ > + u32 ep_ctrl; > + > + DEBUG_IN_EP("%s : Prepare Setup packets.\n", __func__); > + > + invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, > + (unsigned long) usb_ctrl_dma_addr > + + DMA_BUFFER_SIZE); > + > + writel((1 << 19) | sizeof(struct usb_ctrlrequest), > + ®->out_endp[EP0_CON].doeptsiz); > + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); > + > + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); > + writel(ep_ctrl|DEPCTL_EPENA, ®->out_endp[EP0_CON].doepctl); > + > + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", > + __func__, readl(®->in_endp[EP0_CON].diepctl)); > + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", > + __func__, readl(®->out_endp[EP0_CON].doepctl)); > + > +} > + > +static inline void s3c_ep0_complete_out(void) > +{ > + u32 ep_ctrl; > + > + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", > + __func__, readl(®->in_endp[EP0_CON].diepctl)); > + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", > + __func__, readl(®->out_endp[EP0_CON].doepctl)); > + > + DEBUG_IN_EP("%s : Prepare Complete Out packet.\n", __func__); > + > + invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, > + (unsigned long) usb_ctrl_dma_addr > + + DMA_BUFFER_SIZE); > + > + writel((1 << 19) | sizeof(struct usb_ctrlrequest), > + ®->out_endp[EP0_CON].doeptsiz); > + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); > + > + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); > + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, > + ®->out_endp[EP0_CON].doepctl); > + > + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", > + __func__, readl(®->in_endp[EP0_CON].diepctl)); > + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", > + __func__, readl(®->out_endp[EP0_CON].doepctl)); > + > +} > + > + > +static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) > +{ > + u32 *buf, ctrl; > + u32 length, pktcnt; > + u32 ep_num = ep_index(ep); > + > + buf = req->req.buf + req->req.actual; > + > + length = min(req->req.length - req->req.actual, (int)ep->ep.maxpacket); > + > + ep->len = length; > + ep->dma_buf = buf; > + > + invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], > + (unsigned long) ep->dev->dma_buf[ep_num] > + + DMA_BUFFER_SIZE); > + > + if (length == 0) > + pktcnt = 1; > + else > + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; > + > + pktcnt = 1; > + ctrl = readl(®->out_endp[ep_num].doepctl); > + > + writel(the_controller->dma_addr[ep_index(ep)+1], > + ®->out_endp[ep_num].doepdma); > + writel((pktcnt<<19)|(length<<0), ®->out_endp[ep_num].doeptsiz); > + writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->out_endp[ep_num].doepctl); > + > + DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x," > + "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n" > + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", > + __func__, ep_num, > + readl(®->out_endp[ep_num].doepdma), > + readl(®->out_endp[ep_num].doeptsiz), > + readl(®->out_endp[ep_num].doepctl), > + buf, pktcnt, length); > + return 0; > + > +} > + > +int setdma_tx(struct s3c_ep *ep, struct s3c_request *req) > +{ > + u32 *buf, ctrl = 0; > + u32 length, pktcnt; > + u32 ep_num = ep_index(ep); > + u32 *p = the_controller->dma_buf[ep_index(ep)+1]; > + > + buf = req->req.buf + req->req.actual; > + length = req->req.length - req->req.actual; > + > + if (ep_num == EP0_CON) > + length = min_t(length, (u32)ep_maxpacket(ep)); > + > + ep->len = length; > + ep->dma_buf = buf; > + memcpy(p, ep->dma_buf, length); > + > + flush_dcache_range((unsigned long) p , > + (unsigned long) p + DMA_BUFFER_SIZE); > + > + if (length == 0) > + pktcnt = 1; > + else > + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; > + > + /* Flush the endpoint's Tx FIFO */ > + writel(ep->fifo_num<<6, ®->grstctl); > + writel((ep->fifo_num<<6)|0x20, ®->grstctl); > + while (readl(®->grstctl) & 0x20) > + ; > + > + writel(the_controller->dma_addr[ep_index(ep)+1], > + ®->in_endp[ep_num].diepdma); > + writel((pktcnt<<19)|(length<<0), ®->in_endp[ep_num].dieptsiz); > + > + ctrl = readl(®->in_endp[ep_num].diepctl); > + > + /* Write the FIFO number to be used for this endpoint */ > + ctrl &= ~(0xF << 22); > + ctrl |= (ep->fifo_num << 22); > + > + /* Clear reserved (Next EP) bits */ > + ctrl = (ctrl&~(EP_MASK<<DEPCTL_NEXT_EP_BIT)); > + > + writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->in_endp[ep_num].diepctl); > + > + DEBUG_IN_EP("%s:EP%d TX DMA start : DIEPDMA0 = 0x%x," > + "DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n" > + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", > + __func__, ep_num, > + readl(®->in_endp[ep_num].diepdma), > + readl(®->in_endp[ep_num].dieptsiz), > + readl(®->in_endp[ep_num].diepctl), > + buf, pktcnt, length); > + > + return length; > +} > + > +static void complete_rx(struct s3c_udc *dev, u8 ep_num) > +{ > + struct s3c_ep *ep = &dev->ep[ep_num]; > + struct s3c_request *req = NULL; > + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; > + u32 *p = the_controller->dma_buf[ep_index(ep)+1]; > + > + if (list_empty(&ep->queue)) { > + DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n", > + __func__, ep_num); > + return; > + > + } > + > + req = list_entry(ep->queue.next, struct s3c_request, queue); > + ep_tsr = readl(®->out_endp[ep_num].doeptsiz); > + > + if (ep_num == EP0_CON) > + xfer_size = (ep_tsr & 0x7f); > + else > + xfer_size = (ep_tsr & 0x7fff); > + > + xfer_size = ep->len - xfer_size; > + > + invalidate_dcache_range((unsigned long) p, > + (unsigned long) p + DMA_BUFFER_SIZE); > + > + memcpy(ep->dma_buf, p, ep->len); > + > + req->req.actual += min(xfer_size, req->req.length - req->req.actual); > + is_short = (xfer_size < ep->ep.maxpacket); > + > + DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, " > + "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", > + __func__, ep_num, req->req.actual, req->req.length, > + is_short, ep_tsr, xfer_size); > + > + if (is_short || req->req.actual == req->req.length) { > + if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { > + DEBUG_OUT_EP(" => Send ZLP\n"); > + s3c_udc_ep0_zlp(dev); > + /* packet will be completed in complete_tx() */ > + dev->ep0state = WAIT_FOR_IN_COMPLETE; > + } else { > + done(ep, req, 0); > + > + if (!list_empty(&ep->queue)) { > + req = list_entry(ep->queue.next, > + struct s3c_request, queue); > + DEBUG_OUT_EP("%s: Next Rx request start...\n", > + __func__); > + setdma_rx(ep, req); > + } > + } > + } else > + setdma_rx(ep, req); > +} > + > +static void complete_tx(struct s3c_udc *dev, u8 ep_num) > +{ > + struct s3c_ep *ep = &dev->ep[ep_num]; > + struct s3c_request *req; > + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; > + u32 last; > + > + if (dev->ep0state == WAIT_FOR_NULL_COMPLETE) { > + dev->ep0state = WAIT_FOR_OUT_COMPLETE; > + s3c_ep0_complete_out(); > + return; > + } > + > + if (list_empty(&ep->queue)) { > + DEBUG_IN_EP("%s: TX DMA done : NULL REQ on IN EP-%d\n", > + __func__, ep_num); > + return; > + > + } > + > + req = list_entry(ep->queue.next, struct s3c_request, queue); > + > + ep_tsr = readl(®->in_endp[ep_num].dieptsiz); > + > + xfer_size = ep->len; > + is_short = (xfer_size < ep->ep.maxpacket); > + req->req.actual += min(xfer_size, req->req.length - req->req.actual); > + > + DEBUG_IN_EP("%s: TX DMA done : ep = %d, tx bytes = %d/%d, " > + "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n", > + __func__, ep_num, req->req.actual, req->req.length, > + is_short, ep_tsr, xfer_size); > + > + if (ep_num == 0) { > + if (dev->ep0state == DATA_STATE_XMIT) { > + DEBUG_IN_EP("%s: ep_num = %d, ep0stat ==" > + "DATA_STATE_XMIT\n", > + __func__, ep_num); > + last = write_fifo_ep0(ep, req); > + if (last) > + dev->ep0state = WAIT_FOR_COMPLETE; > + } else if (dev->ep0state == WAIT_FOR_IN_COMPLETE) { > + DEBUG_IN_EP("%s: ep_num = %d, completing request\n", > + __func__, ep_num); > + done(ep, req, 0); > + dev->ep0state = WAIT_FOR_SETUP; > + } else if (dev->ep0state == WAIT_FOR_COMPLETE) { > + DEBUG_IN_EP("%s: ep_num = %d, completing request\n", > + __func__, ep_num); > + done(ep, req, 0); > + dev->ep0state = WAIT_FOR_OUT_COMPLETE; > + s3c_ep0_complete_out(); > + } else { > + DEBUG_IN_EP("%s: ep_num = %d, invalid ep state\n", > + __func__, ep_num); > + } > + return; > + } > + > + if (req->req.actual == req->req.length) > + done(ep, req, 0); > + > + if (!list_empty(&ep->queue)) { > + req = list_entry(ep->queue.next, struct s3c_request, queue); > + DEBUG_IN_EP("%s: Next Tx request start...\n", __func__); > + setdma_tx(ep, req); > + } > +} > + > +static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num) > +{ > + struct s3c_ep *ep = &dev->ep[ep_num]; > + struct s3c_request *req; > + > + DEBUG_IN_EP("%s: Check queue, ep_num = %d\n", __func__, ep_num); > + > + if (!list_empty(&ep->queue)) { > + req = list_entry(ep->queue.next, struct s3c_request, queue); > + DEBUG_IN_EP("%s: Next Tx request(0x%p) start...\n", > + __func__, req); > + > + if (ep_is_in(ep)) > + setdma_tx(ep, req); > + else > + setdma_rx(ep, req); > + } else { > + DEBUG_IN_EP("%s: NULL REQ on IN EP-%d\n", __func__, ep_num); > + > + return; > + } > + > +} > + > +static void process_ep_in_intr(struct s3c_udc *dev) > +{ > + u32 ep_intr, ep_intr_status; > + u8 ep_num = 0; > + > + ep_intr = readl(®->daint); > + DEBUG_IN_EP("*** %s: EP In interrupt : DAINT = 0x%x\n", > + __func__, ep_intr); > + > + ep_intr &= DAINT_MASK; > + > + while (ep_intr) { > + if (ep_intr & 0x1) { > + ep_intr_status = readl(®->in_endp[ep_num].diepint); > + DEBUG_IN_EP("\tEP%d-IN : DIEPINT = 0x%x\n", > + ep_num, ep_intr_status); > + > + /* Interrupt Clear */ > + writel(ep_intr_status, ®->in_endp[ep_num].diepint); > + > + if (ep_intr_status & TRANSFER_DONE) { > + complete_tx(dev, ep_num); > + > + if (ep_num == 0) { > + if (dev->ep0state == > + WAIT_FOR_IN_COMPLETE) > + dev->ep0state = WAIT_FOR_SETUP; > + > + if (dev->ep0state == WAIT_FOR_SETUP) > + s3c_udc_pre_setup(); > + > + /* continue transfer after > + set_clear_halt for DMA mode */ > + if (clear_feature_flag == 1) { > + s3c_udc_check_tx_queue(dev, > + clear_feature_num); > + clear_feature_flag = 0; > + } > + } > + } > + } > + ep_num++; > + ep_intr >>= 1; > + } > +} > + > +static void process_ep_out_intr(struct s3c_udc *dev) > +{ > + u32 ep_intr, ep_intr_status; > + u8 ep_num = 0; > + > + ep_intr = readl(®->daint); > + DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n", > + __func__, ep_intr); > + > + ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK; > + > + while (ep_intr) { > + if (ep_intr & 0x1) { > + ep_intr_status = readl(®->out_endp[ep_num].doepint); > + DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n", > + ep_num, ep_intr_status); > + > + /* Interrupt Clear */ > + writel(ep_intr_status, ®->out_endp[ep_num].doepint); > + > + if (ep_num == 0) { > + if (ep_intr_status & TRANSFER_DONE) { > + if (dev->ep0state != > + WAIT_FOR_OUT_COMPLETE) > + complete_rx(dev, ep_num); > + else { > + dev->ep0state = WAIT_FOR_SETUP; > + s3c_udc_pre_setup(); > + } > + } > + > + if (ep_intr_status & > + CTRL_OUT_EP_SETUP_PHASE_DONE) { > + DEBUG_OUT_EP("SETUP packet arrived\n"); > + s3c_handle_ep0(dev); > + } > + } else { > + if (ep_intr_status & TRANSFER_DONE) > + complete_rx(dev, ep_num); > + } > + } > + ep_num++; > + ep_intr >>= 1; > + } > +} > + > +/* > + * usb client interrupt handler. > + */ > +static int s3c_udc_irq(int irq, void *_dev) > +{ > + struct s3c_udc *dev = _dev; > + u32 intr_status; > + u32 usb_status, gintmsk; > + unsigned long flags; > + > + spin_lock_irqsave(&dev->lock, flags); > + > + intr_status = readl(®->gintsts); > + gintmsk = readl(®->gintmsk); > + > + DEBUG_ISR("\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x," > + "DAINT : 0x%x, DAINTMSK : 0x%x\n", > + __func__, intr_status, state_names[dev->ep0state], gintmsk, > + readl(®->daint), readl(®->daintmsk)); > + > + if (!intr_status) { > + spin_unlock_irqrestore(&dev->lock, flags); > + return IRQ_HANDLED; > + } > + > + if (intr_status & INT_ENUMDONE) { > + DEBUG_ISR("\tSpeed Detection interrupt\n"); > + > + writel(INT_ENUMDONE, ®->gintsts); > + usb_status = (readl(®->dsts) & 0x6); > + > + if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) { > + DEBUG_ISR("\t\tFull Speed Detection\n"); > + set_max_pktsize(dev, USB_SPEED_FULL); > + > + } else { > + DEBUG_ISR("\t\tHigh Speed Detection : 0x%x\n", > + usb_status); > + set_max_pktsize(dev, USB_SPEED_HIGH); > + } > + } > + > + if (intr_status & INT_EARLY_SUSPEND) { > + DEBUG_ISR("\tEarly suspend interrupt\n"); > + writel(INT_EARLY_SUSPEND, ®->gintsts); > + } > + > + if (intr_status & INT_SUSPEND) { > + usb_status = readl(®->dsts); > + DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status); > + writel(INT_SUSPEND, ®->gintsts); > + > + if (dev->gadget.speed != USB_SPEED_UNKNOWN > + && dev->driver) { > + if (dev->driver->suspend) > + dev->driver->suspend(&dev->gadget); > + > + /* HACK to let gadget detect disconnected state */ > + if (dev->driver->disconnect) { > + spin_unlock_irqrestore(&dev->lock, flags); > + dev->driver->disconnect(&dev->gadget); > + spin_lock_irqsave(&dev->lock, flags); > + } > + } > + } > + > + if (intr_status & INT_RESUME) { > + DEBUG_ISR("\tResume interrupt\n"); > + writel(INT_RESUME, ®->gintsts); > + > + if (dev->gadget.speed != USB_SPEED_UNKNOWN > + && dev->driver > + && dev->driver->resume) { > + > + dev->driver->resume(&dev->gadget); > + } > + } > + > + if (intr_status & INT_RESET) { > + usb_status = readl(®->gotgctl); > + DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status); > + writel(INT_RESET, ®->gintsts); > + > + if ((usb_status & 0xc0000) == (0x3 << 18)) { > + if (reset_available) { > + DEBUG_ISR("\t\tOTG core got reset (%d)!!\n", > + reset_available); > + reconfig_usbd(); > + dev->ep0state = WAIT_FOR_SETUP; > + reset_available = 0; > + s3c_udc_pre_setup(); > + } else > + reset_available = 1; > + > + } else { > + reset_available = 1; > + DEBUG_ISR("\t\tRESET handling skipped\n"); > + } > + } > + > + if (intr_status & INT_IN_EP) > + process_ep_in_intr(dev); > + > + if (intr_status & INT_OUT_EP) > + process_ep_out_intr(dev); > + > + spin_unlock_irqrestore(&dev->lock, flags); > + > + return IRQ_HANDLED; > +} > + > +/** Queue one request > + * Kickstart transfer if needed > + */ > +static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req, > + gfp_t gfp_flags) > +{ > + struct s3c_request *req; > + struct s3c_ep *ep; > + struct s3c_udc *dev; > + unsigned long flags; > + u32 ep_num, gintsts; > + > + req = container_of(_req, struct s3c_request, req); > + if (unlikely(!_req || !_req->complete || !_req->buf > + || !list_empty(&req->queue))) { > + > + DEBUG("%s: bad params\n", __func__); > + return -EINVAL; > + } > + > + ep = container_of(_ep, struct s3c_ep, ep); > + > + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { > + > + DEBUG("%s: bad ep: %s, %d, %x\n", __func__, > + ep->ep.name, !ep->desc, _ep); > + return -EINVAL; > + } > + > + ep_num = ep_index(ep); > + dev = ep->dev; > + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { > + > + DEBUG("%s: bogus device state %p\n", __func__, dev->driver); > + return -ESHUTDOWN; > + } > + > + spin_lock_irqsave(&dev->lock, flags); > + > + _req->status = -EINPROGRESS; > + _req->actual = 0; > + > + /* kickstart this i/o queue? */ > + DEBUG("\n*** %s: %s-%s req = %p, len = %d, buf = %p" > + "Q empty = %d, stopped = %d\n", > + __func__, _ep->name, ep_is_in(ep) ? "in" : "out", > + _req, _req->length, _req->buf, > + list_empty(&ep->queue), ep->stopped); > + > +#ifdef DEBUG_S3C_UDC > + { > + int i, len = _req->length; > + > + printf("pkt = "); > + if (len > 64) > + len = 64; > + for (i = 0; i < len; i++) { > + printf("%02x", ((u8 *)_req->buf)[i]); > + if ((i & 7) == 7) > + printf(" "); > + } > + printf("\n"); > + } > +#endif > + > + if (list_empty(&ep->queue) && !ep->stopped) { > + > + if (ep_num == 0) { > + /* EP0 */ > + list_add_tail(&req->queue, &ep->queue); > + s3c_ep0_kick(dev, ep); > + req = 0; > + > + } else if (ep_is_in(ep)) { > + gintsts = readl(®->gintsts); > + DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", > + __func__, gintsts); > + > + setdma_tx(ep, req); > + } else { > + gintsts = readl(®->gintsts); > + DEBUG_OUT_EP("%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n", > + __func__, gintsts); > + > + setdma_rx(ep, req); > + } > + } > + > + /* pio or dma irq handler advances the queue. */ > + if (likely(req != 0)) > + list_add_tail(&req->queue, &ep->queue); > + > + spin_unlock_irqrestore(&dev->lock, flags); > + > + return 0; > +} > + > +/****************************************************************/ > +/* End Point 0 related functions */ > +/****************************************************************/ > + > +/* return: 0 = still running, 1 = completed, negative = errno */ > +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req) > +{ > + u32 max; > + unsigned count; > + int is_last; > + > + max = ep_maxpacket(ep); > + > + DEBUG_EP0("%s: max = %d\n", __func__, max); > + > + count = setdma_tx(ep, req); > + > + /* last packet is usually short (or a zlp) */ > + if (likely(count != max)) > + is_last = 1; > + else { > + if (likely(req->req.length != req->req.actual + count) > + || req->req.zero) > + is_last = 0; > + else > + is_last = 1; > + } > + > + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__, > + ep->ep.name, count, > + is_last ? "/L" : "", > + req->req.length - req->req.actual - count, req); > + > + /* requests complete when all IN data is in the FIFO */ > + if (is_last) { > + ep->dev->ep0state = WAIT_FOR_SETUP; > + return 1; > + } > + > + return 0; > +} > + > +int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max) > +{ > + u32 bytes; > + > + bytes = sizeof(struct usb_ctrlrequest); > + > + invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_index(ep)], > + (unsigned long) ep->dev->dma_buf[ep_index(ep)] > + + DMA_BUFFER_SIZE); > + > + DEBUG_EP0("%s: bytes=%d, ep_index=%d %p\n", __func__, > + bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]); > + > + return bytes; > +} > + > +/** > + * udc_set_address - set the USB address for this device > + * @address: > + * > + * Called from control endpoint function > + * after it decodes a set address setup packet. > + */ > +static void udc_set_address(struct s3c_udc *dev, unsigned char address) > +{ > + u32 ctrl = readl(®->dcfg); > + writel(address << 4 | ctrl, ®->dcfg); > + > + s3c_udc_ep0_zlp(dev); > + > + DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", > + __func__, address, readl(®->dcfg)); > + > + dev->usb_address = address; > +} > + > +static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep) > +{ > + struct s3c_udc *dev; > + u32 ep_ctrl = 0; > + > + dev = ep->dev; > + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); > + > + /* set the disable and stall bits */ > + if (ep_ctrl & DEPCTL_EPENA) > + ep_ctrl |= DEPCTL_EPDIS; > + > + ep_ctrl |= DEPCTL_STALL; > + > + writel(ep_ctrl, ®->in_endp[EP0_CON].diepctl); > + > + DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n", > + __func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); > + /* > + * The application can only set this bit, and the core clears it, > + * when a SETUP token is received for this endpoint > + */ > + dev->ep0state = WAIT_FOR_SETUP; > + > + s3c_udc_pre_setup(); > +} > + > +static void s3c_ep0_read(struct s3c_udc *dev) > +{ > + struct s3c_request *req; > + struct s3c_ep *ep = &dev->ep[0]; > + int ret; > + > + if (!list_empty(&ep->queue)) { > + req = list_entry(ep->queue.next, struct s3c_request, queue); > + > + } else { > + DEBUG("%s: ---> BUG\n", __func__); > + BUG(); > + return; > + } > + > + DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", > + __func__, req, req->req.length, req->req.actual); > + > + if (req->req.length == 0) { > + /* zlp for Set_configuration, Set_interface, > + * or Bulk-Only mass storge reset */ > + > + ep->len = 0; > + s3c_udc_ep0_zlp(dev); > + > + DEBUG_EP0("%s: req.length = 0, bRequest = %d\n", > + __func__, usb_ctrl->bRequest); > + return; > + } > + > + ret = setdma_rx(ep, req); > +} > + > +/* > + * DATA_STATE_XMIT > + */ > +static int s3c_ep0_write(struct s3c_udc *dev) > +{ > + struct s3c_request *req; > + struct s3c_ep *ep = &dev->ep[0]; > + int ret, need_zlp = 0; > + > + if (list_empty(&ep->queue)) > + req = 0; > + else > + req = list_entry(ep->queue.next, struct s3c_request, queue); > + > + if (!req) { > + DEBUG_EP0("%s: NULL REQ\n", __func__); > + return 0; > + } > + > + DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", > + __func__, req, req->req.length, req->req.actual); > + > + if (req->req.length - req->req.actual == ep0_fifo_size) { > + /* Next write will end with the packet size, */ > + /* so we need Zero-length-packet */ > + need_zlp = 1; > + } > + > + ret = write_fifo_ep0(ep, req); > + > + if ((ret == 1) && !need_zlp) { > + /* Last packet */ > + dev->ep0state = WAIT_FOR_COMPLETE; > + DEBUG_EP0("%s: finished, waiting for status\n", __func__); > + > + } else { > + dev->ep0state = DATA_STATE_XMIT; > + DEBUG_EP0("%s: not finished\n", __func__); > + } > + > + return 1; > +} > + > +u16 g_status; > + > +int s3c_udc_get_status(struct s3c_udc *dev, > + struct usb_ctrlrequest *crq) > +{ > + u8 ep_num = crq->wIndex & 0x7F; > + u32 ep_ctrl; > + u32 *p = the_controller->dma_buf[1]; > + > + DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__); > + printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK); > + switch (crq->bRequestType & USB_RECIP_MASK) { > + case USB_RECIP_INTERFACE: > + g_status = 0; > + DEBUG_SETUP("\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n", > + g_status); > + break; > + > + case USB_RECIP_DEVICE: > + g_status = 0x1; /* Self powered */ > + DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", > + g_status); > + break; > + > + case USB_RECIP_ENDPOINT: > + if (crq->wLength > 2) { > + DEBUG_SETUP("\tGET_STATUS:Not support EP or wLength\n"); > + return 1; > + } > + > + g_status = dev->ep[ep_num].stopped; > + DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", > + g_status); > + > + break; > + > + default: > + return 1; > + } > + > + memcpy(p, &g_status, sizeof(g_status)); > + > + flush_dcache_range((unsigned long) p, > + (unsigned long) p + DMA_BUFFER_SIZE); > + > + writel(the_controller->dma_addr[1], ®->in_endp[EP0_CON].diepdma); > + writel((1<<19)|(2<<0), ®->in_endp[EP0_CON].dieptsiz); > + > + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); > + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, > + ®->in_endp[EP0_CON].diepctl); > + dev->ep0state = WAIT_FOR_NULL_COMPLETE; > + > + return 0; > +} > + > +static void s3c_udc_set_nak(struct s3c_ep *ep) > +{ > + u8 ep_num; > + u32 ep_ctrl = 0; > + > + ep_num = ep_index(ep); > + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); > + > + if (ep_is_in(ep)) { > + ep_ctrl = readl(®->in_endp[ep_num].diepctl); > + ep_ctrl |= DEPCTL_SNAK; > + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); > + DEBUG("%s: set NAK, DIEPCTL%d = 0x%x\n", > + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); > + } else { > + ep_ctrl = readl(®->out_endp[ep_num].doepctl); > + ep_ctrl |= DEPCTL_SNAK; > + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); > + DEBUG("%s: set NAK, DOEPCTL%d = 0x%x\n", > + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); > + } > + > + return; > +} > + > + > +void s3c_udc_ep_set_stall(struct s3c_ep *ep) > +{ > + u8 ep_num; > + u32 ep_ctrl = 0; > + > + ep_num = ep_index(ep); > + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); > + > + if (ep_is_in(ep)) { > + ep_ctrl = readl(®->in_endp[ep_num].diepctl); > + > + /* set the disable and stall bits */ > + if (ep_ctrl & DEPCTL_EPENA) > + ep_ctrl |= DEPCTL_EPDIS; > + > + ep_ctrl |= DEPCTL_STALL; > + > + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); > + DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n", > + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); > + > + } else { > + ep_ctrl = readl(®->out_endp[ep_num].doepctl); > + > + /* set the stall bit */ > + ep_ctrl |= DEPCTL_STALL; > + > + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); > + DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n", > + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); > + } > + > + return; > +} > + > +void s3c_udc_ep_clear_stall(struct s3c_ep *ep) > +{ > + u8 ep_num; > + u32 ep_ctrl = 0; > + > + ep_num = ep_index(ep); > + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); > + > + if (ep_is_in(ep)) { > + ep_ctrl = readl(®->in_endp[ep_num].diepctl); > + > + /* clear stall bit */ > + ep_ctrl &= ~DEPCTL_STALL; > + > + /* > + * USB Spec 9.4.5: For endpoints using data toggle, regardless > + * of whether an endpoint has the Halt feature set, a > + * ClearFeature(ENDPOINT_HALT) request always results in the > + * data toggle being reinitialized to DATA0. > + */ > + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT > + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { > + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ > + } > + > + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); > + DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n", > + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); > + > + } else { > + ep_ctrl = readl(®->out_endp[ep_num].doepctl); > + > + /* clear stall bit */ > + ep_ctrl &= ~DEPCTL_STALL; > + > + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT > + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { > + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ > + } > + > + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); > + DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n", > + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); > + } > + > + return; > +} > + > +static int s3c_udc_set_halt(struct usb_ep *_ep, int value) > +{ > + struct s3c_ep *ep; > + struct s3c_udc *dev; > + unsigned long flags; > + u8 ep_num; > + > + ep = container_of(_ep, struct s3c_ep, ep); > + ep_num = ep_index(ep); > + > + if (unlikely(!_ep || !ep->desc || ep_num == EP0_CON || > + ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) { > + DEBUG("%s: %s bad ep or descriptor\n", __func__, ep->ep.name); > + return -EINVAL; > + } > + > + /* Attempt to halt IN ep will fail if any transfer requests > + * are still queue */ > + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { > + DEBUG("%s: %s queue not empty, req = %p\n", > + __func__, ep->ep.name, > + list_entry(ep->queue.next, struct s3c_request, queue)); > + > + return -EAGAIN; > + } > + > + dev = ep->dev; > + DEBUG("%s: ep_num = %d, value = %d\n", __func__, ep_num, value); > + > + spin_lock_irqsave(&dev->lock, flags); > + > + if (value == 0) { > + ep->stopped = 0; > + s3c_udc_ep_clear_stall(ep); > + } else { > + if (ep_num == 0) > + dev->ep0state = WAIT_FOR_SETUP; > + > + ep->stopped = 1; > + s3c_udc_ep_set_stall(ep); > + } > + > + spin_unlock_irqrestore(&dev->lock, flags); > + > + return 0; > +} > + > +void s3c_udc_ep_activate(struct s3c_ep *ep) > +{ > + u8 ep_num; > + u32 ep_ctrl = 0, daintmsk = 0; > + > + ep_num = ep_index(ep); > + > + /* Read DEPCTLn register */ > + if (ep_is_in(ep)) { > + ep_ctrl = readl(®->in_endp[ep_num].diepctl); > + daintmsk = 1 << ep_num; > + } else { > + ep_ctrl = readl(®->out_endp[ep_num].doepctl); > + daintmsk = (1 << ep_num) << DAINT_OUT_BIT; > + } > + > + DEBUG("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n", > + __func__, ep_num, ep_ctrl, ep_is_in(ep)); > + > + /* If the EP is already active don't change the EP Control > + * register. */ > + if (!(ep_ctrl & DEPCTL_USBACTEP)) { > + ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) | > + (ep->bmAttributes << DEPCTL_TYPE_BIT); > + ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | > + (ep->ep.maxpacket << DEPCTL_MPS_BIT); > + ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK); > + > + if (ep_is_in(ep)) { > + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); > + DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n", > + __func__, ep_num, ep_num, > + readl(®->in_endp[ep_num].diepctl)); > + } else { > + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); > + DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n", > + __func__, ep_num, ep_num, > + readl(®->out_endp[ep_num].doepctl)); > + } > + } > + > + /* Unmask EP Interrtupt */ > + writel(readl(®->daintmsk)|daintmsk, ®->daintmsk); > + DEBUG("%s: DAINTMSK = 0x%x\n", __func__, readl(®->daintmsk)); > + > +} > + > +static int s3c_udc_clear_feature(struct usb_ep *_ep) > +{ > + struct s3c_udc *dev; > + struct s3c_ep *ep; > + u8 ep_num; > + > + ep = container_of(_ep, struct s3c_ep, ep); > + ep_num = ep_index(ep); > + > + dev = ep->dev; > + DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n", > + __func__, ep_num, ep_is_in(ep), clear_feature_flag); > + > + if (usb_ctrl->wLength != 0) { > + DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n"); > + return 1; > + } > + > + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { > + case USB_RECIP_DEVICE: > + switch (usb_ctrl->wValue) { > + case USB_DEVICE_REMOTE_WAKEUP: > + DEBUG_SETUP("\tOFF:USB_DEVICE_REMOTE_WAKEUP\n"); > + break; > + > + case USB_DEVICE_TEST_MODE: > + DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n"); > + /** @todo Add CLEAR_FEATURE for TEST modes. */ > + break; > + } > + > + s3c_udc_ep0_zlp(dev); > + break; > + > + case USB_RECIP_ENDPOINT: > + DEBUG_SETUP("\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n", > + usb_ctrl->wValue); > + > + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { > + if (ep_num == 0) { > + s3c_udc_ep0_set_stall(ep); > + return 0; > + } > + > + s3c_udc_ep0_zlp(dev); > + > + s3c_udc_ep_clear_stall(ep); > + s3c_udc_ep_activate(ep); > + ep->stopped = 0; > + > + clear_feature_num = ep_num; > + clear_feature_flag = 1; > + } > + break; > + } > + > + return 0; > +} > + > +static int s3c_udc_set_feature(struct usb_ep *_ep) > +{ > + struct s3c_udc *dev; > + struct s3c_ep *ep; > + u8 ep_num; > + > + ep = container_of(_ep, struct s3c_ep, ep); > + ep_num = ep_index(ep); > + dev = ep->dev; > + > + DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", > + __func__, ep_num); > + > + if (usb_ctrl->wLength != 0) { > + DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n"); > + return 1; > + } > + > + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { > + case USB_RECIP_DEVICE: > + switch (usb_ctrl->wValue) { > + case USB_DEVICE_REMOTE_WAKEUP: > + DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n"); > + break; > + case USB_DEVICE_B_HNP_ENABLE: > + DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); > + break; > + > + case USB_DEVICE_A_HNP_SUPPORT: > + /* RH port supports HNP */ > + DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n"); > + break; > + > + case USB_DEVICE_A_ALT_HNP_SUPPORT: > + /* other RH port does */ > + DEBUG_SETUP("\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); > + break; > + } > + > + s3c_udc_ep0_zlp(dev); > + return 0; > + > + case USB_RECIP_INTERFACE: > + DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n"); > + break; > + > + case USB_RECIP_ENDPOINT: > + DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n"); > + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { > + if (ep_num == 0) { > + s3c_udc_ep0_set_stall(ep); > + return 0; > + } > + ep->stopped = 1; > + s3c_udc_ep_set_stall(ep); > + } > + > + s3c_udc_ep0_zlp(dev); > + return 0; > + } > + > + return 1; > +} > + > +/* > + * WAIT_FOR_SETUP (OUT_PKT_RDY) > + */ > +void s3c_ep0_setup(struct s3c_udc *dev) > +{ > + struct s3c_ep *ep = &dev->ep[0]; > + int i, bytes, is_in; > + u8 ep_num; > + > + /* Nuke all previous transfers */ > + nuke(ep, -EPROTO); > + > + /* read control req from fifo (8 bytes) */ > + bytes = s3c_fifo_read(ep, (u32 *)usb_ctrl, 8); > + > + DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x" > + "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n", > + __func__, usb_ctrl->bRequestType, > + (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", > + usb_ctrl->bRequest, > + usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); > + > +#ifdef DEBUG_S3C_UDC > + { > + int i, len = sizeof(*usb_ctrl); > + char *p = usb_ctrl; > + > + printf("pkt = "); > + for (i = 0; i < len; i++) { > + printf("%02x", ((u8 *)p)[i]); > + if ((i & 7) == 7) > + printf(" "); > + } > + printf("\n"); > + } > +#endif > + > + > + if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && > + usb_ctrl->wLength != 1) { > + DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid", > + __func__); > + DEBUG_SETUP("wLength = %d, setup returned\n", > + usb_ctrl->wLength); > + > + s3c_udc_ep0_set_stall(ep); > + dev->ep0state = WAIT_FOR_SETUP; > + > + return; > + } else if (usb_ctrl->bRequest == BOT_RESET_REQUEST && > + usb_ctrl->wLength != 0) { > + /* Bulk-Only *mass storge reset of class-specific request */ > + DEBUG_SETUP("%s:BOT Rest:invalid wLength =%d, setup returned\n", > + __func__, usb_ctrl->wLength); > + > + s3c_udc_ep0_set_stall(ep); > + dev->ep0state = WAIT_FOR_SETUP; > + > + return; > + } > + > + /* Set direction of EP0 */ > + if (likely(usb_ctrl->bRequestType & USB_DIR_IN)) { > + ep->bEndpointAddress |= USB_DIR_IN; > + is_in = 1; > + > + } else { > + ep->bEndpointAddress &= ~USB_DIR_IN; > + is_in = 0; > + } > + /* cope with automagic for some standard requests. */ > + dev->req_std = (usb_ctrl->bRequestType & USB_TYPE_MASK) > + == USB_TYPE_STANDARD; > + dev->req_config = 0; > + dev->req_pending = 1; > + > + /* Handle some SETUP packets ourselves */ > + if (dev->req_std) { > + switch (usb_ctrl->bRequest) { > + case USB_REQ_SET_ADDRESS: > + DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n", > + __func__, usb_ctrl->wValue); > + if (usb_ctrl->bRequestType > + != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) > + break; > + > + udc_set_address(dev, usb_ctrl->wValue); > + return; > + > + case USB_REQ_SET_CONFIGURATION: > + DEBUG_SETUP("=====================================\n"); > + DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n", > + __func__, usb_ctrl->wValue); > + > + if (usb_ctrl->bRequestType == USB_RECIP_DEVICE) { > + reset_available = 1; > + dev->req_config = 1; > + } > + break; > + > + case USB_REQ_GET_DESCRIPTOR: > + DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n", > + __func__); > + break; > + > + case USB_REQ_SET_INTERFACE: > + DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n", > + __func__, usb_ctrl->wValue); > + > + if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE) { > + reset_available = 1; > + dev->req_config = 1; > + } > + break; > + > + case USB_REQ_GET_CONFIGURATION: > + DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n", > + __func__); > + break; > + > + case USB_REQ_GET_STATUS: > + if (!s3c_udc_get_status(dev, usb_ctrl)) > + return; > + > + break; > + > + case USB_REQ_CLEAR_FEATURE: > + ep_num = usb_ctrl->wIndex & 0x7f; > + > + if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep)) > + return; > + > + break; > + > + case USB_REQ_SET_FEATURE: > + ep_num = usb_ctrl->wIndex & 0x7f; > + > + if (!s3c_udc_set_feature(&dev->ep[ep_num].ep)) > + return; > + > + break; > + > + default: > + DEBUG_SETUP("%s: *** Default of usb_ctrl->bRequest=0x%x" > + "happened.\n", __func__, usb_ctrl->bRequest); > + break; > + } > + } > + > + > + if (likely(dev->driver)) { > + /* device-2-host (IN) or no data setup command, > + * process immediately */ > + DEBUG_SETUP("%s:usb_ctrlreq will be passed to fsg_setup()\n", > + __func__); > + > + spin_unlock(&dev->lock); > + i = dev->driver->setup(&dev->gadget, usb_ctrl); > + spin_lock(&dev->lock); > + > + if (i < 0) { > + if (dev->req_config) { > + DEBUG_SETUP("\tconfig change 0x%02x fail %d?\n", > + (u32)usb_ctrl->bRequest, i); > + return; > + } > + > + /* setup processing failed, force stall */ > + s3c_udc_ep0_set_stall(ep); > + dev->ep0state = WAIT_FOR_SETUP; > + > + DEBUG_SETUP("\tdev->driver->setup failed (%d)," > + " bRequest = %d\n", > + i, usb_ctrl->bRequest); > + > + > + } else if (dev->req_pending) { > + dev->req_pending = 0; > + DEBUG_SETUP("\tdev->req_pending...\n"); > + } > + > + DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]); > + > + } > +} > + > +/* > + * handle ep0 interrupt > + */ > +static void s3c_handle_ep0(struct s3c_udc *dev) > +{ > + if (dev->ep0state == WAIT_FOR_SETUP) { > + DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__); > + s3c_ep0_setup(dev); > + > + } else { > + DEBUG_OUT_EP("%s: strange state!!(state = %s)\n", > + __func__, state_names[dev->ep0state]); > + } > +} > + > +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep) > +{ > + DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep)); > + if (ep_is_in(ep)) { > + dev->ep0state = DATA_STATE_XMIT; > + s3c_ep0_write(dev); > + > + } else { > + dev->ep0state = DATA_STATE_RECV; > + s3c_ep0_read(dev); > + } > +} > diff --git a/include/usb/lin_gadget_compat.h b/include/usb/lin_gadget_compat.h > new file mode 100644 > index 0000000..9b31511 > --- /dev/null > +++ b/include/usb/lin_gadget_compat.h > @@ -0,0 +1,62 @@ > +/* > + * Copyright (c) 2011 Samsung Electronics > + * Lukasz Majewski <l.majewski@samsung.com> > + * > + * This is a Linux kernel compatibility layer for USB Gadget > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation; either version 2 of > + * the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, > + * MA 02111-1307 USA > + */ > + > +#ifndef __LIN_COMPAT_H__ > +#define __LIN_COMPAT_H__ > + > +/* common */ > +#define spin_lock_init(...) > +#define spin_lock(...) > +#define spin_lock_irqsave(lock, flags) do {flags = 1; } while (0) > +#define spin_unlock(...) > +#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0) > +#define disable_irq(...) > +#define enable_irq(...) > + > +#define mutex_init(...) > +#define mutex_lock(...) > +#define mutex_unlock(...) > + > +#define WARN_ON(x) if (x) {printf("WARNING in %s line %d\n" \ > + , __FILE__, __LINE__); } > + > +#define KERN_WARNING > +#define KERN_ERR > +#define KERN_NOTICE > +#define KERN_DEBUG > + > +#define GFP_KERNEL 0 > + > +#define IRQ_HANDLED 1 > + > +#define ENOTSUPP 524 /* Operation is not supported */ > + > +#define kmalloc(size, type) memalign(CONFIG_SYS_CACHELINE_SIZE, size) > +#define kfree(addr) free(addr) > +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) > + > +#define __iomem > +#define min_t min > +#define dma_cache_maint(addr, size, mode) cache_flush() > +void cache_flush(void); > + > +#endif /* __LIN_COMPAT_H__ */ > diff --git a/include/usb/s3c_udc.h b/include/usb/s3c_udc.h > new file mode 100644 > index 0000000..5537c4a > --- /dev/null > +++ b/include/usb/s3c_udc.h > @@ -0,0 +1,165 @@ > +/* > + * drivers/usb/gadget/s3c_udc.h > + * Samsung S3C on-chip full/high speed USB device controllers > + * Copyright (C) 2005 for Samsung Electronics > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + */ > + > +#ifndef __S3C_USB_GADGET > +#define __S3C_USB_GADGET > + > +#include <asm/errno.h> > +#include <linux/usb/ch9.h> > +#include <linux/usb/gadget.h> > +#include <linux/list.h> > +#include <usb/lin_gadget_compat.h> > + > +/*-------------------------------------------------------------------------*/ > +/* DMA bounce buffer size, 16K is enough even for mass storage */ > +#define DMA_BUFFER_SIZE (4096*4) > + > +#define EP0_FIFO_SIZE 64 > +#define EP_FIFO_SIZE 512 > +#define EP_FIFO_SIZE2 1024 > +/* ep0-control, ep1in-bulk, ep2out-bulk, ep3in-int */ > +#define S3C_MAX_ENDPOINTS 4 > +#define S3C_MAX_HW_ENDPOINTS 16 > + > +#define WAIT_FOR_SETUP 0 > +#define DATA_STATE_XMIT 1 > +#define DATA_STATE_NEED_ZLP 2 > +#define WAIT_FOR_OUT_STATUS 3 > +#define DATA_STATE_RECV 4 > +#define WAIT_FOR_COMPLETE 5 > +#define WAIT_FOR_OUT_COMPLETE 6 > +#define WAIT_FOR_IN_COMPLETE 7 > +#define WAIT_FOR_NULL_COMPLETE 8 > + > +#define TEST_J_SEL 0x1 > +#define TEST_K_SEL 0x2 > +#define TEST_SE0_NAK_SEL 0x3 > +#define TEST_PACKET_SEL 0x4 > +#define TEST_FORCE_ENABLE_SEL 0x5 > + > +/* ************************************************************************* */ > +/* IO > + */ > + > +enum ep_type { > + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt > +}; > + > +struct s3c_ep { > + struct usb_ep ep; > + struct s3c_udc *dev; > + > + const struct usb_endpoint_descriptor *desc; > + struct list_head queue; > + unsigned long pio_irqs; > + int len; > + void *dma_buf; > + > + u8 stopped; > + u8 bEndpointAddress; > + u8 bmAttributes; > + > + enum ep_type ep_type; > + int fifo_num; > +}; > + > +struct s3c_request { > + struct usb_request req; > + struct list_head queue; > +}; > + > +struct s3c_udc { > + struct usb_gadget gadget; > + struct usb_gadget_driver *driver; > + > + struct s3c_plat_otg_data *pdata; > + > + void *dma_buf[S3C_MAX_ENDPOINTS+1]; > + dma_addr_t dma_addr[S3C_MAX_ENDPOINTS+1]; > + > + int ep0state; > + struct s3c_ep ep[S3C_MAX_ENDPOINTS]; > + > + unsigned char usb_address; > + > + unsigned req_pending:1, req_std:1, req_config:1; > +}; > + > +extern struct s3c_udc *the_controller; > + > +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN) > +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) > +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) > + > +/*-------------------------------------------------------------------------*/ > +/* #define DEBUG_UDC */ > +#ifdef DEBUG_UDC > +#define DBG(stuff...) printf("udc: " stuff) > +#else > +#define DBG(stuff...) do {} while (0) > +#endif > + > +#ifdef DEBUG_S3C_UDC_SETUP > +#define DEBUG_SETUP(fmt, args...) printk(fmt, ##args) > +#else > +#define DEBUG_SETUP(fmt, args...) do {} while (0) > +#endif > + > +#ifdef DEBUG_S3C_UDC_EP0 > +#define DEBUG_EP0(fmt, args...) printk(fmt, ##args) > +#else > +#define DEBUG_EP0(fmt, args...) do {} while (0) > +#endif > + > +#ifdef DEBUG_S3C_UDC > +#define DEBUG(fmt, args...) printk(fmt, ##args) > +#else > +#define DEBUG(fmt, args...) do {} while (0) > +#endif > + > +#ifdef DEBUG_S3C_UDC_ISR > +#define DEBUG_ISR(fmt, args...) printk(fmt, ##args) > +#else > +#define DEBUG_ISR(fmt, args...) do {} while (0) > +#endif > + > +#ifdef DEBUG_S3C_UDC_OUT_EP > +#define DEBUG_OUT_EP(fmt, args...) printk(fmt, ##args) > +#else > +#define DEBUG_OUT_EP(fmt, args...) do {} while (0) > +#endif > + > +#ifdef DEBUG_S3C_UDC_IN_EP > +#define DEBUG_IN_EP(fmt, args...) printk(fmt, ##args) > +#else > +#define DEBUG_IN_EP(fmt, args...) do {} while (0) > +#endif > + > +#define ERR(stuff...) printf("ERR udc: " stuff) > +#define WARN(stuff...) printf("WARNING udc: " stuff) > +#define INFO(stuff...) printf("INFO udc: " stuff) > + > +extern void otg_phy_init(void); > +extern void otg_phy_off(void); > + > +extern void s3c_udc_ep_set_stall(struct s3c_ep *ep); > +extern int s3c_udc_probe(struct s3c_plat_otg_data *pdata); > +#endif > -- > 1.7.2.3 > >
Patch
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 7d5b504..cd22bbe 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -26,6 +26,10 @@ include $(TOPDIR)/config.mk LIB := $(obj)libusb_gadget.o # new USB gadget layer dependencies +ifdef CONFIG_USB_GADGET +COBJS-y += epautoconf.o config.o usbstring.o +COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +endif ifdef CONFIG_USB_ETHER COBJS-y += ether.o epautoconf.o config.o usbstring.o COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o diff --git a/drivers/usb/gadget/regs-otg.h b/drivers/usb/gadget/regs-otg.h new file mode 100644 index 0000000..2c3ea73 --- /dev/null +++ b/drivers/usb/gadget/regs-otg.h @@ -0,0 +1,209 @@ +/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h + * + * Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at> + * + * Registers remapping: + * Lukasz Majewski <l.majewski@samsumg.com> + * + * This include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H +#define __ASM_ARCH_REGS_USB_OTG_HS_H + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_phy { + u32 phypwr; + u32 phyclk; + u32 rstcon; +}; + +/* Device Logical IN Endpoint-Specific Registers */ +struct s3c_dev_in_endp { + u32 diepctl; + u8 res1[4]; + u32 diepint; + u8 res2[4]; + u32 dieptsiz; + u32 diepdma; + u8 res3[4]; + u32 diepdmab; +}; + +/* Device Logical OUT Endpoint-Specific Registers */ +struct s3c_dev_out_endp { + u32 doepctl; + u8 res1[4]; + u32 doepint; + u8 res2[4]; + u32 doeptsiz; + u32 doepdma; + u8 res3[4]; + u32 doepdmab; +}; + +struct ep_fifo { + u32 fifo; + u8 res[4092]; +}; + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_reg { + /* Core Global Registers */ + u32 gotgctl; /* OTG Control & Status */ + u32 gotgint; /* OTG Interrupt */ + u32 gahbcfg; /* Core AHB Configuration */ + u32 gusbcfg; /* Core USB Configuration */ + u32 grstctl; /* Core Reset */ + u32 gintsts; /* Core Interrupt */ + u32 gintmsk; /* Core Interrupt Mask */ + u32 grxstsr; /* Receive Status Debug Read/Status Read */ + u32 grxstsp; /* Receive Status Debug Pop/Status Pop */ + u32 grxfsiz; /* Receive FIFO Size */ + u32 gnptxfsiz; /* Non-Periodic Transmit FIFO Size */ + u8 res1[216]; + u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */ + u8 res2[1728]; + /* Device Configuration */ + u32 dcfg; /* Device Configuration Register */ + u32 dctl; /* Device Control */ + u32 dsts; /* Device Status */ + u8 res3[4]; + u32 diepmsk; /* Device IN Endpoint Common Interrupt Mask */ + u32 doepmsk; /* Device OUT Endpoint Common Interrupt Mask */ + u32 daint; /* Device All Endpoints Interrupt */ + u32 daintmsk; /* Device All Endpoints Interrupt Mask */ + u8 res4[224]; + struct s3c_dev_in_endp in_endp[16]; + struct s3c_dev_out_endp out_endp[16]; + u8 res5[768]; + struct ep_fifo ep[16]; +}; + +/*===================================================================== */ +/*definitions related to CSR setting */ + +/* S3C_UDC_OTG_GOTGCTL */ +#define B_SESSION_VALID (0x1<<19) +#define A_SESSION_VALID (0x1<<18) + +/* S3C_UDC_OTG_GAHBCFG */ +#define PTXFE_HALF (0<<8) +#define PTXFE_ZERO (1<<8) +#define NPTXFE_HALF (0<<7) +#define NPTXFE_ZERO (1<<7) +#define MODE_SLAVE (0<<5) +#define MODE_DMA (1<<5) +#define BURST_SINGLE (0<<1) +#define BURST_INCR (1<<1) +#define BURST_INCR4 (3<<1) +#define BURST_INCR8 (5<<1) +#define BURST_INCR16 (7<<1) +#define GBL_INT_UNMASK (1<<0) +#define GBL_INT_MASK (0<<0) + +/* S3C_UDC_OTG_GRSTCTL */ +#define AHB_MASTER_IDLE (1u<<31) +#define CORE_SOFT_RESET (0x1<<0) + +/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */ +#define INT_RESUME (1u<<31) +#define INT_DISCONN (0x1<<29) +#define INT_CONN_ID_STS_CNG (0x1<<28) +#define INT_OUT_EP (0x1<<19) +#define INT_IN_EP (0x1<<18) +#define INT_ENUMDONE (0x1<<13) +#define INT_RESET (0x1<<12) +#define INT_SUSPEND (0x1<<11) +#define INT_EARLY_SUSPEND (0x1<<10) +#define INT_NP_TX_FIFO_EMPTY (0x1<<5) +#define INT_RX_FIFO_NOT_EMPTY (0x1<<4) +#define INT_SOF (0x1<<3) +#define INT_DEV_MODE (0x0<<0) +#define INT_HOST_MODE (0x1<<1) +#define INT_GOUTNakEff (0x01<<7) +#define INT_GINNakEff (0x01<<6) + +#define FULL_SPEED_CONTROL_PKT_SIZE 8 +#define FULL_SPEED_BULK_PKT_SIZE 64 + +#define HIGH_SPEED_CONTROL_PKT_SIZE 64 +#define HIGH_SPEED_BULK_PKT_SIZE 512 + +#define RX_FIFO_SIZE (1024*4) +#define NPTX_FIFO_SIZE (1024*4) +#define PTX_FIFO_SIZE (1536*1) + +#define DEPCTL_TXFNUM_0 (0x0<<22) +#define DEPCTL_TXFNUM_1 (0x1<<22) +#define DEPCTL_TXFNUM_2 (0x2<<22) +#define DEPCTL_TXFNUM_3 (0x3<<22) +#define DEPCTL_TXFNUM_4 (0x4<<22) + +/* Enumeration speed */ +#define USB_HIGH_30_60MHZ (0x0<<1) +#define USB_FULL_30_60MHZ (0x1<<1) +#define USB_LOW_6MHZ (0x2<<1) +#define USB_FULL_48MHZ (0x3<<1) + +/* S3C_UDC_OTG_GRXSTSP STATUS */ +#define OUT_PKT_RECEIVED (0x2<<17) +#define OUT_TRANSFER_COMPLELTED (0x3<<17) +#define SETUP_TRANSACTION_COMPLETED (0x4<<17) +#define SETUP_PKT_RECEIVED (0x6<<17) +#define GLOBAL_OUT_NAK (0x1<<17) + +/* S3C_UDC_OTG_DCTL device control register */ +#define NORMAL_OPERATION (0x1<<0) +#define SOFT_DISCONNECT (0x1<<1) + +/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */ +#define DAINT_OUT_BIT (16) +#define DAINT_MASK (0xFFFF) + +/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device + control IN/OUT endpoint 0 control register */ +#define DEPCTL_EPENA (0x1<<31) +#define DEPCTL_EPDIS (0x1<<30) +#define DEPCTL_SETD1PID (0x1<<29) +#define DEPCTL_SETD0PID (0x1<<28) +#define DEPCTL_SNAK (0x1<<27) +#define DEPCTL_CNAK (0x1<<26) +#define DEPCTL_STALL (0x1<<21) +#define DEPCTL_TYPE_BIT (18) +#define DEPCTL_TYPE_MASK (0x3<<18) +#define DEPCTL_CTRL_TYPE (0x0<<18) +#define DEPCTL_ISO_TYPE (0x1<<18) +#define DEPCTL_BULK_TYPE (0x2<<18) +#define DEPCTL_INTR_TYPE (0x3<<18) +#define DEPCTL_USBACTEP (0x1<<15) +#define DEPCTL_NEXT_EP_BIT (11) +#define DEPCTL_MPS_BIT (0) +#define DEPCTL_MPS_MASK (0x7FF) + +#define DEPCTL0_MPS_64 (0x0<<0) +#define DEPCTL0_MPS_32 (0x1<<0) +#define DEPCTL0_MPS_16 (0x2<<0) +#define DEPCTL0_MPS_8 (0x3<<0) +#define DEPCTL_MPS_BULK_512 (512<<0) +#define DEPCTL_MPS_INT_MPS_16 (16<<0) + +#define DIEPCTL0_NEXT_EP_BIT (11) + + +/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint + common interrupt mask register */ +/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */ +#define BACK2BACK_SETUP_RECEIVED (0x1<<6) +#define INTKNEPMIS (0x1<<5) +#define INTKN_TXFEMP (0x1<<4) +#define NON_ISO_IN_EP_TIMEOUT (0x1<<3) +#define CTRL_OUT_EP_SETUP_PHASE_DONE (0x1<<3) +#define AHB_ERROR (0x1<<2) +#define EPDISBLD (0x1<<1) +#define TRANSFER_DONE (0x1<<0) + +#endif diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c new file mode 100644 index 0000000..182b519 --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -0,0 +1,888 @@ +/* + * drivers/usb/gadget/s3c_udc_otg.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2008 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * Ported to u-boot: + * Marek Szyprowski <m.szyprowski@samsung.com> + * Lukasz Majewski <l.majewski@samsumg.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <common.h> +#include <asm/errno.h> +#include <linux/list.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <asm/byteorder.h> +#include <asm/io.h> + +#include <asm/mach-types.h> +#include <asm/arch/gpio.h> + +#include "regs-otg.h" +#include <usb/s3c_udc.h> +#include <asm/arch/hs_otg.h> +#include <asm/arch/power.h> +#include <usb/lin_gadget_compat.h> + +/***********************************************************/ + +#define OTG_DMA_MODE 1 + +#undef DEBUG_S3C_UDC_SETUP +#undef DEBUG_S3C_UDC_EP0 +#undef DEBUG_S3C_UDC_ISR +#undef DEBUG_S3C_UDC_OUT_EP +#undef DEBUG_S3C_UDC_IN_EP +#undef DEBUG_S3C_UDC + +/* #define DEBUG_S3C_UDC_SETUP */ +/* #define DEBUG_S3C_UDC_EP0 */ +/* #define DEBUG_S3C_UDC_ISR */ +/* #define DEBUG_S3C_UDC_OUT_EP */ +/* #define DEBUG_S3C_UDC_IN_EP */ +/* #define DEBUG_S3C_UDC */ + +#include <usb/s3c_udc.h> + +#define EP0_CON 0 +#define EP_MASK 0xF + +#if defined(DEBUG_S3C_UDC_SETUP) || defined(DEBUG_S3C_UDC_ISR) \ + || defined(DEBUG_S3C_UDC_OUT_EP) + +static char *state_names[] = { + "WAIT_FOR_SETUP", + "DATA_STATE_XMIT", + "DATA_STATE_NEED_ZLP", + "WAIT_FOR_OUT_STATUS", + "DATA_STATE_RECV", + "WAIT_FOR_COMPLETE", + "WAIT_FOR_OUT_COMPLETE", + "WAIT_FOR_IN_COMPLETE", + "WAIT_FOR_NULL_COMPLETE", +}; +#endif + +#define DRIVER_DESC "S3C HS USB OTG Device Driver, (c) Samsung Electronics" +#define DRIVER_VERSION "15 March 2009" + +struct s3c_udc *the_controller; + +static const char driver_name[] = "s3c-udc"; +static const char driver_desc[] = DRIVER_DESC; +static const char ep0name[] = "ep0-control"; + +/* Max packet size*/ +static unsigned int ep0_fifo_size = 64; +static unsigned int ep_fifo_size = 512; +static unsigned int ep_fifo_size2 = 1024; +static int reset_available = 1; + +static struct usb_ctrlrequest *usb_ctrl; +static dma_addr_t usb_ctrl_dma_addr; + +/* + Local declarations. +*/ +static int s3c_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *); +static int s3c_ep_disable(struct usb_ep *ep); +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags); +static void s3c_free_request(struct usb_ep *ep, struct usb_request *); + +static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags); +static int s3c_dequeue(struct usb_ep *ep, struct usb_request *); +static int s3c_fifo_status(struct usb_ep *ep); +static void s3c_fifo_flush(struct usb_ep *ep); +static void s3c_ep0_read(struct s3c_udc *dev); +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep); +static void s3c_handle_ep0(struct s3c_udc *dev); +static int s3c_ep0_write(struct s3c_udc *dev); +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req); +static void done(struct s3c_ep *ep, struct s3c_request *req, int status); +static void stop_activity(struct s3c_udc *dev, + struct usb_gadget_driver *driver); +static int udc_enable(struct s3c_udc *dev); +static void udc_set_address(struct s3c_udc *dev, unsigned char address); +static void reconfig_usbd(void); +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed); +static void nuke(struct s3c_ep *ep, int status); +static int s3c_udc_set_halt(struct usb_ep *_ep, int value); +static void s3c_udc_set_nak(struct s3c_ep *ep); + +static struct usb_ep_ops s3c_ep_ops = { + .enable = s3c_ep_enable, + .disable = s3c_ep_disable, + + .alloc_request = s3c_alloc_request, + .free_request = s3c_free_request, + + .queue = s3c_queue, + .dequeue = s3c_dequeue, + + .set_halt = s3c_udc_set_halt, + .fifo_status = s3c_fifo_status, + .fifo_flush = s3c_fifo_flush, +}; + +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +/***********************************************************/ + +void __iomem *regs_otg; +struct s3c_usbotg_reg *reg; +struct s3c_usbotg_phy *phy; + + +void otg_phy_init(void) +{ + the_controller->pdata->phy_control(1); + + /*USB PHY0 Enable */ + printf("USB PHY0 Enable\n"); + + /* Enable PHY */ + writel(readl(S5PC110_USB_PHY_CONTROL)|(0x1<<0), + S5PC110_USB_PHY_CONTROL); + + writel((readl(&phy->phypwr) + &~(0x3<<3)&~(0x1<<0)), &phy->phypwr); + + writel((readl(&phy->phyclk) + &~(0x5<<2))|(0x3<<0), &phy->phyclk); /* PLL 24Mhz */ + writel((readl(&phy->rstcon) + &~(0x3<<1))|(0x1<<0), &phy->rstcon); + udelay(10); + writel(readl(&phy->rstcon) + &~(0x7<<0), &phy->rstcon); + udelay(10); +} + +void otg_phy_off(void) +{ + /* reset controller just in case */ + writel(0x1, &phy->rstcon); + udelay(20); + writel(0x0, &phy->rstcon); + udelay(20); + + writel(readl(&phy->phypwr)|(0x3<<3)|(0x1), &phy->phypwr); + writel(readl(S5PC110_USB_PHY_CONTROL)&~(1<<0), + S5PC110_USB_PHY_CONTROL); + + writel((readl(&phy->phyclk) & ~(0x5 << 2)), &phy->phyclk); + + udelay(10000); + + the_controller->pdata->phy_control(0); +} + +/***********************************************************/ + +#include "s3c_udc_otg_xfer_dma.c" + +/* + * udc_disable - disable USB device controller + */ +static void udc_disable(struct s3c_udc *dev) +{ + DEBUG_SETUP("%s: %p\n", __func__, dev); + + udc_set_address(dev, 0); + + dev->ep0state = WAIT_FOR_SETUP; + dev->gadget.speed = USB_SPEED_UNKNOWN; + dev->usb_address = 0; + + otg_phy_off(); +} + +/* + * udc_reinit - initialize software state + */ +static void udc_reinit(struct s3c_udc *dev) +{ + unsigned int i; + + DEBUG_SETUP("%s: %p\n", __func__, dev); + + /* device/ep0 records init */ + INIT_LIST_HEAD(&dev->gadget.ep_list); + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); + dev->ep0state = WAIT_FOR_SETUP; + + /* basic endpoint records init */ + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { + struct s3c_ep *ep = &dev->ep[i]; + + if (i != 0) + list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + + ep->desc = 0; + ep->stopped = 0; + INIT_LIST_HEAD(&ep->queue); + ep->pio_irqs = 0; + } + + /* the rest was statically initialized, and is read-only */ +} + +#define BYTES2MAXP(x) (x / 8) +#define MAXP2BYTES(x) (x * 8) + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static int udc_enable(struct s3c_udc *dev) +{ + DEBUG_SETUP("%s: %p\n", __func__, dev); + + otg_phy_init(); + reconfig_usbd(); + + DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", + readl(®->gintmsk)); + + dev->gadget.speed = USB_SPEED_UNKNOWN; + + return 0; +} + +/* + Register entry point for the peripheral controller driver. +*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct s3c_udc *dev = the_controller; + int retval = 0; + unsigned long flags; + + DEBUG_SETUP("%s: %s\n", __func__, "no name"); + + if (!driver + || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !driver->bind || !driver->disconnect || !driver->setup) + return -EINVAL; + if (!dev) + return -ENODEV; + if (dev->driver) + return -EBUSY; + + spin_lock_irqsave(&dev->lock, flags); + /* first hook up the driver ... */ + dev->driver = driver; + spin_unlock_irqrestore(&dev->lock, flags); + + if (retval) { /* TODO */ + printf("target device_add failed, error %d\n", retval); + return retval; + } + + retval = driver->bind(&dev->gadget); + if (retval) { + DEBUG_SETUP("%s: bind to driver --> error %d\n", + dev->gadget.name, retval); + dev->driver = 0; + return retval; + } + + enable_irq(IRQ_OTG); + + DEBUG_SETUP("Registered gadget driver %s\n", dev->gadget.name); + udc_enable(dev); + + return 0; +} + +/* + * Unregister entry point for the peripheral controller driver. + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct s3c_udc *dev = the_controller; + unsigned long flags; + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + disable_irq(IRQ_OTG); + + udc_disable(dev); + return 0; +} + +/* + * done - retire a request; caller blocked irqs + */ +static void done(struct s3c_ep *ep, struct s3c_request *req, int status) +{ + unsigned int stopped = ep->stopped; + + DEBUG("%s: %s %p, req = %p, stopped = %d\n", + __func__, ep->ep.name, ep, &req->req, stopped); + + list_del_init(&req->queue); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) { + DEBUG("complete %s req %p stat %d len %u/%u\n", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + } + + /* don't modify queue heads during completion callback */ + ep->stopped = 1; + +#ifdef DEBUG_S3C_UDC + printf("calling complete callback\n"); + { + int i, len = req->req.length; + + printf("pkt[%d] = ", req->req.length); + if (len > 64) + len = 64; + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)req->req.buf)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + spin_unlock(&ep->dev->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->dev->lock); + + DEBUG("callback completed\n"); + + ep->stopped = stopped; +} + +/* + * nuke - dequeue ALL requests + */ +static void nuke(struct s3c_ep *ep, int status) +{ + struct s3c_request *req; + + DEBUG("%s: %s %p\n", __func__, ep->ep.name, ep); + + /* called with irqs blocked */ + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + done(ep, req, status); + } +} + +static void stop_activity(struct s3c_udc *dev, + struct usb_gadget_driver *driver) +{ + int i; + + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; + dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { + struct s3c_ep *ep = &dev->ep[i]; + ep->stopped = 1; + nuke(ep, -ESHUTDOWN); + } + + /* report disconnect; the driver is already quiesced */ + if (driver) { + spin_unlock(&dev->lock); + driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + + /* re-init driver-visible data structures */ + udc_reinit(dev); +} + +static void reconfig_usbd(void) +{ + /* 2. Soft-reset OTG Core and then unreset again. */ + int i; + unsigned int uTemp = writel(CORE_SOFT_RESET, ®->grstctl); + + DEBUG(2, "Reseting OTG controller\n"); + + writel(0<<15 /* PHY Low Power Clock sel*/ + |1<<14 /* Non-Periodic TxFIFO Rewind Enable*/ + |0x5<<10 /* Turnaround time*/ + |0<<9 | 0<<8 /* [0:HNP disable,1:HNP enable][ 0:SRP disable*/ + /* 1:SRP enable] H1= 1,1*/ + |0<<7 /* Ulpi DDR sel*/ + |0<<6 /* 0: high speed utmi+, 1: full speed serial*/ + |0<<4 /* 0: utmi+, 1:ulpi*/ + |1<<3 /* phy i/f 0:8bit, 1:16bit*/ + |0x7<<0, /* HS/FS Timeout**/ + ®->gusbcfg); + + /* 3. Put the OTG device core in the disconnected state.*/ + uTemp = readl(®->dctl); + uTemp |= SOFT_DISCONNECT; + writel(uTemp, ®->dctl); + + udelay(20); + + /* 4. Make the OTG device core exit from the disconnected state.*/ + uTemp = readl(®->dctl); + uTemp = uTemp & ~SOFT_DISCONNECT; + writel(uTemp, ®->dctl); + + /* 5. Configure OTG Core to initial settings of device mode.*/ + /* [][1: full speed(30Mhz) 0:high speed]*/ + writel(1<<18 | 0x0<<0, ®->dcfg); + + mdelay(1); + + /* 6. Unmask the core interrupts*/ + writel(GINTMSK_INIT, ®->gintmsk); + + /* 7. Set NAK bit of EP0, EP1, EP2*/ + writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), + ®->out_endp[EP0_CON].doepctl); + writel(DEPCTL_EPDIS|DEPCTL_SNAK|(0<<0), ®->in_endp[EP0_CON].diepctl); + + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) { + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[i].doepctl); + writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[i].diepctl); + } + + /* 8. Unmask EPO interrupts*/ + writel(((1 << EP0_CON) << DAINT_OUT_BIT) + | (1 << EP0_CON), ®->daintmsk); + + /* 9. Unmask device OUT EP common interrupts*/ + writel(DOEPMSK_INIT, ®->doepmsk); + + /* 10. Unmask device IN EP common interrupts*/ + writel(DIEPMSK_INIT, ®->diepmsk); + + /* 11. Set Rx FIFO Size (in 32-bit words) */ + writel(RX_FIFO_SIZE >> 2, ®->grxfsiz); + + /* 12. Set Non Periodic Tx FIFO Size*/ + writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0, + ®->gnptxfsiz); + + for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++) + writel((PTX_FIFO_SIZE >> 2) << 16 | + ((RX_FIFO_SIZE + NPTX_FIFO_SIZE + + PTX_FIFO_SIZE*(i-1)) >> 2) << 0, + ®->dieptxf[i-1]); + + /* Flush the RX FIFO */ + writel(0x10, ®->grstctl); + while (readl(®->grstctl) & 0x10) + DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + + /* Flush all the Tx FIFO's */ + writel(0x10<<6, ®->grstctl); + writel((0x10<<6)|0x20, ®->grstctl); + while (readl(®->grstctl) & 0x20) + DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + + /* 13. Clear NAK bit of EP0, EP1, EP2*/ + /* For Slave mode*/ + /* EP0: Control OUT */ + writel(DEPCTL_EPDIS | DEPCTL_CNAK|(0<<0), + ®->out_endp[EP0_CON].doepctl); + + /* 14. Initialize OTG Link Core.*/ + writel(GAHBCFG_INIT, ®->gahbcfg); +} + +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed) +{ + unsigned int ep_ctrl; + int i; + + if (speed == USB_SPEED_HIGH) { + ep0_fifo_size = 64; + ep_fifo_size = 512; + ep_fifo_size2 = 1024; + dev->gadget.speed = USB_SPEED_HIGH; + } else { + ep0_fifo_size = 64; + ep_fifo_size = 64; + ep_fifo_size2 = 64; + dev->gadget.speed = USB_SPEED_FULL; + } + + dev->ep[0].ep.maxpacket = ep0_fifo_size; + for (i = 1; i < S3C_MAX_ENDPOINTS; i++) + dev->ep[i].ep.maxpacket = ep_fifo_size; + + /* EP0 - Control IN (64 bytes)*/ + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|(0<<0), ®->in_endp[EP0_CON].diepctl); + + /* EP0 - Control OUT (64 bytes)*/ + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|(0<<0), ®->out_endp[EP0_CON].doepctl); +} + +static int s3c_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + + DEBUG("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || !desc || ep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || ep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { + + DEBUG("%s: bad ep or descriptor\n", __func__); + return -EINVAL; + } + + /* xfer types must match, except that interrupt ~= bulk */ + if (ep->bmAttributes != desc->bmAttributes + && ep->bmAttributes != USB_ENDPOINT_XFER_BULK + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + + DEBUG("%s: %s type mismatch\n", __func__, _ep->name); + return -EINVAL; + } + + /* hardware _could_ do smaller, but driver doesn't */ + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) + || !desc->wMaxPacketSize) { + + DEBUG("%s: bad %s maxpacket\n", __func__, _ep->name); + return -ERANGE; + } + + dev = ep->dev; + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + + DEBUG("%s: bogus device state\n", __func__); + return -ESHUTDOWN; + } + + ep->stopped = 0; + ep->desc = desc; + ep->pio_irqs = 0; + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + /* Reset halt state */ + s3c_udc_set_nak(ep); + s3c_udc_set_halt(_ep, 0); + + spin_lock_irqsave(&ep->dev->lock, flags); + s3c_udc_ep_activate(ep); + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: enabled %s, stopped = %d, maxpacket = %d\n", + __func__, _ep->name, ep->stopped, ep->ep.maxpacket); + return 0; +} + +/* + * Disable EP + */ +static int s3c_ep_disable(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + unsigned long flags; + + DEBUG("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || !ep->desc) { + DEBUG("%s: %s not enabled\n", __func__, + _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* Nuke all pending requests */ + nuke(ep, -ESHUTDOWN); + + ep->desc = 0; + ep->stopped = 1; + + spin_unlock_irqrestore(&ep->dev->lock, flags); + + DEBUG("%s: disabled %s\n", __func__, _ep->name); + return 0; +} + +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct s3c_request *req; + + DEBUG("%s: %s %p\n", __func__, ep->name, ep); + + req = kmalloc(sizeof *req, gfp_flags); + if (!req) + return 0; + + memset(req, 0, sizeof *req); + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct s3c_request *req; + + DEBUG("%s: %p\n", __func__, ep); + + req = container_of(_req, struct s3c_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/* dequeue JUST ONE request */ +static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c_ep *ep; + struct s3c_request *req; + unsigned long flags; + + DEBUG("%s: %p\n", __func__, _ep); + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep || ep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&ep->dev->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore(&ep->dev->lock, flags); + return -EINVAL; + } + + done(ep, req, -ECONNRESET); + + spin_unlock_irqrestore(&ep->dev->lock, flags); + return 0; +} + +/* + * Return bytes in EP FIFO + */ +static int s3c_fifo_status(struct usb_ep *_ep) +{ + int count = 0; + struct s3c_ep *ep; + + ep = container_of(_ep, struct s3c_ep, ep); + if (!_ep) { + DEBUG("%s: bad ep\n", __func__); + return -ENODEV; + } + + DEBUG("%s: %d\n", __func__, ep_index(ep)); + + /* LPD can't report unclaimed bytes from IN fifos */ + if (ep_is_in(ep)) + return -EOPNOTSUPP; + + return count; +} + +/* + * Flush EP FIFO + */ +static void s3c_fifo_flush(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + + ep = container_of(_ep, struct s3c_ep, ep); + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + DEBUG("%s: bad ep\n", __func__); + return; + } + + DEBUG("%s: %d\n", __func__, ep_index(ep)); +} + +static const struct usb_gadget_ops s3c_udc_ops = { + /* current versions must always be self-powered */ +}; + +static struct s3c_udc memory = { + .usb_address = 0, + .gadget = { + .ops = &s3c_udc_ops, + .ep0 = &memory.ep[0].ep, + .name = driver_name, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = ep0name, + .ops = &s3c_ep_ops, + .maxpacket = EP0_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = 0, + .bmAttributes = 0, + + .ep_type = ep_control, + }, + + /* first group of endpoints */ + .ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_out, + .fifo_num = 1, + }, + + .ep[2] = { + .ep = { + .name = "ep2out-bulk", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_OUT | 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + + .ep_type = ep_bulk_in, + .fifo_num = 2, + }, + + .ep[3] = { + .ep = { + .name = "ep3in-int", + .ops = &s3c_ep_ops, + .maxpacket = EP_FIFO_SIZE, + }, + .dev = &memory, + + .bEndpointAddress = USB_DIR_IN | 3, + .bmAttributes = USB_ENDPOINT_XFER_INT, + + .ep_type = ep_interrupt, + .fifo_num = 3, + }, +}; + +/* + * probe - binds to the platform device + */ + +int s3c_udc_probe(struct s3c_plat_otg_data *pdata) +{ + struct s3c_udc *dev = &memory; + int retval = 0, i; + + DEBUG("%s: %p\n", __func__, pdata); + + dev->pdata = pdata; + + phy = (struct s3c_usbotg_phy *)pdata->regs_phy; + reg = (struct s3c_usbotg_reg *)pdata->regs_otg; + + /* regs_otg = (void *)pdata->regs_otg; */ + + dev->gadget.is_dualspeed = 1; /* Hack only*/ + dev->gadget.is_otg = 0; + dev->gadget.is_a_peripheral = 0; + dev->gadget.b_hnp_enable = 0; + dev->gadget.a_hnp_support = 0; + dev->gadget.a_alt_hnp_support = 0; + + the_controller = dev; + + for (i = 0; i < S3C_MAX_ENDPOINTS+1; i++) { + dev->dma_buf[i] = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL); + dev->dma_addr[i] = (dma_addr_t) dev->dma_buf[i]; + invalidate_dcache_range((unsigned long) dev->dma_buf[i], + (unsigned long) (dev->dma_buf[i] + + DMA_BUFFER_SIZE)); + } + usb_ctrl = dev->dma_buf[0]; + usb_ctrl_dma_addr = dev->dma_addr[0]; + + udc_reinit(dev); + + return retval; +} + +int usb_gadget_handle_interrupts() +{ + u32 intr_status = readl(®->gintsts); + u32 gintmsk = readl(®->gintmsk); + + if (intr_status & gintmsk) + return s3c_udc_irq(1, (void *)the_controller); + return 0; +} diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c new file mode 100644 index 0000000..335440a --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -0,0 +1,1449 @@ +/* + * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2009 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * Ported to u-boot: + * Marek Szyprowski <m.szyprowski@samsung.com> + * Lukasz Majewski <l.majewski@samsumg.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define GINTMSK_INIT (INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\ + | INT_RESET | INT_SUSPEND) +#define DOEPMSK_INIT (CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE) +#define DIEPMSK_INIT (NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE) +#define GAHBCFG_INIT (PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\ + | GBL_INT_UNMASK) + +static u8 clear_feature_num; +int clear_feature_flag; + +/* Bulk-Only Mass Storage Reset (class-specific request) */ +#define GET_MAX_LUN_REQUEST 0xFE +#define BOT_RESET_REQUEST 0xFF + +static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev) +{ + u32 ep_ctrl; + + flush_dcache_range((unsigned long) usb_ctrl_dma_addr, + (unsigned long) usb_ctrl_dma_addr + + DMA_BUFFER_SIZE); + + writel(usb_ctrl_dma_addr, ®->in_endp[EP0_CON].diepdma); + writel((1<<19 | 0<<0), ®->in_endp[EP0_CON].dieptsiz); + + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->in_endp[EP0_CON].diepctl); + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + dev->ep0state = WAIT_FOR_IN_COMPLETE; +} + +void s3c_udc_pre_setup(void) +{ + u32 ep_ctrl; + + DEBUG_IN_EP("%s : Prepare Setup packets.\n", __func__); + + invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, + (unsigned long) usb_ctrl_dma_addr + + DMA_BUFFER_SIZE); + + writel((1 << 19) | sizeof(struct usb_ctrlrequest), + ®->out_endp[EP0_CON].doeptsiz); + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|DEPCTL_EPENA, ®->out_endp[EP0_CON].doepctl); + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + +static inline void s3c_ep0_complete_out(void) +{ + u32 ep_ctrl; + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + + DEBUG_IN_EP("%s : Prepare Complete Out packet.\n", __func__); + + invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, + (unsigned long) usb_ctrl_dma_addr + + DMA_BUFFER_SIZE); + + writel((1 << 19) | sizeof(struct usb_ctrlrequest), + ®->out_endp[EP0_CON].doeptsiz); + writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + + ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->out_endp[EP0_CON].doepctl); + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, readl(®->in_endp[EP0_CON].diepctl)); + DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", + __func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + + +static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + + buf = req->req.buf + req->req.actual; + + length = min(req->req.length - req->req.actual, (int)ep->ep.maxpacket); + + ep->len = length; + ep->dma_buf = buf; + + invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], + (unsigned long) ep->dev->dma_buf[ep_num] + + DMA_BUFFER_SIZE); + + if (length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + + pktcnt = 1; + ctrl = readl(®->out_endp[ep_num].doepctl); + + writel(the_controller->dma_addr[ep_index(ep)+1], + ®->out_endp[ep_num].doepdma); + writel((pktcnt<<19)|(length<<0), ®->out_endp[ep_num].doeptsiz); + writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->out_endp[ep_num].doepctl); + + DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x," + "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n" + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", + __func__, ep_num, + readl(®->out_endp[ep_num].doepdma), + readl(®->out_endp[ep_num].doeptsiz), + readl(®->out_endp[ep_num].doepctl), + buf, pktcnt, length); + return 0; + +} + +int setdma_tx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl = 0; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + u32 *p = the_controller->dma_buf[ep_index(ep)+1]; + + buf = req->req.buf + req->req.actual; + length = req->req.length - req->req.actual; + + if (ep_num == EP0_CON) + length = min_t(length, (u32)ep_maxpacket(ep)); + + ep->len = length; + ep->dma_buf = buf; + memcpy(p, ep->dma_buf, length); + + flush_dcache_range((unsigned long) p , + (unsigned long) p + DMA_BUFFER_SIZE); + + if (length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + + /* Flush the endpoint's Tx FIFO */ + writel(ep->fifo_num<<6, ®->grstctl); + writel((ep->fifo_num<<6)|0x20, ®->grstctl); + while (readl(®->grstctl) & 0x20) + ; + + writel(the_controller->dma_addr[ep_index(ep)+1], + ®->in_endp[ep_num].diepdma); + writel((pktcnt<<19)|(length<<0), ®->in_endp[ep_num].dieptsiz); + + ctrl = readl(®->in_endp[ep_num].diepctl); + + /* Write the FIFO number to be used for this endpoint */ + ctrl &= ~(0xF << 22); + ctrl |= (ep->fifo_num << 22); + + /* Clear reserved (Next EP) bits */ + ctrl = (ctrl&~(EP_MASK<<DEPCTL_NEXT_EP_BIT)); + + writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->in_endp[ep_num].diepctl); + + DEBUG_IN_EP("%s:EP%d TX DMA start : DIEPDMA0 = 0x%x," + "DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n" + "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", + __func__, ep_num, + readl(®->in_endp[ep_num].diepdma), + readl(®->in_endp[ep_num].dieptsiz), + readl(®->in_endp[ep_num].diepctl), + buf, pktcnt, length); + + return length; +} + +static void complete_rx(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req = NULL; + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; + u32 *p = the_controller->dma_buf[ep_index(ep)+1]; + + if (list_empty(&ep->queue)) { + DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n", + __func__, ep_num); + return; + + } + + req = list_entry(ep->queue.next, struct s3c_request, queue); + ep_tsr = readl(®->out_endp[ep_num].doeptsiz); + + if (ep_num == EP0_CON) + xfer_size = (ep_tsr & 0x7f); + else + xfer_size = (ep_tsr & 0x7fff); + + xfer_size = ep->len - xfer_size; + + invalidate_dcache_range((unsigned long) p, + (unsigned long) p + DMA_BUFFER_SIZE); + + memcpy(ep->dma_buf, p, ep->len); + + req->req.actual += min(xfer_size, req->req.length - req->req.actual); + is_short = (xfer_size < ep->ep.maxpacket); + + DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, " + "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", + __func__, ep_num, req->req.actual, req->req.length, + is_short, ep_tsr, xfer_size); + + if (is_short || req->req.actual == req->req.length) { + if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { + DEBUG_OUT_EP(" => Send ZLP\n"); + s3c_udc_ep0_zlp(dev); + /* packet will be completed in complete_tx() */ + dev->ep0state = WAIT_FOR_IN_COMPLETE; + } else { + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct s3c_request, queue); + DEBUG_OUT_EP("%s: Next Rx request start...\n", + __func__); + setdma_rx(ep, req); + } + } + } else + setdma_rx(ep, req); +} + +static void complete_tx(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req; + u32 ep_tsr = 0, xfer_size = 0, is_short = 0; + u32 last; + + if (dev->ep0state == WAIT_FOR_NULL_COMPLETE) { + dev->ep0state = WAIT_FOR_OUT_COMPLETE; + s3c_ep0_complete_out(); + return; + } + + if (list_empty(&ep->queue)) { + DEBUG_IN_EP("%s: TX DMA done : NULL REQ on IN EP-%d\n", + __func__, ep_num); + return; + + } + + req = list_entry(ep->queue.next, struct s3c_request, queue); + + ep_tsr = readl(®->in_endp[ep_num].dieptsiz); + + xfer_size = ep->len; + is_short = (xfer_size < ep->ep.maxpacket); + req->req.actual += min(xfer_size, req->req.length - req->req.actual); + + DEBUG_IN_EP("%s: TX DMA done : ep = %d, tx bytes = %d/%d, " + "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n", + __func__, ep_num, req->req.actual, req->req.length, + is_short, ep_tsr, xfer_size); + + if (ep_num == 0) { + if (dev->ep0state == DATA_STATE_XMIT) { + DEBUG_IN_EP("%s: ep_num = %d, ep0stat ==" + "DATA_STATE_XMIT\n", + __func__, ep_num); + last = write_fifo_ep0(ep, req); + if (last) + dev->ep0state = WAIT_FOR_COMPLETE; + } else if (dev->ep0state == WAIT_FOR_IN_COMPLETE) { + DEBUG_IN_EP("%s: ep_num = %d, completing request\n", + __func__, ep_num); + done(ep, req, 0); + dev->ep0state = WAIT_FOR_SETUP; + } else if (dev->ep0state == WAIT_FOR_COMPLETE) { + DEBUG_IN_EP("%s: ep_num = %d, completing request\n", + __func__, ep_num); + done(ep, req, 0); + dev->ep0state = WAIT_FOR_OUT_COMPLETE; + s3c_ep0_complete_out(); + } else { + DEBUG_IN_EP("%s: ep_num = %d, invalid ep state\n", + __func__, ep_num); + } + return; + } + + if (req->req.actual == req->req.length) + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + DEBUG_IN_EP("%s: Next Tx request start...\n", __func__); + setdma_tx(ep, req); + } +} + +static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num) +{ + struct s3c_ep *ep = &dev->ep[ep_num]; + struct s3c_request *req; + + DEBUG_IN_EP("%s: Check queue, ep_num = %d\n", __func__, ep_num); + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + DEBUG_IN_EP("%s: Next Tx request(0x%p) start...\n", + __func__, req); + + if (ep_is_in(ep)) + setdma_tx(ep, req); + else + setdma_rx(ep, req); + } else { + DEBUG_IN_EP("%s: NULL REQ on IN EP-%d\n", __func__, ep_num); + + return; + } + +} + +static void process_ep_in_intr(struct s3c_udc *dev) +{ + u32 ep_intr, ep_intr_status; + u8 ep_num = 0; + + ep_intr = readl(®->daint); + DEBUG_IN_EP("*** %s: EP In interrupt : DAINT = 0x%x\n", + __func__, ep_intr); + + ep_intr &= DAINT_MASK; + + while (ep_intr) { + if (ep_intr & 0x1) { + ep_intr_status = readl(®->in_endp[ep_num].diepint); + DEBUG_IN_EP("\tEP%d-IN : DIEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + writel(ep_intr_status, ®->in_endp[ep_num].diepint); + + if (ep_intr_status & TRANSFER_DONE) { + complete_tx(dev, ep_num); + + if (ep_num == 0) { + if (dev->ep0state == + WAIT_FOR_IN_COMPLETE) + dev->ep0state = WAIT_FOR_SETUP; + + if (dev->ep0state == WAIT_FOR_SETUP) + s3c_udc_pre_setup(); + + /* continue transfer after + set_clear_halt for DMA mode */ + if (clear_feature_flag == 1) { + s3c_udc_check_tx_queue(dev, + clear_feature_num); + clear_feature_flag = 0; + } + } + } + } + ep_num++; + ep_intr >>= 1; + } +} + +static void process_ep_out_intr(struct s3c_udc *dev) +{ + u32 ep_intr, ep_intr_status; + u8 ep_num = 0; + + ep_intr = readl(®->daint); + DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n", + __func__, ep_intr); + + ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK; + + while (ep_intr) { + if (ep_intr & 0x1) { + ep_intr_status = readl(®->out_endp[ep_num].doepint); + DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + writel(ep_intr_status, ®->out_endp[ep_num].doepint); + + if (ep_num == 0) { + if (ep_intr_status & TRANSFER_DONE) { + if (dev->ep0state != + WAIT_FOR_OUT_COMPLETE) + complete_rx(dev, ep_num); + else { + dev->ep0state = WAIT_FOR_SETUP; + s3c_udc_pre_setup(); + } + } + + if (ep_intr_status & + CTRL_OUT_EP_SETUP_PHASE_DONE) { + DEBUG_OUT_EP("SETUP packet arrived\n"); + s3c_handle_ep0(dev); + } + } else { + if (ep_intr_status & TRANSFER_DONE) + complete_rx(dev, ep_num); + } + } + ep_num++; + ep_intr >>= 1; + } +} + +/* + * usb client interrupt handler. + */ +static int s3c_udc_irq(int irq, void *_dev) +{ + struct s3c_udc *dev = _dev; + u32 intr_status; + u32 usb_status, gintmsk; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + + intr_status = readl(®->gintsts); + gintmsk = readl(®->gintmsk); + + DEBUG_ISR("\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x," + "DAINT : 0x%x, DAINTMSK : 0x%x\n", + __func__, intr_status, state_names[dev->ep0state], gintmsk, + readl(®->daint), readl(®->daintmsk)); + + if (!intr_status) { + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_HANDLED; + } + + if (intr_status & INT_ENUMDONE) { + DEBUG_ISR("\tSpeed Detection interrupt\n"); + + writel(INT_ENUMDONE, ®->gintsts); + usb_status = (readl(®->dsts) & 0x6); + + if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) { + DEBUG_ISR("\t\tFull Speed Detection\n"); + set_max_pktsize(dev, USB_SPEED_FULL); + + } else { + DEBUG_ISR("\t\tHigh Speed Detection : 0x%x\n", + usb_status); + set_max_pktsize(dev, USB_SPEED_HIGH); + } + } + + if (intr_status & INT_EARLY_SUSPEND) { + DEBUG_ISR("\tEarly suspend interrupt\n"); + writel(INT_EARLY_SUSPEND, ®->gintsts); + } + + if (intr_status & INT_SUSPEND) { + usb_status = readl(®->dsts); + DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status); + writel(INT_SUSPEND, ®->gintsts); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver) { + if (dev->driver->suspend) + dev->driver->suspend(&dev->gadget); + + /* HACK to let gadget detect disconnected state */ + if (dev->driver->disconnect) { + spin_unlock_irqrestore(&dev->lock, flags); + dev->driver->disconnect(&dev->gadget); + spin_lock_irqsave(&dev->lock, flags); + } + } + } + + if (intr_status & INT_RESUME) { + DEBUG_ISR("\tResume interrupt\n"); + writel(INT_RESUME, ®->gintsts); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->resume) { + + dev->driver->resume(&dev->gadget); + } + } + + if (intr_status & INT_RESET) { + usb_status = readl(®->gotgctl); + DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status); + writel(INT_RESET, ®->gintsts); + + if ((usb_status & 0xc0000) == (0x3 << 18)) { + if (reset_available) { + DEBUG_ISR("\t\tOTG core got reset (%d)!!\n", + reset_available); + reconfig_usbd(); + dev->ep0state = WAIT_FOR_SETUP; + reset_available = 0; + s3c_udc_pre_setup(); + } else + reset_available = 1; + + } else { + reset_available = 1; + DEBUG_ISR("\t\tRESET handling skipped\n"); + } + } + + if (intr_status & INT_IN_EP) + process_ep_in_intr(dev); + + if (intr_status & INT_OUT_EP) + process_ep_out_intr(dev); + + spin_unlock_irqrestore(&dev->lock, flags); + + return IRQ_HANDLED; +} + +/** Queue one request + * Kickstart transfer if needed + */ +static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c_request *req; + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + u32 ep_num, gintsts; + + req = container_of(_req, struct s3c_request, req); + if (unlikely(!_req || !_req->complete || !_req->buf + || !list_empty(&req->queue))) { + + DEBUG("%s: bad params\n", __func__); + return -EINVAL; + } + + ep = container_of(_ep, struct s3c_ep, ep); + + if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + + DEBUG("%s: bad ep: %s, %d, %x\n", __func__, + ep->ep.name, !ep->desc, _ep); + return -EINVAL; + } + + ep_num = ep_index(ep); + dev = ep->dev; + if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + + DEBUG("%s: bogus device state %p\n", __func__, dev->driver); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + /* kickstart this i/o queue? */ + DEBUG("\n*** %s: %s-%s req = %p, len = %d, buf = %p" + "Q empty = %d, stopped = %d\n", + __func__, _ep->name, ep_is_in(ep) ? "in" : "out", + _req, _req->length, _req->buf, + list_empty(&ep->queue), ep->stopped); + +#ifdef DEBUG_S3C_UDC + { + int i, len = _req->length; + + printf("pkt = "); + if (len > 64) + len = 64; + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)_req->buf)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + + if (list_empty(&ep->queue) && !ep->stopped) { + + if (ep_num == 0) { + /* EP0 */ + list_add_tail(&req->queue, &ep->queue); + s3c_ep0_kick(dev, ep); + req = 0; + + } else if (ep_is_in(ep)) { + gintsts = readl(®->gintsts); + DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); + + setdma_tx(ep, req); + } else { + gintsts = readl(®->gintsts); + DEBUG_OUT_EP("%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); + + setdma_rx(ep, req); + } + } + + /* pio or dma irq handler advances the queue. */ + if (likely(req != 0)) + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +/****************************************************************/ +/* End Point 0 related functions */ +/****************************************************************/ + +/* return: 0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 max; + unsigned count; + int is_last; + + max = ep_maxpacket(ep); + + DEBUG_EP0("%s: max = %d\n", __func__, max); + + count = setdma_tx(ep, req); + + /* last packet is usually short (or a zlp) */ + if (likely(count != max)) + is_last = 1; + else { + if (likely(req->req.length != req->req.actual + count) + || req->req.zero) + is_last = 0; + else + is_last = 1; + } + + DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__, + ep->ep.name, count, + is_last ? "/L" : "", + req->req.length - req->req.actual - count, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + ep->dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + return 0; +} + +int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max) +{ + u32 bytes; + + bytes = sizeof(struct usb_ctrlrequest); + + invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_index(ep)], + (unsigned long) ep->dev->dma_buf[ep_index(ep)] + + DMA_BUFFER_SIZE); + + DEBUG_EP0("%s: bytes=%d, ep_index=%d %p\n", __func__, + bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]); + + return bytes; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function + * after it decodes a set address setup packet. + */ +static void udc_set_address(struct s3c_udc *dev, unsigned char address) +{ + u32 ctrl = readl(®->dcfg); + writel(address << 4 | ctrl, ®->dcfg); + + s3c_udc_ep0_zlp(dev); + + DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", + __func__, address, readl(®->dcfg)); + + dev->usb_address = address; +} + +static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep) +{ + struct s3c_udc *dev; + u32 ep_ctrl = 0; + + dev = ep->dev; + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->in_endp[EP0_CON].diepctl); + + DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n", + __func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); + /* + * The application can only set this bit, and the core clears it, + * when a SETUP token is received for this endpoint + */ + dev->ep0state = WAIT_FOR_SETUP; + + s3c_udc_pre_setup(); +} + +static void s3c_ep0_read(struct s3c_udc *dev) +{ + struct s3c_request *req; + struct s3c_ep *ep = &dev->ep[0]; + int ret; + + if (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, struct s3c_request, queue); + + } else { + DEBUG("%s: ---> BUG\n", __func__); + BUG(); + return; + } + + DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); + + if (req->req.length == 0) { + /* zlp for Set_configuration, Set_interface, + * or Bulk-Only mass storge reset */ + + ep->len = 0; + s3c_udc_ep0_zlp(dev); + + DEBUG_EP0("%s: req.length = 0, bRequest = %d\n", + __func__, usb_ctrl->bRequest); + return; + } + + ret = setdma_rx(ep, req); +} + +/* + * DATA_STATE_XMIT + */ +static int s3c_ep0_write(struct s3c_udc *dev) +{ + struct s3c_request *req; + struct s3c_ep *ep = &dev->ep[0]; + int ret, need_zlp = 0; + + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct s3c_request, queue); + + if (!req) { + DEBUG_EP0("%s: NULL REQ\n", __func__); + return 0; + } + + DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", + __func__, req, req->req.length, req->req.actual); + + if (req->req.length - req->req.actual == ep0_fifo_size) { + /* Next write will end with the packet size, */ + /* so we need Zero-length-packet */ + need_zlp = 1; + } + + ret = write_fifo_ep0(ep, req); + + if ((ret == 1) && !need_zlp) { + /* Last packet */ + dev->ep0state = WAIT_FOR_COMPLETE; + DEBUG_EP0("%s: finished, waiting for status\n", __func__); + + } else { + dev->ep0state = DATA_STATE_XMIT; + DEBUG_EP0("%s: not finished\n", __func__); + } + + return 1; +} + +u16 g_status; + +int s3c_udc_get_status(struct s3c_udc *dev, + struct usb_ctrlrequest *crq) +{ + u8 ep_num = crq->wIndex & 0x7F; + u32 ep_ctrl; + u32 *p = the_controller->dma_buf[1]; + + DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__); + printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK); + switch (crq->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_INTERFACE: + g_status = 0; + DEBUG_SETUP("\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n", + g_status); + break; + + case USB_RECIP_DEVICE: + g_status = 0x1; /* Self powered */ + DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", + g_status); + break; + + case USB_RECIP_ENDPOINT: + if (crq->wLength > 2) { + DEBUG_SETUP("\tGET_STATUS:Not support EP or wLength\n"); + return 1; + } + + g_status = dev->ep[ep_num].stopped; + DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", + g_status); + + break; + + default: + return 1; + } + + memcpy(p, &g_status, sizeof(g_status)); + + flush_dcache_range((unsigned long) p, + (unsigned long) p + DMA_BUFFER_SIZE); + + writel(the_controller->dma_addr[1], ®->in_endp[EP0_CON].diepdma); + writel((1<<19)|(2<<0), ®->in_endp[EP0_CON].dieptsiz); + + ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + ®->in_endp[EP0_CON].diepctl); + dev->ep0state = WAIT_FOR_NULL_COMPLETE; + + return 0; +} + +static void s3c_udc_set_nak(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + ep_ctrl |= DEPCTL_SNAK; + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: set NAK, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + ep_ctrl |= DEPCTL_SNAK; + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: set NAK, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + + +void s3c_udc_ep_set_stall(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + + /* set the stall bit */ + ep_ctrl |= DEPCTL_STALL; + + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + +void s3c_udc_ep_clear_stall(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0; + + ep_num = ep_index(ep); + DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + + /* clear stall bit */ + ep_ctrl &= ~DEPCTL_STALL; + + /* + * USB Spec 9.4.5: For endpoints using data toggle, regardless + * of whether an endpoint has the Halt feature set, a + * ClearFeature(ENDPOINT_HALT) request always results in the + * data toggle being reinitialized to DATA0. + */ + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ + } + + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + + /* clear stall bit */ + ep_ctrl &= ~DEPCTL_STALL; + + if (ep->bmAttributes == USB_ENDPOINT_XFER_INT + || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ + } + + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); + } + + return; +} + +static int s3c_udc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c_ep *ep; + struct s3c_udc *dev; + unsigned long flags; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + + if (unlikely(!_ep || !ep->desc || ep_num == EP0_CON || + ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) { + DEBUG("%s: %s bad ep or descriptor\n", __func__, ep->ep.name); + return -EINVAL; + } + + /* Attempt to halt IN ep will fail if any transfer requests + * are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + DEBUG("%s: %s queue not empty, req = %p\n", + __func__, ep->ep.name, + list_entry(ep->queue.next, struct s3c_request, queue)); + + return -EAGAIN; + } + + dev = ep->dev; + DEBUG("%s: ep_num = %d, value = %d\n", __func__, ep_num, value); + + spin_lock_irqsave(&dev->lock, flags); + + if (value == 0) { + ep->stopped = 0; + s3c_udc_ep_clear_stall(ep); + } else { + if (ep_num == 0) + dev->ep0state = WAIT_FOR_SETUP; + + ep->stopped = 1; + s3c_udc_ep_set_stall(ep); + } + + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; +} + +void s3c_udc_ep_activate(struct s3c_ep *ep) +{ + u8 ep_num; + u32 ep_ctrl = 0, daintmsk = 0; + + ep_num = ep_index(ep); + + /* Read DEPCTLn register */ + if (ep_is_in(ep)) { + ep_ctrl = readl(®->in_endp[ep_num].diepctl); + daintmsk = 1 << ep_num; + } else { + ep_ctrl = readl(®->out_endp[ep_num].doepctl); + daintmsk = (1 << ep_num) << DAINT_OUT_BIT; + } + + DEBUG("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n", + __func__, ep_num, ep_ctrl, ep_is_in(ep)); + + /* If the EP is already active don't change the EP Control + * register. */ + if (!(ep_ctrl & DEPCTL_USBACTEP)) { + ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) | + (ep->bmAttributes << DEPCTL_TYPE_BIT); + ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | + (ep->ep.maxpacket << DEPCTL_MPS_BIT); + ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK); + + if (ep_is_in(ep)) { + writel(ep_ctrl, ®->in_endp[ep_num].diepctl); + DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + readl(®->in_endp[ep_num].diepctl)); + } else { + writel(ep_ctrl, ®->out_endp[ep_num].doepctl); + DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + readl(®->out_endp[ep_num].doepctl)); + } + } + + /* Unmask EP Interrtupt */ + writel(readl(®->daintmsk)|daintmsk, ®->daintmsk); + DEBUG("%s: DAINTMSK = 0x%x\n", __func__, readl(®->daintmsk)); + +} + +static int s3c_udc_clear_feature(struct usb_ep *_ep) +{ + struct s3c_udc *dev; + struct s3c_ep *ep; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + + dev = ep->dev; + DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n", + __func__, ep_num, ep_is_in(ep), clear_feature_flag); + + if (usb_ctrl->wLength != 0) { + DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n"); + return 1; + } + + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (usb_ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + DEBUG_SETUP("\tOFF:USB_DEVICE_REMOTE_WAKEUP\n"); + break; + + case USB_DEVICE_TEST_MODE: + DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n"); + /** @todo Add CLEAR_FEATURE for TEST modes. */ + break; + } + + s3c_udc_ep0_zlp(dev); + break; + + case USB_RECIP_ENDPOINT: + DEBUG_SETUP("\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n", + usb_ctrl->wValue); + + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { + if (ep_num == 0) { + s3c_udc_ep0_set_stall(ep); + return 0; + } + + s3c_udc_ep0_zlp(dev); + + s3c_udc_ep_clear_stall(ep); + s3c_udc_ep_activate(ep); + ep->stopped = 0; + + clear_feature_num = ep_num; + clear_feature_flag = 1; + } + break; + } + + return 0; +} + +static int s3c_udc_set_feature(struct usb_ep *_ep) +{ + struct s3c_udc *dev; + struct s3c_ep *ep; + u8 ep_num; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + dev = ep->dev; + + DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", + __func__, ep_num); + + if (usb_ctrl->wLength != 0) { + DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n"); + return 1; + } + + switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + switch (usb_ctrl->wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n"); + break; + case USB_DEVICE_B_HNP_ENABLE: + DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); + break; + + case USB_DEVICE_A_HNP_SUPPORT: + /* RH port supports HNP */ + DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n"); + break; + + case USB_DEVICE_A_ALT_HNP_SUPPORT: + /* other RH port does */ + DEBUG_SETUP("\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); + break; + } + + s3c_udc_ep0_zlp(dev); + return 0; + + case USB_RECIP_INTERFACE: + DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n"); + break; + + case USB_RECIP_ENDPOINT: + DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n"); + if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { + if (ep_num == 0) { + s3c_udc_ep0_set_stall(ep); + return 0; + } + ep->stopped = 1; + s3c_udc_ep_set_stall(ep); + } + + s3c_udc_ep0_zlp(dev); + return 0; + } + + return 1; +} + +/* + * WAIT_FOR_SETUP (OUT_PKT_RDY) + */ +void s3c_ep0_setup(struct s3c_udc *dev) +{ + struct s3c_ep *ep = &dev->ep[0]; + int i, bytes, is_in; + u8 ep_num; + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + /* read control req from fifo (8 bytes) */ + bytes = s3c_fifo_read(ep, (u32 *)usb_ctrl, 8); + + DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x" + "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n", + __func__, usb_ctrl->bRequestType, + (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", + usb_ctrl->bRequest, + usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); + +#ifdef DEBUG_S3C_UDC + { + int i, len = sizeof(*usb_ctrl); + char *p = usb_ctrl; + + printf("pkt = "); + for (i = 0; i < len; i++) { + printf("%02x", ((u8 *)p)[i]); + if ((i & 7) == 7) + printf(" "); + } + printf("\n"); + } +#endif + + + if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && + usb_ctrl->wLength != 1) { + DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid", + __func__); + DEBUG_SETUP("wLength = %d, setup returned\n", + usb_ctrl->wLength); + + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + return; + } else if (usb_ctrl->bRequest == BOT_RESET_REQUEST && + usb_ctrl->wLength != 0) { + /* Bulk-Only *mass storge reset of class-specific request */ + DEBUG_SETUP("%s:BOT Rest:invalid wLength =%d, setup returned\n", + __func__, usb_ctrl->wLength); + + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + return; + } + + /* Set direction of EP0 */ + if (likely(usb_ctrl->bRequestType & USB_DIR_IN)) { + ep->bEndpointAddress |= USB_DIR_IN; + is_in = 1; + + } else { + ep->bEndpointAddress &= ~USB_DIR_IN; + is_in = 0; + } + /* cope with automagic for some standard requests. */ + dev->req_std = (usb_ctrl->bRequestType & USB_TYPE_MASK) + == USB_TYPE_STANDARD; + dev->req_config = 0; + dev->req_pending = 1; + + /* Handle some SETUP packets ourselves */ + if (dev->req_std) { + switch (usb_ctrl->bRequest) { + case USB_REQ_SET_ADDRESS: + DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n", + __func__, usb_ctrl->wValue); + if (usb_ctrl->bRequestType + != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + + udc_set_address(dev, usb_ctrl->wValue); + return; + + case USB_REQ_SET_CONFIGURATION: + DEBUG_SETUP("=====================================\n"); + DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n", + __func__, usb_ctrl->wValue); + + if (usb_ctrl->bRequestType == USB_RECIP_DEVICE) { + reset_available = 1; + dev->req_config = 1; + } + break; + + case USB_REQ_GET_DESCRIPTOR: + DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n", + __func__); + break; + + case USB_REQ_SET_INTERFACE: + DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n", + __func__, usb_ctrl->wValue); + + if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE) { + reset_available = 1; + dev->req_config = 1; + } + break; + + case USB_REQ_GET_CONFIGURATION: + DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n", + __func__); + break; + + case USB_REQ_GET_STATUS: + if (!s3c_udc_get_status(dev, usb_ctrl)) + return; + + break; + + case USB_REQ_CLEAR_FEATURE: + ep_num = usb_ctrl->wIndex & 0x7f; + + if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep)) + return; + + break; + + case USB_REQ_SET_FEATURE: + ep_num = usb_ctrl->wIndex & 0x7f; + + if (!s3c_udc_set_feature(&dev->ep[ep_num].ep)) + return; + + break; + + default: + DEBUG_SETUP("%s: *** Default of usb_ctrl->bRequest=0x%x" + "happened.\n", __func__, usb_ctrl->bRequest); + break; + } + } + + + if (likely(dev->driver)) { + /* device-2-host (IN) or no data setup command, + * process immediately */ + DEBUG_SETUP("%s:usb_ctrlreq will be passed to fsg_setup()\n", + __func__); + + spin_unlock(&dev->lock); + i = dev->driver->setup(&dev->gadget, usb_ctrl); + spin_lock(&dev->lock); + + if (i < 0) { + if (dev->req_config) { + DEBUG_SETUP("\tconfig change 0x%02x fail %d?\n", + (u32)usb_ctrl->bRequest, i); + return; + } + + /* setup processing failed, force stall */ + s3c_udc_ep0_set_stall(ep); + dev->ep0state = WAIT_FOR_SETUP; + + DEBUG_SETUP("\tdev->driver->setup failed (%d)," + " bRequest = %d\n", + i, usb_ctrl->bRequest); + + + } else if (dev->req_pending) { + dev->req_pending = 0; + DEBUG_SETUP("\tdev->req_pending...\n"); + } + + DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]); + + } +} + +/* + * handle ep0 interrupt + */ +static void s3c_handle_ep0(struct s3c_udc *dev) +{ + if (dev->ep0state == WAIT_FOR_SETUP) { + DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__); + s3c_ep0_setup(dev); + + } else { + DEBUG_OUT_EP("%s: strange state!!(state = %s)\n", + __func__, state_names[dev->ep0state]); + } +} + +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep) +{ + DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep)); + if (ep_is_in(ep)) { + dev->ep0state = DATA_STATE_XMIT; + s3c_ep0_write(dev); + + } else { + dev->ep0state = DATA_STATE_RECV; + s3c_ep0_read(dev); + } +} diff --git a/include/usb/lin_gadget_compat.h b/include/usb/lin_gadget_compat.h new file mode 100644 index 0000000..9b31511 --- /dev/null +++ b/include/usb/lin_gadget_compat.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * This is a Linux kernel compatibility layer for USB Gadget + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __LIN_COMPAT_H__ +#define __LIN_COMPAT_H__ + +/* common */ +#define spin_lock_init(...) +#define spin_lock(...) +#define spin_lock_irqsave(lock, flags) do {flags = 1; } while (0) +#define spin_unlock(...) +#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0) +#define disable_irq(...) +#define enable_irq(...) + +#define mutex_init(...) +#define mutex_lock(...) +#define mutex_unlock(...) + +#define WARN_ON(x) if (x) {printf("WARNING in %s line %d\n" \ + , __FILE__, __LINE__); } + +#define KERN_WARNING +#define KERN_ERR +#define KERN_NOTICE +#define KERN_DEBUG + +#define GFP_KERNEL 0 + +#define IRQ_HANDLED 1 + +#define ENOTSUPP 524 /* Operation is not supported */ + +#define kmalloc(size, type) memalign(CONFIG_SYS_CACHELINE_SIZE, size) +#define kfree(addr) free(addr) +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) + +#define __iomem +#define min_t min +#define dma_cache_maint(addr, size, mode) cache_flush() +void cache_flush(void); + +#endif /* __LIN_COMPAT_H__ */ diff --git a/include/usb/s3c_udc.h b/include/usb/s3c_udc.h new file mode 100644 index 0000000..5537c4a --- /dev/null +++ b/include/usb/s3c_udc.h @@ -0,0 +1,165 @@ +/* + * drivers/usb/gadget/s3c_udc.h + * Samsung S3C on-chip full/high speed USB device controllers + * Copyright (C) 2005 for Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __S3C_USB_GADGET +#define __S3C_USB_GADGET + +#include <asm/errno.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/list.h> +#include <usb/lin_gadget_compat.h> + +/*-------------------------------------------------------------------------*/ +/* DMA bounce buffer size, 16K is enough even for mass storage */ +#define DMA_BUFFER_SIZE (4096*4) + +#define EP0_FIFO_SIZE 64 +#define EP_FIFO_SIZE 512 +#define EP_FIFO_SIZE2 1024 +/* ep0-control, ep1in-bulk, ep2out-bulk, ep3in-int */ +#define S3C_MAX_ENDPOINTS 4 +#define S3C_MAX_HW_ENDPOINTS 16 + +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 +#define WAIT_FOR_COMPLETE 5 +#define WAIT_FOR_OUT_COMPLETE 6 +#define WAIT_FOR_IN_COMPLETE 7 +#define WAIT_FOR_NULL_COMPLETE 8 + +#define TEST_J_SEL 0x1 +#define TEST_K_SEL 0x2 +#define TEST_SE0_NAK_SEL 0x3 +#define TEST_PACKET_SEL 0x4 +#define TEST_FORCE_ENABLE_SEL 0x5 + +/* ************************************************************************* */ +/* IO + */ + +enum ep_type { + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt +}; + +struct s3c_ep { + struct usb_ep ep; + struct s3c_udc *dev; + + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + unsigned long pio_irqs; + int len; + void *dma_buf; + + u8 stopped; + u8 bEndpointAddress; + u8 bmAttributes; + + enum ep_type ep_type; + int fifo_num; +}; + +struct s3c_request { + struct usb_request req; + struct list_head queue; +}; + +struct s3c_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct s3c_plat_otg_data *pdata; + + void *dma_buf[S3C_MAX_ENDPOINTS+1]; + dma_addr_t dma_addr[S3C_MAX_ENDPOINTS+1]; + + int ep0state; + struct s3c_ep ep[S3C_MAX_ENDPOINTS]; + + unsigned char usb_address; + + unsigned req_pending:1, req_std:1, req_config:1; +}; + +extern struct s3c_udc *the_controller; + +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN) +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) + +/*-------------------------------------------------------------------------*/ +/* #define DEBUG_UDC */ +#ifdef DEBUG_UDC +#define DBG(stuff...) printf("udc: " stuff) +#else +#define DBG(stuff...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_SETUP +#define DEBUG_SETUP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_SETUP(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_EP0 +#define DEBUG_EP0(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_EP0(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC +#define DEBUG(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_ISR +#define DEBUG_ISR(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_ISR(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_OUT_EP +#define DEBUG_OUT_EP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_OUT_EP(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_IN_EP +#define DEBUG_IN_EP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_IN_EP(fmt, args...) do {} while (0) +#endif + +#define ERR(stuff...) printf("ERR udc: " stuff) +#define WARN(stuff...) printf("WARNING udc: " stuff) +#define INFO(stuff...) printf("INFO udc: " stuff) + +extern void otg_phy_init(void); +extern void otg_phy_off(void); + +extern void s3c_udc_ep_set_stall(struct s3c_ep *ep); +extern int s3c_udc_probe(struct s3c_plat_otg_data *pdata); +#endif