diff options
Diffstat (limited to 'drivers/usb/gadget/s3c_udc_otg_xfer_dma.c')
-rw-r--r-- | drivers/usb/gadget/s3c_udc_otg_xfer_dma.c | 1455 |
1 files changed, 1455 insertions, 0 deletions
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..7554772 --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -0,0 +1,1455 @@ +/* drivers/usb/gadget/s3c_udc_otg_xfer_dma.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#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) + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE +#include <linux/usb/composite.h> +#endif +static u8 clear_feature_num; +static int clear_feature_flag; +static int set_conf_done; + +/* Bulk-Only Mass Storage Reset (class-specific request) */ +#define GET_MAX_LUN_REQUEST 0xFE +#define BOT_RESET_REQUEST 0xFF + +/* TEST MODE in set_feature request */ +#define TEST_SELECTOR_MASK 0xFF +#define TEST_PKT_SIZE 53 + +static u8 test_pkt[TEST_PKT_SIZE] __attribute__((aligned(8))) = { + /* JKJKJKJK x 9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK x 8 */ + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + /* JJJJKKKK x 8 */ + 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + /* JJJJJJJKKKKKKK x8 - '1' */ + 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + /* '1' + JJJJJJJK x 8 */ + 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, + /* {JKKKKKKK x 10},JK */ + 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E +}; + +static void s3c_udc_ep_set_stall(struct s3c_ep *ep); + +#if defined(CONFIG_BATTERY_SAMSUNG) || defined(CONFIG_BATTERY_SAMSUNG_S2PLUS) +u32 cable_connected; + +void s3c_udc_cable_connect(struct s3c_udc *dev) +{ + samsung_cable_check_status(1); + cable_connected = 1; +} + +void s3c_udc_cable_disconnect(struct s3c_udc *dev) +{ + if (cable_connected) { + samsung_cable_check_status(0); + cable_connected = 0; + } +} +#endif + +static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev) +{ + u32 ep_ctrl; + + __raw_writel(dev->usb_ctrl_dma, dev->regs + S3C_UDC_OTG_DIEPDMA(EP0_CON)); + __raw_writel((1<<19 | 0<<0), dev->regs + S3C_UDC_OTG_DIEPTSIZ(EP0_CON)); + + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + __raw_writel(ep_ctrl | DEPCTL_EPENA | DEPCTL_CNAK, + dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + + DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", + __func__, __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON))); +} + +static inline void s3c_udc_pre_setup(struct s3c_udc *dev) +{ + u32 ep_ctrl; + + DEBUG_IN_EP("%s : Prepare Setup packets.\n", __func__); + + __raw_writel((3<<29) | (1<<19) | sizeof(struct usb_ctrlrequest), + dev->regs + S3C_UDC_OTG_DOEPTSIZ(EP0_CON)); + __raw_writel(dev->usb_ctrl_dma, dev->regs + S3C_UDC_OTG_DOEPDMA(EP0_CON)); + + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DOEPCTL(EP0_CON)); + __raw_writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, + dev->regs + S3C_UDC_OTG_DOEPCTL(EP0_CON)); +} + +static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + struct s3c_udc *udc = ep->dev; + struct device *dev = &udc->dev->dev; + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + length = req->req.length - req->req.actual; + + req->req.dma = dma_map_single(dev, buf, + length, DMA_FROM_DEVICE); + req->mapped = 1; + + if (length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + + ctrl = __raw_readl(udc->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + + __raw_writel(virt_to_phys(buf), udc->regs + S3C_UDC_OTG_DOEPDMA(ep_num)); + __raw_writel((pktcnt<<19) | (length<<0), + udc->regs + S3C_UDC_OTG_DOEPTSIZ(ep_num)); + __raw_writel(DEPCTL_EPENA | DEPCTL_CNAK | ctrl, + udc->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + + 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, + __raw_readl(udc->regs + S3C_UDC_OTG_DOEPDMA(ep_num)), + __raw_readl(udc->regs + S3C_UDC_OTG_DOEPTSIZ(ep_num)), + __raw_readl(udc->regs + S3C_UDC_OTG_DOEPCTL(ep_num)), + buf, pktcnt, length); + return 0; +} + +static int setdma_tx(struct s3c_ep *ep, struct s3c_request *req) +{ + u32 *buf, ctrl = 0; + u32 length, pktcnt; + u32 ep_num = ep_index(ep); + struct s3c_udc *udc = ep->dev; + struct device *dev = &udc->dev->dev; + + buf = req->req.buf + req->req.actual; + prefetch(buf); + length = req->req.length - req->req.actual; + + if (ep_num == EP0_CON) + length = min(length, (u32)ep_maxpacket(ep)); + + req->req.actual += length; + + req->req.dma = dma_map_single(dev, buf, + length, DMA_TO_DEVICE); + req->mapped = 1; + + if (length == 0) + pktcnt = 1; + else + pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + +#ifdef DED_TX_FIFO + /* Write the FIFO number to be used for this endpoint */ + ctrl = __raw_readl(udc->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + ctrl &= ~DEPCTL_TXFNUM_MASK;; + ctrl |= (ep_num << DEPCTL_TXFNUM_BIT); + __raw_writel(ctrl , udc->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); +#endif + + __raw_writel(virt_to_phys(buf), udc->regs + S3C_UDC_OTG_DIEPDMA(ep_num)); + __raw_writel((pktcnt<<19)|(length<<0), udc->regs + S3C_UDC_OTG_DIEPTSIZ(ep_num)); + ctrl = __raw_readl(udc->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + __raw_writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, + udc->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + +#ifndef DED_TX_FIFO + ctrl = __raw_readl(udc->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + ctrl = (ctrl & ~(EP_MASK<<DEPCTL_NEXT_EP_BIT)) | + (ep_num<<DEPCTL_NEXT_EP_BIT); + __raw_writel(ctrl, udc->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); +#endif + + 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, + __raw_readl(udc->regs + S3C_UDC_OTG_DIEPDMA(ep_num)), + __raw_readl(udc->regs + S3C_UDC_OTG_DIEPTSIZ(ep_num)), + __raw_readl(udc->regs + S3C_UDC_OTG_DIEPCTL(ep_num)), + buf, pktcnt, length); + + req->written_bytes = 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, xfer_length, is_short = 0; + + 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 = __raw_readl(dev->regs + S3C_UDC_OTG_DOEPTSIZ(ep_num)); + + if (ep_num == EP0_CON) + xfer_size = (ep_tsr & 0x7f); + + else + xfer_size = (ep_tsr & 0x7fff); + + __dma_single_cpu_to_dev(req->req.buf, req->req.length, DMA_FROM_DEVICE); + xfer_length = req->req.length - xfer_size; + req->req.actual += min(xfer_length, req->req.length - req->req.actual); + is_short = (xfer_length < 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 == xfer_length) { + if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { + DEBUG_OUT_EP(" => Send ZLP\n"); + dev->ep0state = WAIT_FOR_SETUP; + s3c_udc_ep0_zlp(dev); + } 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); + } + } + } +} + +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, xfer_length, is_short = 0; + u32 last; + + 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); + + 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_SETUP; + + return; + } + + ep_tsr = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPTSIZ(ep_num)); + + if (ep_num == EP0_CON) + xfer_size = (ep_tsr & 0x7f); + else + xfer_size = (ep_tsr & 0x7fff); + + req->req.actual = req->req.length - xfer_size; + xfer_length = req->req.length - xfer_size; + req->req.actual += min(xfer_length, req->req.length - req->req.actual); + is_short = (xfer_length < ep->ep.maxpacket); + + 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 (req->req.actual == req->req.length) { + /* send ZLP when req.zero is set and the last packet is maxpacket */ + if (req->req.zero) { + req->req.zero = 0; + if (req->written_bytes == ep_maxpacket(ep)) { + setdma_tx(ep, req); + return; + } + } + 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 = __raw_readl(dev->regs + S3C_UDC_OTG_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 = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPINT(ep_num)); + DEBUG_IN_EP("\tEP%d-IN : DIEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + __raw_writel(ep_intr_status, dev->regs + S3C_UDC_OTG_DIEPINT(ep_num)); + + if (ep_intr_status & TRANSFER_DONE) { + complete_tx(dev, ep_num); + + if (ep_num == 0) { + if (dev->ep0state == WAIT_FOR_SETUP) + s3c_udc_pre_setup(dev); + + /* 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; + u32 ep_ctrl = 0; + ep_intr = __raw_readl(dev->regs + S3C_UDC_OTG_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 = __raw_readl(dev->regs + S3C_UDC_OTG_DOEPINT(ep_num)); + DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n", + ep_num, ep_intr_status); + + /* Interrupt Clear */ + __raw_writel(ep_intr_status, dev->regs + S3C_UDC_OTG_DOEPINT(ep_num)); + + if (ep_num == 0) { + if (ep_intr_status & + CTRL_OUT_EP_SETUP_PHASE_DONE) { + DEBUG_OUT_EP("\tSETUP" + "packet(transaction)" + "arrived\n"); + s3c_handle_ep0(dev); + } + + if (ep_intr_status & TRANSFER_DONE) { + complete_rx(dev, ep_num); + __raw_writel((3<<29)|(1 << 19)|sizeof(struct usb_ctrlrequest), + dev->regs + S3C_UDC_OTG_DOEPTSIZ(EP0_CON)); + __raw_writel(dev->usb_ctrl_dma, + dev->regs + S3C_UDC_OTG_DOEPDMA(EP0_CON)); + + ep_ctrl = readl(dev->regs + S3C_UDC_OTG_DOEPCTL(EP0_CON)); + __raw_writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_SNAK, + dev->regs + S3C_UDC_OTG_DOEPCTL(EP0_CON)); + } + + } else { + if (ep_intr_status & TRANSFER_DONE) + complete_rx(dev, ep_num); + } + } + ep_num++; + ep_intr >>= 1; + } +} + +/* + * usb client interrupt handler. + */ +static irqreturn_t 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 = __raw_readl(dev->regs + S3C_UDC_OTG_GINTSTS); + gintmsk = __raw_readl(dev->regs + S3C_UDC_OTG_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, + __raw_readl(dev->regs + S3C_UDC_OTG_DAINT), __raw_readl(dev->regs + S3C_UDC_OTG_DAINTMSK)); + + if (!intr_status) { + spin_unlock_irqrestore(&dev->lock, flags); + return IRQ_HANDLED; + } + + if (intr_status & INT_ENUMDONE) { + DEBUG_ISR("\tSpeed Detection interrupt\n"); + + __raw_writel(INT_ENUMDONE, dev->regs + S3C_UDC_OTG_GINTSTS); + usb_status = (__raw_readl(dev->regs + S3C_UDC_OTG_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); + } + wake_lock(&dev->usbd_wake_lock); + } + + if (intr_status & INT_EARLY_SUSPEND) { + DEBUG_ISR("\tEarly suspend interrupt\n"); + __raw_writel(INT_EARLY_SUSPEND, dev->regs + S3C_UDC_OTG_GINTSTS); + } + + if (intr_status & INT_SUSPEND) { + usb_status = __raw_readl(dev->regs + S3C_UDC_OTG_DSTS); + DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status); + __raw_writel(INT_SUSPEND, dev->regs + S3C_UDC_OTG_GINTSTS); + + if (dev->gadget.speed != USB_SPEED_UNKNOWN + && dev->driver + && dev->driver->suspend) { + + dev->driver->suspend(&dev->gadget); + } + } + + if (intr_status & INT_RESUME) { + DEBUG_ISR("\tResume interrupt\n"); + __raw_writel(INT_RESUME, dev->regs + S3C_UDC_OTG_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 = __raw_readl(dev->regs + S3C_UDC_OTG_GOTGCTL); + DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status); + __raw_writel(INT_RESET, dev->regs + S3C_UDC_OTG_GINTSTS); + +#if defined(CONFIG_MACH_M0_CMCC) + pr_info("[YSJ][%s] intr_status=0x%x, " + "gintmsk=0x%x, " + "usb_status=0x%x\n", + __func__, + intr_status, + gintmsk, + usb_status); +#endif + + set_conf_done = 0; + + if ((usb_status & 0xc0000) == (0x3 << 18)) { + if (reset_available) { + DEBUG_ISR("\t\tOTG core got reset (%d)!!\n", + reset_available); + reset_usbd(); + dev->ep0state = WAIT_FOR_SETUP; + reset_available = 0; + s3c_udc_pre_setup(dev); + } else { + reset_available = 1; + } + } else { +#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE + struct usb_composite_dev *cdev; +#endif + reset_available = 1; + DEBUG_ISR("\t\tRESET handling skipped\n"); + wake_lock_timeout(&dev->usbd_wake_lock, HZ * 5); + printk(KERN_DEBUG "usb: reset\n"); + /* report disconnect; the driver is already quiesced */ + if (dev->driver) { +#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE + cdev = get_gadget_data(&dev->gadget); + if (cdev != NULL) { + cdev->mute_switch = 0; + cdev->force_disconnect = 1; + } +#endif + spin_unlock(&dev->lock); +#if defined(CONFIG_MACH_M0_CMCC) + pr_info("[YSJ][%s] disconnect gadget", + __func__); +#endif + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); + } + +#if defined(CONFIG_BATTERY_SAMSUNG) || defined(CONFIG_BATTERY_SAMSUNG_S2PLUS) + s3c_udc_cable_disconnect(dev); +#endif + } + } + + 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\n", __func__); + 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); + + 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 = __raw_readl(dev->regs + S3C_UDC_OTG_GINTSTS); + DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", + __func__, gintsts); + + if (set_conf_done == 1) { + setdma_tx(ep, req); + } else { + done(ep, req, 0); + DEBUG("%s: Not yet Set_configureation," + "ep_num = %d, req = %p\n", + __func__, ep_num, req); + req = 0; + } + + } else { + gintsts = __raw_readl(dev->regs + S3C_UDC_OTG_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)) + 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, req); + + /* requests complete when all IN data is in the FIFO */ + if (is_last) { + ep->dev->ep0state = WAIT_FOR_SETUP; + return 1; + } + + return 0; +} + +/** + * 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 = __raw_readl(dev->regs + S3C_UDC_OTG_DCFG); + __raw_writel(address << 4 | ctrl, dev->regs + S3C_UDC_OTG_DCFG); + + s3c_udc_ep0_zlp(dev); + + DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", + __func__, address, __raw_readl(dev->regs + S3C_UDC_OTG_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 = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + __raw_writel(ep_ctrl, dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + + DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n", + __func__, ep_index(ep), __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON))); + /* + * 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(dev); +} + +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 */ + + dev->ep0state = WAIT_FOR_SETUP; + set_conf_done = 1; + s3c_udc_ep0_zlp(dev); + + DEBUG_EP0("%s: req.length = 0, bRequest = %d\n", + __func__, dev->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; + + 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); + + ret = write_fifo_ep0(ep, req); + + if (ret == 1) { + /* Last packet */ + dev->ep0state = WAIT_FOR_SETUP; + 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 __attribute__((aligned(8))); + +static int s3c_udc_get_status(struct s3c_udc *dev, + struct usb_ctrlrequest *crq) +{ + u8 ep_num = crq->wIndex & 0x7F; + u32 ep_ctrl; + + DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__); + + 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 = 0x0; + 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; + } + + __dma_single_cpu_to_dev(&g_status, 2, DMA_TO_DEVICE); + + __raw_writel(virt_to_phys(&g_status), dev->regs + S3C_UDC_OTG_DIEPDMA(EP0_CON)); + __raw_writel((1<<19)|(2<<0), dev->regs + S3C_UDC_OTG_DIEPTSIZ(EP0_CON)); + + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + __raw_writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + dev->ep0state = WAIT_FOR_SETUP; + + return 0; +} + +static void s3c_udc_ep_set_stall(struct s3c_ep *ep) +{ + struct s3c_udc *dev = ep->dev; + 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 = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + + /* set the disable and stall bits */ + if (ep_ctrl & DEPCTL_EPENA) + ep_ctrl |= DEPCTL_EPDIS; + + ep_ctrl |= DEPCTL_STALL; + + __raw_writel(ep_ctrl, dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num))); + + } else { + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + + /* set the stall bit */ + ep_ctrl |= DEPCTL_STALL; + + __raw_writel(ep_ctrl, dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, __raw_readl(dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num))); + } + + return; +} + +void s3c_udc_ep_clear_stall(struct s3c_ep *ep) +{ + struct s3c_udc *dev = ep->dev; + 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 = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + + /* 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 */ + } + + __raw_writel(ep_ctrl, dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n", + __func__, ep_num, __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num))); + + } else { + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + + /* 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 */ + } + + __raw_writel(ep_ctrl, dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n", + __func__, ep_num, __raw_readl(dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num))); + } + + 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 { + 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) +{ + struct s3c_udc *dev = ep->dev; + u8 ep_num; + u32 ep_ctrl = 0, daintmsk = 0; + + ep_num = ep_index(ep); + + /* Read DEPCTLn register */ + if (ep_is_in(ep)) { + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + daintmsk = 1 << ep_num; + } else { + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + 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)) { + __raw_writel(ep_ctrl, dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num)); + DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(ep_num))); + } else { + __raw_writel(ep_ctrl, dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num)); + DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n", + __func__, ep_num, ep_num, + __raw_readl(dev->regs + S3C_UDC_OTG_DOEPCTL(ep_num))); + } + } + + /* Unmask EP Interrtupt */ + __raw_writel(__raw_readl(dev->regs + S3C_UDC_OTG_DAINTMSK)|daintmsk, dev->regs + S3C_UDC_OTG_DAINTMSK); + DEBUG("%s: DAINTMSK = 0x%x\n", __func__, __raw_readl(dev->regs + S3C_UDC_OTG_DAINTMSK)); +} + +static int s3c_udc_clear_feature(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + u8 ep_num; + struct usb_ctrlrequest *usb_ctrl; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + usb_ctrl = ep->dev->usb_ctrl; + + 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("\tCLEAR_FEATURE:" + "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; + default: + s3c_udc_ep0_set_stall(ep); + return 1; + } + + s3c_udc_ep0_zlp(ep->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(ep->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; +} + +/* Set into the test mode for Test Mode set_feature request */ +static inline void set_test_mode(struct s3c_udc *dev) +{ + u32 ep_ctrl, dctl; + u8 test_selector = (dev->usb_ctrl->wIndex>>8) & TEST_SELECTOR_MASK; + + if (test_selector > 0 && test_selector < 6) { + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + + __raw_writel(1<<19 | 0<<0, dev->regs + S3C_UDC_OTG_DIEPTSIZ(EP0_CON)); + __raw_writel(ep_ctrl | DEPCTL_EPENA | DEPCTL_CNAK + | EP0_CON<<DEPCTL_NEXT_EP_BIT, + dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + } + + switch (test_selector) { + case TEST_J_SEL: + /* some delay is necessary like printk() or udelay() */ + printk(KERN_INFO "Test mode selector in set_feature request is" + "TEST J\n"); + + dctl = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL); + __raw_writel((dctl & ~(TEST_CONTROL_MASK)) | TEST_J_MODE, + dev->regs + S3C_UDC_OTG_DCTL); + break; + case TEST_K_SEL: + /* some delay is necessary like printk() or udelay() */ + printk(KERN_INFO "Test mode selector in set_feature request is" + "TEST K\n"); + + dctl = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL); + __raw_writel((dctl&~(TEST_CONTROL_MASK))|TEST_K_MODE, + dev->regs + S3C_UDC_OTG_DCTL); + break; + case TEST_SE0_NAK_SEL: + /* some delay is necessary like printk() or udelay() */ + printk(KERN_INFO "Test mode selector in set_feature request is" + "TEST SE0 NAK\n"); + + dctl = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL); + __raw_writel((dctl & ~(TEST_CONTROL_MASK)) | TEST_SE0_NAK_MODE, + dev->regs + S3C_UDC_OTG_DCTL); + break; + case TEST_PACKET_SEL: + /* some delay is necessary like printk() or udelay() */ + printk(KERN_INFO "Test mode selector in set_feature request is" + "TEST PACKET\n"); + + __dma_single_cpu_to_dev(test_pkt, TEST_PKT_SIZE, DMA_TO_DEVICE); + __raw_writel(virt_to_phys(test_pkt), dev->regs + S3C_UDC_OTG_DIEPDMA(EP0_CON)); + + ep_ctrl = __raw_readl(dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + + __raw_writel(1<<19 | TEST_PKT_SIZE<<0, dev->regs + S3C_UDC_OTG_DIEPTSIZ(EP0_CON)); + __raw_writel(ep_ctrl | DEPCTL_EPENA | DEPCTL_CNAK + | EP0_CON<<DEPCTL_NEXT_EP_BIT, + dev->regs + S3C_UDC_OTG_DIEPCTL(EP0_CON)); + + dctl = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL); + __raw_writel((dctl & ~(TEST_CONTROL_MASK)) | TEST_PACKET_MODE, + dev->regs + S3C_UDC_OTG_DCTL); + break; + case TEST_FORCE_ENABLE_SEL: + /* some delay is necessary like printk() or udelay() */ + printk(KERN_INFO "Test mode selector in set_feature request is" + "TEST FORCE ENABLE\n"); + + dctl = __raw_readl(dev->regs + S3C_UDC_OTG_DCTL); + __raw_writel((dctl & ~(TEST_CONTROL_MASK)) | TEST_FORCE_ENABLE_MODE, + dev->regs + S3C_UDC_OTG_DCTL); + break; + } +} + +static int s3c_udc_set_feature(struct usb_ep *_ep) +{ + struct s3c_ep *ep; + u8 ep_num; + struct usb_ctrlrequest *usb_ctrl; + + ep = container_of(_ep, struct s3c_ep, ep); + ep_num = ep_index(ep); + usb_ctrl = ep->dev->usb_ctrl; + + 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_TEST_MODE: + DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_TEST_MODE\n"); + set_test_mode(ep->dev); + 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_FEATURE:" + "USB_DEVICE_A_ALT_HNP_SUPPORT\n"); + break; + default: + s3c_udc_ep0_set_stall(ep); + return 1; + } + + s3c_udc_ep0_zlp(ep->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(ep->dev); + return 0; + } + + return 1; +} + +/* + * WAIT_FOR_SETUP (OUT_PKT_RDY) + */ +static void s3c_ep0_setup(struct s3c_udc *dev) +{ + struct s3c_ep *ep = &dev->ep[0]; + int i, is_in; + u8 ep_num; + struct usb_ctrlrequest *usb_ctrl = dev->usb_ctrl; + + /* Nuke all previous transfers */ + nuke(ep, -EPROTO); + + 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); + + if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && usb_ctrl->wLength != 1) { + DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid wLength = %d," + "setup returned\n", __func__, 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("\t%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 */ + 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; + } +#if defined(CONFIG_BATTERY_SAMSUNG) || defined(CONFIG_BATTERY_SAMSUNG_S2PLUS) + s3c_udc_cable_connect(dev); +#endif + 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 (dev->req_std) { + 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_ctrlrequest 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); + } + + /* 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); + } +} |