/* * drivers/usb/sunxi_usb/udc/sunxi_udc.c * (C) Copyright 2010-2015 * Allwinner Technology Co., Ltd. * javen, 2010-3-3, create this file * * usb device contoller driver * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sunxi_udc_config.h" #include "sunxi_udc_board.h" #include "sunxi_udc_debug.h" #include "sunxi_udc_dma.h" #include #include #include #include #if defined(CONFIG_AW_AXP) #include #include #endif #define DRIVER_DESC "SoftWinner USB Device Controller" #define DRIVER_VERSION "20080411" #define DRIVER_AUTHOR "SoftWinner USB Developer" static const char gadget_name[] = "sunxi_usb_udc"; static const char driver_desc[] = DRIVER_DESC; static u64 sunxi_udc_mask = DMA_BIT_MASK(32); static struct sunxi_udc *the_controller = NULL; static u32 usbd_port_no = 0; static sunxi_udc_io_t g_sunxi_udc_io; static u32 usb_connect = 0; static u32 is_controller_alive = 0; static u8 is_udc_enable = 0; /* is udc enable by gadget? */ static struct platform_device *g_udc_pdev = NULL; static __u32 dma_working = 0; atomic_t vfs_read_flag; atomic_t vfs_write_flag; unsigned int vfs_amount = 0; loff_t vfs_file_offset = 0; #define DMA_ADDR_INVALID (~(dma_addr_t)0) static u8 crq_bRequest = 0; static u8 crq_wIndex = 0; static const unsigned char TestPkt[54] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E, 0x00 }; /* debug */ static int g_queue_debug = 0; int g_dma_debug = 0; static int g_write_debug = 0; static int g_read_debug = 0; static int g_irq_debug = 0; static int g_msc_write_debug = 0; static int g_msc_read_debug = 0; static ssize_t show_ed_test(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_queue_debug); } static ssize_t ed_test(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { void __iomem *fifo = NULL; if(!strncmp(buf, "test_j_state", 12)){ USBC_EnterMode_Test_J(g_sunxi_udc_io.usb_bsp_hdle); DMSG_INFO("test_mode:%s\n", "test_j_state"); }else if(!strncmp(buf, "test_k_state", 12)){ USBC_EnterMode_Test_K(g_sunxi_udc_io.usb_bsp_hdle); DMSG_INFO("test_mode:%s\n", "test_k_state"); }else if(!strncmp(buf, "test_se0_nak", 12)){ USBC_EnterMode_Test_SE0_NAK(g_sunxi_udc_io.usb_bsp_hdle); DMSG_INFO("test_mode:%s\n", "test_se0_nak"); }else if(!strncmp(buf, "test_pack", 9)){ DMSG_INFO("test_mode___:%s\n", "test_pack"); fifo = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, 0); USBC_WritePacket(g_sunxi_udc_io.usb_bsp_hdle, fifo, 54, (u32 *)TestPkt); USBC_EnterMode_TestPacket(g_sunxi_udc_io.usb_bsp_hdle); USBC_Dev_WriteDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 0); }else if(!strncmp(buf, "disable_test_mode", 17)){ DMSG_INFO("start disable_test_mode\n"); USBC_EnterMode_Idle(g_sunxi_udc_io.usb_bsp_hdle); }else { DMSG_PANIC("ERR: test_mode Argment is invalid\n"); } return count; } static DEVICE_ATTR(otg_ed_test, 0644, show_ed_test, ed_test); static ssize_t show_udc_debug(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_queue_debug); } static ssize_t udc_queue_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int debug = 0; sscanf(buf, "%d", &debug); g_queue_debug = debug; return count; } static DEVICE_ATTR(queue_debug, 0644, show_udc_debug, udc_queue_debug); static ssize_t show_dma_debug(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_dma_debug); } static ssize_t udc_dma_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int debug = 0; sscanf(buf, "%d", &debug); g_dma_debug = debug; return count; } static DEVICE_ATTR(dma_debug, 0644, show_dma_debug, udc_dma_debug); static ssize_t show_write_debug(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_write_debug); } static ssize_t udc_write_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int debug = 0; sscanf(buf, "%d", &debug); g_write_debug = debug; return count; } static DEVICE_ATTR(write_debug, 0644, show_write_debug, udc_write_debug); static ssize_t show_read_debug(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_read_debug); } static ssize_t udc_read_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int debug = 0; sscanf(buf, "%d", &debug); g_read_debug = debug; return count; } static DEVICE_ATTR(read_debug, 0644, show_read_debug, udc_read_debug); static ssize_t show_irq_debug(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_irq_debug); } static ssize_t udc_irq_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int debug = 0; sscanf(buf, "%d", &debug); g_irq_debug = debug; return count; } static DEVICE_ATTR(irq_debug, 0644, show_irq_debug, udc_irq_debug); static ssize_t show_msc_write_debug(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_msc_write_debug); } static ssize_t udc_msc_write_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int debug = 0; sscanf(buf, "%d", &debug); g_msc_write_debug = debug; return count; } static DEVICE_ATTR(msc_write_debug, 0644, show_msc_write_debug, udc_msc_write_debug); static ssize_t show_msc_read_debug(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%u\n", g_msc_read_debug); } static ssize_t udc_msc_read_debug(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int debug = 0; sscanf(buf, "%d", &debug); g_msc_read_debug = debug; return count; } static DEVICE_ATTR(msc_read_debug, 0644, show_msc_read_debug, udc_msc_read_debug); /* function defination */ static void cfg_udc_command(enum sunxi_udc_cmd_e cmd); static void cfg_vbus_draw(unsigned int ma); static __u32 is_peripheral_active(void) { return is_controller_alive; } /* DMA transfer conditions: * 1. the driver support dma transfer * 2. not EP0 * 3. more than one packet */ #define big_req(req, ep) ((req->req.length != req->req.actual) \ ? ((req->req.length - req->req.actual) > ep->ep.maxpacket) \ : (req->req.length > ep->ep.maxpacket)) #define is_sunxi_udc_dma_capable(req, ep) (is_udc_support_dma() \ && big_req(req, ep) \ && req->req.dma_flag \ && ep->num) #define is_buffer_mapped(req, ep) (is_sunxi_udc_dma_capable(req, ep) && (req->map_state != UN_MAPPED)) /* Maps the buffer to dma */ static inline void sunxi_udc_map_dma_buffer(struct sunxi_udc_request *req, struct sunxi_udc *udc, struct sunxi_udc_ep *ep) { if (!is_sunxi_udc_dma_capable(req, ep)) { DMSG_PANIC("err: need not to dma map\n"); return; } req->map_state = UN_MAPPED; if (req->req.dma == DMA_ADDR_INVALID) { req->req.dma = dma_map_single( udc->controller, req->req.buf, req->req.length, (is_tx_ep(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); if (dma_mapping_error(udc->controller, req->req.dma)){ DMSG_PANIC("dma_mapping_error, %p, %x\n", req->req.buf, req->req.length); return; } req->map_state = SW_UDC_USB_MAPPED; } else { dma_sync_single_for_device(udc->controller, req->req.dma, req->req.length, (is_tx_ep(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); req->map_state = PRE_MAPPED; } return; } /* Unmap the buffer from dma and maps it back to cpu */ static inline void sunxi_udc_unmap_dma_buffer(struct sunxi_udc_request *req, struct sunxi_udc *udc, struct sunxi_udc_ep *ep) { if (!is_buffer_mapped(req, ep)) { //DMSG_PANIC("err: need not to dma ummap\n"); return; } if (req->req.dma == DMA_ADDR_INVALID) { DMSG_PANIC("not unmapping a never mapped buffer\n"); return; } if (req->map_state == SW_UDC_USB_MAPPED) { dma_unmap_single(udc->controller, req->req.dma, req->req.length, (is_tx_ep(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); req->req.dma = DMA_ADDR_INVALID; } else { /* PRE_MAPPED */ dma_sync_single_for_cpu(udc->controller, req->req.dma, req->req.length, (is_tx_ep(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); } req->map_state = UN_MAPPED; return; } /* disable usb module irq */ static void disable_irq_udc(struct sunxi_udc *dev) { //disable_irq(dev->irq_no); } /* enable usb module irq */ static void enable_irq_udc(struct sunxi_udc *dev) { //enable_irq(dev->irq_no); } static void sunxi_udc_done(struct sunxi_udc_ep *ep, struct sunxi_udc_request *req, int status) __releases(ep->dev->lock) __acquires(ep->dev->lock) { unsigned halted = ep->halted; if(g_queue_debug){ DMSG_INFO("d: (%s, %p, %d, %d)\n\n\n", ep->ep.name, &(req->req), req->req.length, req->req.actual); } list_del_init(&req->queue); if (likely (req->req.status == -EINPROGRESS)) req->req.status = status; else status = req->req.status; ep->halted = 1; if (is_sunxi_udc_dma_capable(req, ep)) { spin_unlock(&ep->dev->lock); sunxi_udc_unmap_dma_buffer(req, ep->dev, ep); req->req.complete(&ep->ep, &req->req); spin_lock(&ep->dev->lock); }else{ spin_unlock(&ep->dev->lock); req->req.complete(&ep->ep, &req->req); spin_lock(&ep->dev->lock); } ep->halted = halted; } static void sunxi_udc_nuke(struct sunxi_udc *udc, struct sunxi_udc_ep *ep, int status) { /* Sanity check */ if (&ep->queue == NULL) return; while (!list_empty (&ep->queue)) { struct sunxi_udc_request *req; req = list_entry (ep->queue.next, struct sunxi_udc_request,queue); DMSG_INFO_UDC("nuke: ep num is %d\n", ep->num); sunxi_udc_done(ep, req, status); } } static inline void sunxi_udc_clear_ep_state(struct sunxi_udc *dev) { unsigned i = 0; /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint * fifos, and pending transactions mustn't be continued in any case. */ for (i = 1; i < SW_UDC_ENDPOINTS; i++) { sunxi_udc_nuke(dev, &dev->ep[i], -ECONNABORTED); } } static inline int sunxi_udc_fifo_count_out(__hdle usb_bsp_hdle, __u8 ep_index) { if (ep_index) { return USBC_ReadLenFromFifo(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); } else { return USBC_ReadLenFromFifo(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); } } static inline int sunxi_udc_write_packet(void __iomem *fifo, struct sunxi_udc_request *req, unsigned max) { unsigned len = min(req->req.length - req->req.actual, max); void *buf = req->req.buf + req->req.actual; prefetch(buf); DMSG_DBG_UDC("W: req.actual(%d), req.length(%d), len(%d), total(%d)\n", req->req.actual, req->req.length, len, req->req.actual + len); req->req.actual += len; udelay(5); USBC_WritePacket(g_sunxi_udc_io.usb_bsp_hdle, fifo, len, buf); return len; } static int pio_write_fifo(struct sunxi_udc_ep *ep, struct sunxi_udc_request *req) { unsigned count = 0; int is_last = 0; u32 idx = 0; void __iomem *fifo_reg = 0; __s32 ret = 0; u8 old_ep_index = 0; idx = ep->bEndpointAddress & 0x7F; /* write data */ /* select ep */ old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, idx); /* select fifo */ fifo_reg = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, idx); count = sunxi_udc_write_packet(fifo_reg, req, ep->ep.maxpacket); /* check if the last packet */ /* last packet is often short (sometimes a zlp) */ if (count != ep->ep.maxpacket) is_last = 1; else if (req->req.length != req->req.actual || req->req.zero) is_last = 0; else is_last = 2; if (g_write_debug) { DMSG_INFO("pw: (0x%p, %d, %d)\n", &(req->req), req->req.length, req->req.actual); } if (idx) { /* ep1~4 */ ret = USBC_Dev_WriteDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, is_last); if (ret != 0) { DMSG_PANIC("ERR: USBC_Dev_WriteDataStatus, failed\n"); req->req.status = -EOVERFLOW; } } else { /* ep0 */ ret = USBC_Dev_WriteDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, is_last); if (ret != 0) { DMSG_PANIC("ERR: USBC_Dev_WriteDataStatus, failed\n"); req->req.status = -EOVERFLOW; } } USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); if (is_last) { if (!idx) { /* ep0 */ ep->dev->ep0state=EP0_IDLE; } sunxi_udc_done(ep,req, 0); is_last = 1; } return is_last; } static int dma_write_fifo(struct sunxi_udc_ep *ep, struct sunxi_udc_request *req) { u32 left_len = 0; u32 idx = 0; void __iomem *fifo_reg = 0; u8 old_ep_index = 0; idx = ep->bEndpointAddress & 0x7F; /* select ep */ old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, idx); /* select fifo */ fifo_reg = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, idx); /* auto_set, tx_mode, dma_tx_en, mode1 */ USBC_Dev_ConfigEpDma(ep->dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_TX); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); /* cut fragment part(??) */ left_len = req->req.length - req->req.actual; left_len = left_len - (left_len % ep->ep.maxpacket); ep->dma_working = 1; dma_working = 1; ep->dma_transfer_len = left_len; spin_unlock(&ep->dev->lock); sunxi_udc_dma_set_config(ep, req, (__u32)req->req.dma, left_len); sunxi_udc_dma_start(ep, fifo_reg, (__u32)req->req.dma, left_len); spin_lock(&ep->dev->lock); return 0; } /* return: 0 = still running, 1 = completed, negative = errno */ static int sunxi_udc_write_fifo(struct sunxi_udc_ep *ep, struct sunxi_udc_request *req) { if (ep->dma_working) { if (g_dma_debug) { struct sunxi_udc_request *req_next = NULL; DMSG_PANIC("ERR: dma is busy, write fifo. ep(0x%p, %d), req(0x%p, 0x%p, 0x%p, %d, %d)\n\n", ep, ep->num, req, &(req->req), req->req.buf, req->req.length, req->req.actual); if (likely (!list_empty(&ep->queue))) { req_next = list_entry(ep->queue.next, struct sunxi_udc_request, queue); } else { req_next = NULL; } if (req_next) { DMSG_PANIC("ERR: dma is busy, write fifo. req(0x%p, 0x%p, 0x%p, %d, %d)\n\n", req_next, &(req_next->req), req_next->req.buf, req_next->req.length, req_next->req.actual); } } return 0; } if (is_sunxi_udc_dma_capable(req, ep)) return dma_write_fifo(ep, req); else return pio_write_fifo(ep, req); } static inline int sunxi_udc_read_packet(void __iomem *fifo, void *buf, struct sunxi_udc_request *req, unsigned avail) { unsigned len = 0; len = min(req->req.length - req->req.actual, avail); req->req.actual += len; DMSG_DBG_UDC("R: req.actual(%d), req.length(%d), len(%d), total(%d)\n", req->req.actual, req->req.length, len, req->req.actual + len); USBC_ReadPacket(g_sunxi_udc_io.usb_bsp_hdle, fifo, len, buf); return len; } /* return: 0 = still running, 1 = completed, negative = errno */ static int pio_read_fifo(struct sunxi_udc_ep *ep, struct sunxi_udc_request *req) { void *buf = NULL; unsigned bufferspace = 0; int is_last = 1; unsigned avail = 0; int fifo_count = 0; u32 idx = 0; void __iomem *fifo_reg = 0; __s32 ret = 0; u8 old_ep_index = 0; idx = ep->bEndpointAddress & 0x7F; /* select fifo */ fifo_reg = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, idx); if (!req->req.length) { DMSG_PANIC("ERR: req->req.length == 0\n"); return 1; } buf = req->req.buf + req->req.actual; bufferspace = req->req.length - req->req.actual; if (!bufferspace) { DMSG_PANIC("ERR: buffer full!\n"); return -1; } /* select ep */ old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, idx); fifo_count = sunxi_udc_fifo_count_out(g_sunxi_udc_io.usb_bsp_hdle, idx); if (fifo_count > ep->ep.maxpacket) { avail = ep->ep.maxpacket; } else { avail = fifo_count; } fifo_count = sunxi_udc_read_packet(fifo_reg, buf, req, avail); /* checking this with ep0 is not accurate as we already * read a control request */ if (idx != 0 && fifo_count < ep->ep.maxpacket) { is_last = 1; /* overflowed this request? flush extra data */ if (fifo_count != avail) req->req.status = -EOVERFLOW; } else { is_last = (req->req.length <= req->req.actual) ? 1 : 0; } if (g_read_debug) { DMSG_INFO("pr: (0x%p, %d, %d)\n", &(req->req), req->req.length, req->req.actual); } if (idx) { ret = USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX, is_last); if (ret != 0) { DMSG_PANIC("ERR: pio_read_fifo: USBC_Dev_WriteDataStatus, failed\n"); req->req.status = -EOVERFLOW; } } else { ret = USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, is_last); if (ret != 0) { DMSG_PANIC("ERR: pio_read_fifo: USBC_Dev_WriteDataStatus, failed\n"); req->req.status = -EOVERFLOW; } } USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); if (is_last) { if (!idx) { ep->dev->ep0state = EP0_IDLE; } sunxi_udc_done(ep, req, 0); is_last = 1; } return is_last; } static int dma_read_fifo(struct sunxi_udc_ep *ep, struct sunxi_udc_request *req) { u32 left_len = 0; u32 idx = 0; void __iomem *fifo_reg = 0; u8 old_ep_index = 0; idx = ep->bEndpointAddress & 0x7F; /* select ep */ old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, idx); /* select fifo */ fifo_reg = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, idx); /* auto_set, tx_mode, dma_tx_en, mode1 */ USBC_Dev_ConfigEpDma(ep->dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_RX); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); /* cut fragment packet part */ left_len = req->req.length - req->req.actual; left_len = left_len - (left_len % ep->ep.maxpacket); if (g_dma_debug) { DMSG_INFO("dr: (0x%p, %d, %d)\n", &(req->req), req->req.length, req->req.actual); } ep->dma_working = 1; dma_working = 1; ep->dma_transfer_len = left_len; spin_unlock(&ep->dev->lock); sunxi_udc_dma_set_config(ep, req, (__u32)req->req.dma, left_len); sunxi_udc_dma_start(ep, fifo_reg, (__u32)req->req.dma, left_len); spin_lock(&ep->dev->lock); return 0; } /* return: 0 = still running, 1 = completed, negative = errno */ static int sunxi_udc_read_fifo(struct sunxi_udc_ep *ep, struct sunxi_udc_request *req) { if (ep->dma_working) { if (g_dma_debug) { struct sunxi_udc_request *req_next = NULL; DMSG_PANIC("ERR: dma is busy, read fifo. ep(0x%p, %d), req(0x%p, 0x%p, 0x%p, %d, %d)\n\n", ep, ep->num, req, &(req->req), req->req.buf, req->req.length, req->req.actual); if (likely (!list_empty(&ep->queue))) { req_next = list_entry(ep->queue.next, struct sunxi_udc_request, queue); } else { req_next = NULL; } if (req_next) { DMSG_PANIC("ERR: dma is busy, read fifo. req(0x%p, 0x%p, 0x%p, %d, %d)\n\n", req_next, &(req_next->req), req_next->req.buf, req_next->req.length, req_next->req.actual); } } return 0; } if (is_sunxi_udc_dma_capable(req, ep)) { return dma_read_fifo(ep, req); } else { return pio_read_fifo(ep, req); } } static int sunxi_udc_read_fifo_crq(struct usb_ctrlrequest *crq) { u32 fifo_count = 0; u32 i = 0; void *pOut = crq; void __iomem *fifo = 0; fifo = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, 0); fifo_count = USBC_ReadLenFromFifo(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); if (fifo_count != 8) { i = 0; while(i < 16 && (fifo_count != 8) ) { fifo_count = USBC_ReadLenFromFifo(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); i++; } if (i >= 16) { DMSG_PANIC("ERR: get ep0 fifo len failed\n"); } } return USBC_ReadPacket(g_sunxi_udc_io.usb_bsp_hdle, fifo, fifo_count, pOut); } static int sunxi_udc_get_status(struct sunxi_udc *dev, struct usb_ctrlrequest *crq) { u16 status = 0; u8 buf[8]; u8 ep_num = crq->wIndex & 0x7F; u8 is_in = crq->wIndex & USB_DIR_IN; void __iomem *fifo = 0; u8 old_ep_index = 0; int ret = 0; switch (crq->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE: buf[0] = 0x00; buf[1] = 0x00; break; case USB_RECIP_DEVICE: status = dev->devstatus; buf[0] = 0x01; buf[1] = 0x00; break; case USB_RECIP_ENDPOINT: if (ep_num > 4 || crq->wLength > 2) { return 1; } old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, ep_num); if (ep_num == 0) { status = USBC_Dev_IsEpStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); } else { if (is_in) { ret = readw(g_sunxi_udc_io.usb_vbase + USBC_REG_o_TXCSR); status = ret & (0x1 << USBC_BP_TXCSR_D_SEND_STALL); } else { ret = readw(g_sunxi_udc_io.usb_vbase + USBC_REG_o_RXCSR); status = ret & (0x1 << USBC_BP_RXCSR_D_SEND_STALL); } } status = status ? 1 : 0; if (status) { buf[0] = 0x01; buf[1] = 0x00; } else { buf[0] = 0x00; buf[1] = 0x00; } USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); break; default: return 1; } /* Seems to be needed to get it working. ouch :( */ udelay(5); USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 0); fifo = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, 0); USBC_WritePacket(g_sunxi_udc_io.usb_bsp_hdle, fifo, crq->wLength, buf); USBC_Dev_WriteDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); return 0; } static int sunxi_udc_set_halt(struct usb_ep *_ep, int value); static int sunxi_udc_set_halt_ex(struct usb_ep *_ep, int value, int is_in); static void sunxi_udc_handle_ep0_idle(struct sunxi_udc *dev, struct sunxi_udc_ep *ep, struct usb_ctrlrequest *crq, u32 ep0csr) { int len = 0, ret = 0, tmp = 0; int is_in = 0; /* start control request? */ if (!USBC_Dev_IsReadDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0)) { DMSG_WRN("ERR: data is ready, can not read data.\n"); return; } sunxi_udc_nuke(dev, ep, -EPROTO); len = sunxi_udc_read_fifo_crq(crq); if (len != sizeof(*crq)) { //SG_PANIC("setup begin: fifo READ ERROR" // wanted %x bytes got %x. Stalling out...\n", //izeof(*crq), len); USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 0); USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); return; } DMSG_DBG_UDC("ep0: bRequest = %d bRequestType %d wLength = %d\n", crq->bRequest, crq->bRequestType, crq->wLength); /* cope with automagic for some standard requests. */ dev->req_std = ((crq->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD); dev->req_config = 0; dev->req_pending = 1; if(dev->req_std){ //standard request switch (crq->bRequest) { case USB_REQ_SET_CONFIGURATION: DMSG_DBG_UDC("USB_REQ_SET_CONFIGURATION ... \n"); if (crq->bRequestType == USB_RECIP_DEVICE) { dev->req_config = 1; } break; case USB_REQ_SET_INTERFACE: DMSG_DBG_UDC("USB_REQ_SET_INTERFACE ... \n"); if (crq->bRequestType == USB_RECIP_INTERFACE) { dev->req_config = 1; } break; case USB_REQ_SET_ADDRESS: DMSG_DBG_UDC("USB_REQ_SET_ADDRESS ... \n"); if (crq->bRequestType == USB_RECIP_DEVICE) { tmp = crq->wValue & 0x7F; dev->address = tmp; //rx receive over, dataend, tx_pakect ready USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); dev->ep0state = EP0_END_XFER; crq_bRequest = USB_REQ_SET_ADDRESS; return; } break; case USB_REQ_GET_STATUS: DMSG_DBG_UDC("USB_REQ_GET_STATUS ... \n"); if (!sunxi_udc_get_status(dev, crq)) { return; } break; case USB_REQ_CLEAR_FEATURE: //--<1>--data direction must be host to device if(x_test_bit(crq->bRequestType, 7)){ DMSG_PANIC("USB_REQ_CLEAR_FEATURE: data is not host to device\n"); break; } USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); //--<3>--data stage if(crq->bRequestType == USB_RECIP_DEVICE){ /* wValue 0-1 */ if(crq->wValue){ dev->devstatus &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); }else{ int k = 0; for(k = 0;k < SW_UDC_ENDPOINTS;k++){ is_in = crq->wIndex & USB_DIR_IN; sunxi_udc_set_halt_ex(&dev->ep[k].ep, 0, is_in); } } }else if(crq->bRequestType == USB_RECIP_INTERFACE){ //--<2>--token stage over //do nothing }else if(crq->bRequestType == USB_RECIP_ENDPOINT){ //--<3>--release the forbidden of ep //sunxi_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0); //dev->devstatus &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); /* wValue 0-1 */ if(crq->wValue){ dev->devstatus &= ~(1 << USB_DEVICE_REMOTE_WAKEUP); }else{ int k = 0; is_in = crq->wIndex & USB_DIR_IN; for (k = 0; k < SW_UDC_ENDPOINTS; k++) { if (dev->ep[k].bEndpointAddress == (crq->wIndex & 0xff)) sunxi_udc_set_halt_ex(&dev->ep[k].ep, 0, is_in); } } }else{ DMSG_PANIC("PANIC : nonsupport set feature request. (%d)\n", crq->bRequestType); USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); } dev->ep0state = EP0_IDLE; return; //break; case USB_REQ_SET_FEATURE: //--<1>--data direction must be host to device if(x_test_bit(crq->bRequestType, 7)){ DMSG_PANIC("USB_REQ_SET_FEATURE: data is not host to device\n"); break; } //--<3>--data stage if(crq->bRequestType == USB_RECIP_DEVICE){ if((crq->wValue == USB_DEVICE_TEST_MODE) ){ //setup packet receive over switch (crq->wIndex){ case SUNXI_UDC_TEST_J: USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); dev->ep0state = EP0_END_XFER; crq_wIndex = TEST_J; crq_bRequest = USB_REQ_SET_FEATURE; return; case SUNXI_UDC_TEST_K: USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); dev->ep0state = EP0_END_XFER; crq_wIndex = TEST_K; crq_bRequest = USB_REQ_SET_FEATURE; return; case SUNXI_UDC_TEST_SE0_NAK: USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); dev->ep0state = EP0_END_XFER; crq_wIndex = TEST_SE0_NAK; crq_bRequest = USB_REQ_SET_FEATURE; return; case SUNXI_UDC_TEST_PACKET: USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); dev->ep0state = EP0_END_XFER; crq_wIndex = TEST_PACKET; crq_bRequest = USB_REQ_SET_FEATURE; return; default: break; } } USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); dev->devstatus |= (1 << USB_DEVICE_REMOTE_WAKEUP); }else if(crq->bRequestType == USB_RECIP_INTERFACE){ //--<2>--token stage over USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); //do nothing }else if(crq->bRequestType == USB_RECIP_ENDPOINT){ //--<3>--forbidden ep int k = 0; is_in = crq->wIndex & USB_DIR_IN; USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); for (k = 0; k < SW_UDC_ENDPOINTS; k++) { if (dev->ep[k].bEndpointAddress == (crq->wIndex & 0xff)) sunxi_udc_set_halt_ex(&dev->ep[k].ep, 1, is_in); } }else{ DMSG_PANIC("PANIC : nonsupport set feature request. (%d)\n", crq->bRequestType); USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); } dev->ep0state = EP0_IDLE; return; //break; default: /* only receive setup_data packet, cannot set DataEnd */ USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 0); break; } }else{ USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 0); } if(crq->bRequestType & USB_DIR_IN){ dev->ep0state = EP0_IN_DATA_PHASE; }else{ dev->ep0state = EP0_OUT_DATA_PHASE; } if(!dev->driver) return; spin_unlock(&dev->lock); ret = dev->driver->setup(&dev->gadget, crq); spin_lock(&dev->lock); if (ret < 0) { if (dev->req_config) { DMSG_PANIC("ERR: config change %02x fail %d?\n", crq->bRequest, ret); return; } if (ret == -EOPNOTSUPP) { DMSG_PANIC("ERR: Operation not supported\n"); } else { DMSG_PANIC("ERR: dev->driver->setup failed. (%d)\n", ret); } udelay(5); USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); dev->ep0state = EP0_IDLE; /* deferred i/o == no response yet */ } else if (dev->req_pending) { //DMSG_PANIC("ERR: dev->req_pending... what now?\n"); dev->req_pending=0; } if (crq->bRequest == USB_REQ_SET_CONFIGURATION || crq->bRequest == USB_REQ_SET_INTERFACE) { //rx_packet receive over USBC_Dev_ReadDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 1); } return; } static void sunxi_udc_handle_ep0(struct sunxi_udc *dev) { u32 ep0csr = 0; struct sunxi_udc_ep *ep = &dev->ep[0]; struct sunxi_udc_request *req = NULL; struct usb_ctrlrequest crq; DMSG_DBG_UDC("sunxi_udc_handle_ep0--1--\n"); if (list_empty(&ep->queue)) { req = NULL; } else { req = list_entry(ep->queue.next, struct sunxi_udc_request, queue); } DMSG_DBG_UDC("sunxi_udc_handle_ep0--2--\n"); /* We make the assumption that sunxi_udc_UDC_IN_CSR1_REG equal to * sunxi_udc_UDC_EP0_CSR_REG when index is zero */ USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, 0); /* clear stall status */ if (USBC_Dev_IsEpStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0)) { DMSG_PANIC("ERR: ep0 stall\n"); sunxi_udc_nuke(dev, ep, -EPIPE); USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); dev->ep0state = EP0_IDLE; return; } /* clear setup end */ if (USBC_Dev_Ctrl_IsSetupEnd(g_sunxi_udc_io.usb_bsp_hdle)) { DMSG_PANIC("handle_ep0: ep0 setup end\n"); sunxi_udc_nuke(dev, ep, 0); USBC_Dev_Ctrl_ClearSetupEnd(g_sunxi_udc_io.usb_bsp_hdle); dev->ep0state = EP0_IDLE; } DMSG_DBG_UDC("sunxi_udc_handle_ep0--3--%d\n", dev->ep0state); ep0csr = USBC_Readw(USBC_REG_CSR0(g_sunxi_udc_io.usb_vbase)); switch (dev->ep0state) { case EP0_IDLE: sunxi_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); break; case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ DMSG_DBG_UDC("EP0_IN_DATA_PHASE ... what now?\n"); if (!USBC_Dev_IsWriteDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0) && req) { sunxi_udc_write_fifo(ep, req); } break; case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ DMSG_DBG_UDC("EP0_OUT_DATA_PHASE ... what now?\n"); if (USBC_Dev_IsReadDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0) && req ) { sunxi_udc_read_fifo(ep,req); } break; case EP0_END_XFER: DMSG_DBG_UDC("EP0_END_XFER ... what now?\n"); DMSG_DBG_UDC("crq_bRequest = 0x%x\n", crq_bRequest); switch (crq_bRequest) { case USB_REQ_SET_ADDRESS: USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, 0); USBC_Dev_Ctrl_ClearSetupEnd(g_sunxi_udc_io.usb_bsp_hdle); USBC_Dev_SetAddress(g_sunxi_udc_io.usb_bsp_hdle, dev->address); DMSG_INFO_UDC("Set address %d\n", dev->address); break; case USB_REQ_SET_FEATURE: switch (crq_wIndex){ case TEST_J: USBC_EnterMode_Test_J(g_sunxi_udc_io.usb_bsp_hdle); break; case TEST_K: USBC_EnterMode_Test_K(g_sunxi_udc_io.usb_bsp_hdle); break; case TEST_SE0_NAK: USBC_EnterMode_Test_SE0_NAK(g_sunxi_udc_io.usb_bsp_hdle); break; case TEST_PACKET: { void __iomem *fifo = 0; fifo = USBC_SelectFIFO(g_sunxi_udc_io.usb_bsp_hdle, 0); USBC_WritePacket(g_sunxi_udc_io.usb_bsp_hdle, fifo, 54, (u32 *)TestPkt); USBC_EnterMode_TestPacket(g_sunxi_udc_io.usb_bsp_hdle); USBC_Dev_WriteDataStatus(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0, 0); } break; default: break; } crq_wIndex = 0; break; default: break; } crq_bRequest = 0; dev->ep0state = EP0_IDLE; break; case EP0_STALL: DMSG_DBG_UDC("EP0_STALL ... what now?\n"); dev->ep0state = EP0_IDLE; break; } DMSG_DBG_UDC("sunxi_udc_handle_ep0--4--%d\n", dev->ep0state); return; } static void sunxi_udc_handle_ep(struct sunxi_udc_ep *ep) { struct sunxi_udc_request *req = NULL; int is_in = ep->bEndpointAddress & USB_DIR_IN; u32 idx = 0; u8 old_ep_index = 0; /* see sunxi_udc_queue. */ if (likely (!list_empty(&ep->queue))) { req = list_entry(ep->queue.next, struct sunxi_udc_request, queue); } else { req = NULL; } if(g_irq_debug){ DMSG_INFO("e: (%s), tx_csr=0x%x\n", ep->ep.name, USBC_Readw(USBC_REG_TXCSR(g_sunxi_udc_io.usb_vbase))); if(req){ DMSG_INFO("req: (0x%p, %d, %d)\n", &(req->req), req->req.length, req->req.actual); } } idx = ep->bEndpointAddress & 0x7F; old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, idx); if (is_in) { if (USBC_Dev_IsEpStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX)) { DMSG_PANIC("ERR: tx ep(%d) is stall\n", idx); USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); goto end; } } else { if (USBC_Dev_IsEpStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX)) { DMSG_PANIC("ERR: rx ep(%d) is stall\n", idx); USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); goto end; } } if (req) { if (is_in) { if (!USBC_Dev_IsWriteDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX)) { sunxi_udc_write_fifo(ep, req); } } else { if (USBC_Dev_IsReadDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX)) { sunxi_udc_read_fifo(ep,req); } } } end: USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); return; } /* mask the useless irq, save disconect, reset, resume, suspend */ static u32 filtrate_irq_misc(u32 irq_misc) { u32 irq = irq_misc; irq &= ~(USBC_INTUSB_VBUS_ERROR | USBC_INTUSB_SESSION_REQ | USBC_INTUSB_CONNECT | USBC_INTUSB_SOF); USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_VBUS_ERROR); USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_SESSION_REQ); USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_CONNECT); USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_SOF); return irq; } static void clear_all_irq(void) { USBC_INT_ClearEpPendingAll(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); USBC_INT_ClearEpPendingAll(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); USBC_INT_ClearMiscPendingAll(g_sunxi_udc_io.usb_bsp_hdle); } static void throw_away_all_urb(struct sunxi_udc *dev) { int k = 0; DMSG_INFO_UDC("irq: reset happen, throw away all urb\n"); for(k = 0; k < SW_UDC_ENDPOINTS; k++){ sunxi_udc_nuke(dev, (struct sunxi_udc_ep * )&(dev->ep[k]), -ECONNRESET); } } /* clear all dma status of the EP, called when dma exception */ static void sunxi_udc_clean_dma_status(struct sunxi_udc_ep *ep) { u8 ep_index = 0; u8 old_ep_index = 0; struct sunxi_udc_request *req = NULL; ep_index = ep->bEndpointAddress & 0x7F; old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, ep_index); if ((ep->bEndpointAddress) & USB_DIR_IN) { // dma_mode1 /* clear ep dma status */ USBC_Dev_ClearEpDma(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); /* select bus to pio */ sunxi_udc_switch_bus_to_pio(ep, 1); } else { // dma_mode0 /* clear ep dma status */ USBC_Dev_ClearEpDma(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); /* select bus to pio */ sunxi_udc_switch_bus_to_pio(ep, 0); } USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); /* done req */ while(likely (!list_empty(&ep->queue))) { req = list_entry(ep->queue.next, struct sunxi_udc_request, queue); if (req) { req->req.status = -ECONNRESET; req->req.actual = 0; sunxi_udc_done(ep, req, -ECONNRESET); } } ep->dma_working = 0; dma_working = 0; return; } static void sunxi_udc_stop_dma_work(struct sunxi_udc *dev, u32 unlock) { __u32 i = 0; struct sunxi_udc_ep *ep = NULL; for(i = 0; i < SW_UDC_ENDPOINTS; i++) { ep = &dev->ep[i]; if (sunxi_udc_dma_is_busy(ep)) { DMSG_PANIC("wrn: ep(%d) must stop working\n", i); if (unlock){ spin_unlock(&ep->dev->lock); sunxi_udc_dma_stop(ep); spin_lock(&ep->dev->lock); }else { sunxi_udc_dma_stop(ep); } #ifdef SW_UDC_DMA_INNER ep->dev->dma_hdle = NULL; #else ep->dev->sunxi_udc_dma[ep->num].is_start = 0; #endif ep->dma_transfer_len = 0; sunxi_udc_clean_dma_status(ep); } } return; } void sunxi_udc_dma_completion(struct sunxi_udc *dev, struct sunxi_udc_ep *ep, struct sunxi_udc_request *req) { unsigned long flags = 0; __u8 old_ep_index = 0; __u32 dma_transmit_len = 0; int is_complete = 0; struct sunxi_udc_request *req_next = NULL; if (dev == NULL || ep == NULL || req == NULL) { DMSG_PANIC("ERR: argment invaild. (0x%p, 0x%p, 0x%p)\n", dev, ep, req); return; } if (!ep->dma_working) { DMSG_PANIC("ERR: dma is not work, can not callback\n"); return; } sunxi_udc_unmap_dma_buffer(req, dev, ep); spin_lock_irqsave(&dev->lock, flags); old_ep_index = USBC_GetActiveEp(dev->sunxi_udc_io->usb_bsp_hdle); USBC_SelectActiveEp(dev->sunxi_udc_io->usb_bsp_hdle, ep->num); if ((ep->bEndpointAddress) & USB_DIR_IN) { //tx, dma_mode1 while(USBC_Dev_IsWriteDataReady_FifoEmpty(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_TX)); USBC_Dev_ClearEpDma(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_TX); } else { //rx, dma_mode0 USBC_Dev_ClearEpDma(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_RX); } dma_transmit_len = sunxi_udc_dma_transmit_length(ep); if (dma_transmit_len < req->req.length) { if ((ep->bEndpointAddress) & USB_DIR_IN) { USBC_Dev_ClearEpDma(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_TX); } else { USBC_Dev_ClearEpDma(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_RX); } } if (g_dma_debug) { DMSG_INFO("di: (0x%p, %d, %d),(%d,%d)\n", &(req->req), req->req.length, req->req.actual, ep->bEndpointAddress, USB_DIR_IN); } ep->dma_working = 0; dma_working = 0; ep->dma_transfer_len = 0; /* if current data transfer not complete, then go on */ req->req.actual += dma_transmit_len; if(req->req.length > req->req.actual){ if(((ep->bEndpointAddress & USB_DIR_IN) != 0) && !USBC_Dev_IsWriteDataReady_FifoEmpty(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_TX)){ if(pio_write_fifo(ep, req)){ req = NULL; is_complete = 1; } }else if(((ep->bEndpointAddress & USB_DIR_IN) == 0) && USBC_Dev_IsReadDataReady(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_RX)){ if(pio_read_fifo(ep, req)){ req = NULL; is_complete = 1; } } }else{ /* if DMA transfer data over, then done */ sunxi_udc_done(ep, req, 0); is_complete = 1; } /* start next transfer */ if(is_complete){ if(likely (!list_empty(&ep->queue))){ req_next = list_entry(ep->queue.next, struct sunxi_udc_request, queue); }else{ req_next = NULL; } if(req_next){ if((ep->bEndpointAddress & USB_DIR_IN) != 0) { while(USBC_Dev_IsWriteDataReady_FifoEmpty(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_TX)); sunxi_udc_write_fifo(ep, req_next); }else if(((ep->bEndpointAddress & USB_DIR_IN) == 0) && USBC_Dev_IsReadDataReady(dev->sunxi_udc_io->usb_bsp_hdle, USBC_EP_TYPE_RX)){ sunxi_udc_read_fifo(ep, req_next); } } } USBC_SelectActiveEp(dev->sunxi_udc_io->usb_bsp_hdle, old_ep_index); spin_unlock_irqrestore(&dev->lock, flags); return; } static irqreturn_t sunxi_udc_irq(int dummy, void *_dev) { struct sunxi_udc *dev = _dev; int usb_irq = 0; int tx_irq = 0; int rx_irq = 0; int i = 0; int dma_irq = 0; u32 old_ep_idx = 0; unsigned long flags = 0; spin_lock_irqsave(&dev->lock, flags); /* Driver connected ? */ if (!dev->driver || !is_peripheral_active()) { DMSG_PANIC("ERR: functoin driver is not exist, or udc is not active.\n"); /* Clear interrupts */ clear_all_irq(); spin_unlock_irqrestore(&dev->lock, flags); return IRQ_NONE; } /* Save index */ old_ep_idx = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); /* Read status registers */ usb_irq = USBC_INT_MiscPending(g_sunxi_udc_io.usb_bsp_hdle); tx_irq = USBC_INT_EpPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); rx_irq = USBC_INT_EpPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); dma_irq = USBC_Readw(USBC_REG_DMA_INTS(dev->sunxi_udc_io->usb_vbase)); usb_irq = filtrate_irq_misc(usb_irq); if(g_irq_debug){ DMSG_INFO("\nirq: %02x,tx_irq=%02x,rx_irq=%02x, dma_irq:%x\n", usb_irq, tx_irq, rx_irq, dma_irq); } /* * Now, handle interrupts. There's two types : * - Reset, Resume, Suspend coming -> usb_int_reg * - EP -> ep_int_reg */ /* RESET */ if (usb_irq & USBC_INTUSB_RESET) { DMSG_INFO_UDC("IRQ: reset\n"); DMSG_INFO_UDC("(1:star,2:end): vfs_read:%d, vfs_write:%d,dma_working:%d,amount:%u,file_offset:%llu\n", atomic_read(&vfs_read_flag), atomic_read(&vfs_write_flag), dma_working, vfs_amount, (unsigned long long)vfs_file_offset); USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_RESET); clear_all_irq(); usb_connect = 1; USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, 0); USBC_Dev_SetAddress_default(g_sunxi_udc_io.usb_bsp_hdle); if (is_udc_support_dma()) { sunxi_udc_stop_dma_work(dev, 1); } throw_away_all_urb(dev); dev->address = 0; dev->ep0state = EP0_IDLE; g_irq_debug = 0; g_queue_debug = 0; g_dma_debug = 0; spin_unlock_irqrestore(&dev->lock, flags); #if defined CONFIG_AW_AXP && !defined SUNXI_USB_FPGA axp_usbvol(CHARGE_USB_20); axp_usbcur(CHARGE_USB_20); #endif return IRQ_HANDLED; } /* RESUME */ if (usb_irq & USBC_INTUSB_RESUME) { DMSG_INFO_UDC("IRQ: resume\n"); /* clear interrupt */ USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_RESUME); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume) { spin_unlock(&dev->lock); dev->driver->resume(&dev->gadget); spin_lock(&dev->lock); } } /* SUSPEND */ if (usb_irq & USBC_INTUSB_SUSPEND) { DMSG_INFO_UDC("IRQ: suspend\n"); /* clear interrupt */ USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_SUSPEND); if (dev->gadget.speed != USB_SPEED_UNKNOWN) { schedule_work(&dev->vbus_det_work); usb_connect = 0; } else { DMSG_INFO_UDC("ERR: usb speed is unkown\n"); } if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->suspend) { spin_unlock(&dev->lock); dev->driver->suspend(&dev->gadget); spin_lock(&dev->lock); } dev->ep0state = EP0_IDLE; } /* DISCONNECT */ if (usb_irq & USBC_INTUSB_DISCONNECT) { DMSG_INFO_UDC("IRQ: disconnect\n"); USBC_INT_ClearMiscPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_DISCONNECT); dev->ep0state = EP0_IDLE; usb_connect = 0; } /* EP */ /* control traffic */ /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready * generate an interrupt */ if (tx_irq & USBC_INTTx_FLAG_EP0) { DMSG_DBG_UDC("USB ep0 irq\n"); /* Clear the interrupt bit by setting it to 1 */ USBC_INT_ClearEpPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, 0); if (dev->gadget.speed == USB_SPEED_UNKNOWN) { if (USBC_Dev_QueryTransferMode(g_sunxi_udc_io.usb_bsp_hdle) == USBC_TS_MODE_HS) { dev->gadget.speed = USB_SPEED_HIGH; DMSG_INFO_UDC("\n+++++++++++++++++++++++++++++++++++++\n"); DMSG_INFO_UDC(" usb enter high speed.\n"); DMSG_INFO_UDC("\n+++++++++++++++++++++++++++++++++++++\n"); } else { dev->gadget.speed= USB_SPEED_FULL; DMSG_INFO_UDC("\n+++++++++++++++++++++++++++++++++++++\n"); DMSG_INFO_UDC(" usb enter full speed.\n"); DMSG_INFO_UDC("\n+++++++++++++++++++++++++++++++++++++\n"); } } sunxi_udc_handle_ep0(dev); } /* firstly to get data */ /* rx endpoint data transfers */ for (i = 1; i <= SW_UDC_EPNUMS; i++) { //for (i = 1; i < SW_UDC_ENDPOINTS; i++) { u32 tmp = 1 << i; if (rx_irq & tmp) { DMSG_DBG_UDC("USB rx ep%d irq\n", i); /* Clear the interrupt bit by setting it to 1 */ USBC_INT_ClearEpPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX, i); sunxi_udc_handle_ep(&dev->ep[ep_fifo_out[i]]); } } /* tx endpoint data transfers */ for (i = 1; i <= SW_UDC_EPNUMS; i++) { //for (i = 1; i < SW_UDC_ENDPOINTS; i++) { u32 tmp = 1 << i; if (tx_irq & tmp) { DMSG_DBG_UDC("USB tx ep%d irq\n", i); /* Clear the interrupt bit by setting it to 1 */ USBC_INT_ClearEpPending(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, i); sunxi_udc_handle_ep(&dev->ep[ep_fifo_in[i]]); } } #ifdef SW_UDC_DMA_INNER if(is_udc_support_dma()){ struct sunxi_udc_request *req = NULL; struct sunxi_udc_ep *ep = NULL; int i = 0; /* tx endpoint data transfers */ for (i = 0; i < DMA_CHAN_TOTAL; i++) { u32 tmp = 1 << i; if (dma_irq & tmp) { DMSG_DBG_UDC("USB dma chanle%d irq\n", i); /* set 1 to clear pending */ writel(BIT(i), USBC_REG_DMA_INTS(dev->sunxi_udc_io->usb_vbase)); //USBC_REG_clear_bit_w(tmp, USBC_REG_DMA_INTS(dev->sunxi_udc_io->usb_vbase)); ep = &dev->ep[dma_chnl[i].ep_num]; sunxi_udc_dma_release((dm_hdl_t)ep->dev->dma_hdle); ep->dev->dma_hdle = NULL; if (ep) { /* find req */ if (likely (!list_empty(&ep->queue))) { req = list_entry(ep->queue.next, struct sunxi_udc_request, queue); } else { req = NULL; } /* call back */ if (req) { spin_unlock_irqrestore(&dev->lock, flags); sunxi_udc_dma_completion(dev, ep, req); spin_lock_irqsave(&dev->lock, flags); } } else { DMSG_PANIC("ERR: sunxi_udc_dma_callback: dma is remove, but dma irq is happened\n"); } } } } #endif /* Restore old index */ USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_idx); spin_unlock_irqrestore(&dev->lock, flags); return IRQ_HANDLED; } /*------------------------- sunxi_udc_ep_ops ----------------------------------*/ static inline struct sunxi_udc_ep *to_sunxi_udc_ep(struct usb_ep *ep) { return container_of(ep, struct sunxi_udc_ep, ep); } static inline struct sunxi_udc *to_sunxi_udc(struct usb_gadget *gadget) { return container_of(gadget, struct sunxi_udc, gadget); } static inline struct sunxi_udc_request *to_sunxi_udc_req(struct usb_request *req) { return container_of(req, struct sunxi_udc_request, req); } static int sunxi_udc_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct sunxi_udc *dev = NULL; struct sunxi_udc_ep *ep = NULL; u32 max = 0; u32 old_ep_index = 0; __u32 fifo_addr = 0; unsigned long flags = 0; u32 ep_type = 0; u32 ts_type = 0; u32 fifo_size = 0; u8 double_fifo = 0; int i = 0; if(_ep == NULL || desc == NULL){ DMSG_PANIC("ERR: invalid argment\n"); return -EINVAL; } if (_ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) { DMSG_PANIC("PANIC : _ep->name(%s) == ep0name || desc->bDescriptorType(%d) != USB_DT_ENDPOINT\n", _ep->name , desc->bDescriptorType); return -EINVAL; } ep = to_sunxi_udc_ep(_ep); if (ep == NULL) { DMSG_PANIC("ERR: usbd_ep_enable, ep = NULL\n"); return -EINVAL; } if (ep->desc) { DMSG_PANIC("ERR: usbd_ep_enable, ep->desc is not NULL, ep%d(%s)\n", ep->num, _ep->name); return -EINVAL; } DMSG_INFO_UDC("ep enable: ep%d(0x%p, %s, %d, %d)\n", ep->num, _ep, _ep->name, (desc->bEndpointAddress & USB_DIR_IN), _ep->maxpacket); dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DMSG_PANIC("PANIC : dev->driver = 0x%p ?= NULL dev->gadget->speed =%d ?= USB_SPEED_UNKNOWN\n", dev->driver ,dev->gadget.speed); return -ESHUTDOWN; } max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; spin_lock_irqsave(&ep->dev->lock, flags); _ep->maxpacket = max & 0x7ff; ep->desc = desc; ep->halted = 0; ep->bEndpointAddress = desc->bEndpointAddress; //ep_type if ((ep->bEndpointAddress) & USB_DIR_IN) { /* tx */ ep_type = USBC_EP_TYPE_TX; } else { /* rx */ ep_type = USBC_EP_TYPE_RX; } //ts_type switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_CONTROL: ts_type = USBC_TS_TYPE_CTRL; break; case USB_ENDPOINT_XFER_BULK: ts_type = USBC_TS_TYPE_BULK; break; case USB_ENDPOINT_XFER_ISOC: ts_type = USBC_TS_TYPE_ISO; break; case USB_ENDPOINT_XFER_INT: ts_type = USBC_TS_TYPE_INT; break; default: DMSG_PANIC("err: usbd_ep_enable, unkown ep type(%d)\n", (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)); goto end; } //fifo_addr && fifo_size && double fifo for (i = 0; i < SW_UDC_ENDPOINTS; i++) { if (!strcmp(_ep->name, ep_fifo[i].name)) { fifo_addr = ep_fifo[i].fifo_addr; fifo_size = ep_fifo[i].fifo_size; double_fifo = ep_fifo[i].double_fifo; break; } } DMSG_INFO_UDC("ep enable: ep%d(0x%p, %s, %d, %d), fifo(%d, %d, %d)\n", ep->num, _ep, _ep->name, (desc->bEndpointAddress & USB_DIR_IN), _ep->maxpacket, fifo_addr, fifo_size, double_fifo); if (i >= SW_UDC_ENDPOINTS) { DMSG_PANIC("err: usbd_ep_enable, config fifo failed\n"); goto end; } /* check fifo size */ if ((_ep->maxpacket & 0x7ff) > fifo_size) { DMSG_PANIC("err: usbd_ep_enable, fifo size is too small\n"); goto end; } /* check double fifo */ if (double_fifo) { if (((_ep->maxpacket & 0x7ff) * 2) > fifo_size) { DMSG_PANIC("err: usbd_ep_enable, it is double fifo, but fifo size is too small\n"); goto end; } /* ????FIFO, ????????????? */ fifo_size = _ep->maxpacket & 0x7ff; } if (!is_peripheral_active()) { DMSG_INFO_UDC("usbd_ep_enable, usb device is not active\n"); goto end; } old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, ep->num); USBC_Dev_ConfigEp_Default(g_sunxi_udc_io.usb_bsp_hdle, ep_type); USBC_Dev_FlushFifo(g_sunxi_udc_io.usb_bsp_hdle, ep_type); //set max packet ,type, direction, address; reset fifo counters, enable irq USBC_Dev_ConfigEp(g_sunxi_udc_io.usb_bsp_hdle, ts_type, ep_type, double_fifo, (_ep->maxpacket & 0x7ff)); USBC_ConfigFifo(g_sunxi_udc_io.usb_bsp_hdle, ep_type, double_fifo, fifo_size, fifo_addr); if(ts_type == USBC_TS_TYPE_ISO){ USBC_Dev_IsoUpdateEnable(g_sunxi_udc_io.usb_bsp_hdle); } USBC_INT_EnableEp(g_sunxi_udc_io.usb_bsp_hdle, ep_type, ep->num); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); end: spin_unlock_irqrestore(&ep->dev->lock, flags); sunxi_udc_set_halt(_ep, 0); return 0; } static int sunxi_udc_ep_disable(struct usb_ep *_ep) { struct sunxi_udc_ep *ep = NULL; u32 old_ep_index = 0; unsigned long flags = 0; if (!_ep) { DMSG_PANIC("ERR: invalid argment\n"); return -EINVAL; } ep = to_sunxi_udc_ep(_ep); if (ep == NULL) { DMSG_PANIC("ERR: usbd_ep_disable: ep = NULL\n"); return -EINVAL; } if (!ep->desc) { DMSG_PANIC("ERR: %s not enabled\n", _ep ? ep->ep.name : NULL); return -EINVAL; } DMSG_INFO_UDC("ep disable: ep%d(0x%p, %s, %d, %x)\n", ep->num, _ep, _ep->name, (ep->bEndpointAddress & USB_DIR_IN), _ep->maxpacket); spin_lock_irqsave(&ep->dev->lock, flags); DMSG_DBG_UDC("ep_disable: %s\n", _ep->name); ep->desc = NULL; ep->halted = 1; sunxi_udc_nuke (ep->dev, ep, -ESHUTDOWN); if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); goto end; } old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, ep->num); if ((ep->bEndpointAddress) & USB_DIR_IN) { /* tx */ USBC_Dev_ConfigEp_Default(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); USBC_INT_DisableEp(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, ep->num); } else { /* rx */ USBC_Dev_ConfigEp_Default(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); USBC_INT_DisableEp(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX, ep->num); } USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); end: spin_unlock_irqrestore(&ep->dev->lock, flags); DMSG_DBG_UDC("%s disabled\n", _ep->name); return 0; } static struct usb_request * sunxi_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) { struct sunxi_udc_request *req = NULL; if (!_ep) { DMSG_PANIC("ERR: invalid argment\n"); return NULL; } req = kzalloc (sizeof(struct sunxi_udc_request), mem_flags); if (!req) { DMSG_PANIC("ERR: kzalloc failed\n"); return NULL; } memset(req, 0, sizeof(struct sunxi_udc_request)); req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD (&req->queue); DMSG_INFO_UDC("alloc request: ep(0x%p, %s, %d), req(0x%p)\n", _ep, _ep->name, _ep->maxpacket, req); return &req->req; } static void sunxi_udc_free_request(struct usb_ep *_ep, struct usb_request *_req) { struct sunxi_udc_request *req = NULL; if (_ep == NULL || _req == NULL) { DMSG_PANIC("ERR: invalid argment\n"); return; } req = to_sunxi_udc_req(_req); if (req == NULL) { DMSG_PANIC("ERR: invalid argment\n"); return; } DMSG_INFO_UDC("free request: ep(0x%p, %s, %d), req(0x%p)\n", _ep, _ep->name, _ep->maxpacket, req); kfree(req); return; } static int sunxi_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { struct sunxi_udc_request *req = NULL; struct sunxi_udc_ep *ep = NULL; struct sunxi_udc *dev = NULL; unsigned long flags = 0; u8 old_ep_index = 0; if (_ep == NULL || _req == NULL ) { DMSG_PANIC("ERR: invalid argment\n"); return -EINVAL; } ep = to_sunxi_udc_ep(_ep); if ((ep == NULL || (!ep->desc && _ep->name != ep0name))) { DMSG_PANIC("ERR: sunxi_udc_queue: inval 2\n"); return -EINVAL; } dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { DMSG_PANIC("ERR : dev->driver=0x%p, dev->gadget.speed=%x\n", dev->driver, dev->gadget.speed); return -ESHUTDOWN; } if (!_req->complete || !_req->buf) { DMSG_PANIC("ERR: usbd_queue: _req is invalid\n"); return -EINVAL; } req = to_sunxi_udc_req(_req); if (!req) { DMSG_PANIC("ERR: req is NULL\n"); return -EINVAL; } spin_lock_irqsave(&ep->dev->lock, flags); _req->status = -EINPROGRESS; _req->actual = 0; if (is_sunxi_udc_dma_capable(req, ep)) { spin_unlock_irqrestore(&ep->dev->lock, flags); sunxi_udc_map_dma_buffer(req, dev, ep); spin_lock_irqsave(&ep->dev->lock, flags); } list_add_tail(&req->queue, &ep->queue); if (!is_peripheral_active()) { DMSG_PANIC("warn: peripheral is active\n"); goto end; } if(g_queue_debug){ DMSG_INFO("q:(0x%p,%d,%d)\n", _req, _req->length, _req->actual); } old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); if (ep->bEndpointAddress) { USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, ep->bEndpointAddress & 0x7F); } else { USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, 0); } /* if there is only one in the queue, then execute it */ if (!ep->halted && (&req->queue == ep->queue.next)) { if (ep->bEndpointAddress == 0 /* ep0 */) { switch (dev->ep0state) { case EP0_IN_DATA_PHASE: if (!USBC_Dev_IsWriteDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX) && sunxi_udc_write_fifo(ep, req)) { dev->ep0state = EP0_IDLE; req = NULL; } break; case EP0_OUT_DATA_PHASE: if ((!_req->length) || (USBC_Dev_IsReadDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX) && sunxi_udc_read_fifo(ep, req))) { dev->ep0state = EP0_IDLE; req = NULL; } break; default: spin_unlock_irqrestore(&ep->dev->lock, flags); return -EL2HLT; } } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && !USBC_Dev_IsWriteDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX)) { if (sunxi_udc_write_fifo(ep, req)) { req = NULL; } } else if ((ep->bEndpointAddress & USB_DIR_IN) == 0 && USBC_Dev_IsReadDataReady(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX)) { if (sunxi_udc_read_fifo(ep, req)) { req = NULL; } } } USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); end: spin_unlock_irqrestore(&ep->dev->lock, flags); return 0; } static int sunxi_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct sunxi_udc_ep *ep = NULL; struct sunxi_udc *udc = NULL; int retval = -EINVAL; struct sunxi_udc_request *req = NULL; unsigned long flags = 0; DMSG_DBG_UDC("(%p,%p)\n", _ep, _req); if (!the_controller->driver) { DMSG_PANIC("ERR: sunxi_udc_dequeue: driver is null\n"); return -ESHUTDOWN; } if (!_ep || !_req) { DMSG_PANIC("ERR: sunxi_udc_dequeue: invalid argment\n"); return retval; } ep = to_sunxi_udc_ep(_ep); if (ep == NULL) { DMSG_PANIC("ERR: ep == NULL\n"); return -EINVAL; } udc = to_sunxi_udc(ep->gadget); if (udc == NULL) { DMSG_PANIC("ERR: ep == NULL\n"); return -EINVAL; } DMSG_INFO_UDC("dequeue: ep(0x%p, %d), _req(0x%p, %d, %d)\n", ep, ep->num, _req, _req->length, _req->actual); spin_lock_irqsave(&ep->dev->lock, flags); list_for_each_entry (req, &ep->queue, queue) { if (&req->req == _req) { list_del_init (&req->queue); _req->status = -ECONNRESET; retval = 0; break; } } if (retval == 0) { DMSG_DBG_UDC("dequeued req %p from %s, len %d buf %p\n", req, _ep->name, _req->length, _req->buf); sunxi_udc_done(ep, req, -ECONNRESET); } spin_unlock_irqrestore(&ep->dev->lock, flags); return retval; } static int sunxi_udc_set_halt_ex(struct usb_ep *_ep, int value, int is_in) { struct sunxi_udc_ep *ep = NULL; u32 idx = 0; __u8 old_ep_index = 0; if (_ep == NULL) { DMSG_PANIC("ERR: invalid argment\n"); return -EINVAL; } ep = to_sunxi_udc_ep(_ep); if (ep == NULL) { DMSG_PANIC("ERR: invalid argment\n"); return -EINVAL; } if (!ep->desc && ep->ep.name != ep0name) { DMSG_PANIC("ERR: !ep->desc && ep->ep.name != ep0name\n"); return -EINVAL; } if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } idx = ep->bEndpointAddress & 0x7F; old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, idx); if (idx == 0) { USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); } else { if (is_in) { if (value) { USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); } else { USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); } } else { if (value) { USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); } else { USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); } } } ep->halted = value ? 1 : 0; USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); return 0; } static int sunxi_udc_set_halt(struct usb_ep *_ep, int value) { struct sunxi_udc_ep *ep = NULL; unsigned long flags = 0; u32 idx = 0; __u8 old_ep_index = 0; if (_ep == NULL) { DMSG_PANIC("ERR: invalid argment\n"); return -EINVAL; } ep = to_sunxi_udc_ep(_ep); if (ep == NULL) { DMSG_PANIC("ERR: invalid argment\n"); return -EINVAL; } if (!ep->desc && ep->ep.name != ep0name) { DMSG_PANIC("ERR: !ep->desc && ep->ep.name != ep0name\n"); return -EINVAL; } if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } spin_lock_irqsave(&ep->dev->lock, flags); idx = ep->bEndpointAddress & 0x7F; old_ep_index = USBC_GetActiveEp(g_sunxi_udc_io.usb_bsp_hdle); USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, idx); if (idx == 0) { USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_EP0); } else { if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { if (value) { USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); } else { USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); } } else { if (value) { USBC_Dev_EpSendStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); } else { USBC_Dev_EpClearStall(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); } } } ep->halted = value ? 1 : 0; USBC_SelectActiveEp(g_sunxi_udc_io.usb_bsp_hdle, old_ep_index); spin_unlock_irqrestore(&ep->dev->lock, flags); return 0; } static const struct usb_ep_ops sunxi_udc_ep_ops = { .enable = sunxi_udc_ep_enable, .disable = sunxi_udc_ep_disable, .alloc_request = sunxi_udc_alloc_request, .free_request = sunxi_udc_free_request, .queue = sunxi_udc_queue, .dequeue = sunxi_udc_dequeue, .set_halt = sunxi_udc_set_halt, }; static int sunxi_udc_get_frame(struct usb_gadget *_gadget) { int ret = 0; if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } DMSG_INFO_UDC("sunxi_udc_get_frame is no susport\n"); //ret = USBC_REG_FRNUM(g_sunxi_udc_io.usb_bsp_hdle); return ret; } static int sunxi_udc_wakeup(struct usb_gadget *_gadget) { if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } return 0; } static int sunxi_udc_set_selfpowered(struct usb_gadget *gadget, int value) { if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } return 0; } static void sunxi_udc_disable(struct sunxi_udc *dev); static void sunxi_udc_enable(struct sunxi_udc *dev); static int sunxi_udc_set_pullup(struct sunxi_udc *udc, int is_on) { DMSG_DBG_UDC("sunxi_udc_set_pullup\n"); is_udc_enable = is_on; if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } if (is_on) { sunxi_udc_enable(udc); } else { if (udc->gadget.speed != USB_SPEED_UNKNOWN) { if (udc->driver && udc->driver->disconnect) udc->driver->disconnect(&udc->gadget); } sunxi_udc_disable(udc); } return 0; } static int sunxi_udc_vbus_session(struct usb_gadget *gadget, int is_active) { struct sunxi_udc *udc = to_sunxi_udc(gadget); DMSG_DBG_UDC("sunxi_udc_vbus_session\n"); if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } udc->vbus = (is_active != 0); sunxi_udc_set_pullup(udc, is_active); return 0; } static int sunxi_udc_pullup(struct usb_gadget *gadget, int is_on) { struct sunxi_udc *udc = to_sunxi_udc(gadget); DMSG_INFO_UDC("sunxi_udc_pullup, is_on = %d\n", is_on); sunxi_udc_set_pullup(udc, is_on); return 0; } static int sunxi_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma) { if (!is_peripheral_active()) { DMSG_INFO("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } DMSG_DBG_UDC("sunxi_udc_vbus_draw\n"); cfg_vbus_draw(ma); return 0; } static int sunxi_get_udc_base(struct platform_device *pdev, sunxi_udc_io_t *sunxi_udc_io) { struct device_node *np = pdev->dev.of_node; #ifdef CONFIG_ARCH_SUN3IW1 __u32 reg_value = 0; #endif sunxi_udc_io->usb_vbase = of_iomap(np, 0); if (sunxi_udc_io->usb_vbase == NULL) { dev_err(&pdev->dev, "can't get usb_vbase resource\n"); return -EINVAL; } #ifdef CONFIG_ARCH_SUN3IW1 sunxi_udc_io->sram_vbase = of_iomap(np, 1); if (sunxi_udc_io->sram_vbase == NULL) { dev_err(&pdev->dev, "can't get sram_vbase resource\n"); return -EINVAL; } reg_value = USBC_Readl(sunxi_udc_io->sram_vbase + 0x04); reg_value |= 0x01; USBC_Writel(reg_value, (sunxi_udc_io->sram_vbase + 0x04)); #endif //DMSG_INFO("usbc base:%p\n", sunxi_udc_io->usb_vbase); return 0; } static int sunxi_get_udc_clock(struct platform_device *pdev, sunxi_udc_io_t *sunxi_udc_io) { struct device_node *np = pdev->dev.of_node; sunxi_udc_io->ahb_otg = of_clk_get(np, 1); if (IS_ERR(sunxi_udc_io->ahb_otg)) { sunxi_udc_io->ahb_otg = NULL; DMSG_PANIC("ERR: get usb ahb_otg clk failed.\n"); return -EINVAL; } sunxi_udc_io->mod_usbphy = of_clk_get(np, 0); if (IS_ERR(sunxi_udc_io->mod_usbphy)) { sunxi_udc_io->ahb_otg = NULL; DMSG_PANIC("ERR: get usb mod_usbphy failed.\n"); return -EINVAL; } return 0; } /* gadget driver handling */ static void sunxi_udc_reinit(struct sunxi_udc *dev) { u32 i = 0; /* device/ep0 records init */ INIT_LIST_HEAD (&dev->gadget.ep_list); INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); dev->ep0state = EP0_IDLE; for (i = 0; i < SW_UDC_ENDPOINTS; i++) { struct sunxi_udc_ep *ep = &dev->ep[i]; if (i != 0) { list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); } ep->dev = dev; ep->desc = NULL; ep->halted = 0; INIT_LIST_HEAD (&ep->queue); } return; } static void sunxi_udc_enable(struct sunxi_udc *dev) { DMSG_DBG_UDC("sunxi_udc_enable called\n"); /* dev->gadget.speed = USB_SPEED_UNKNOWN; */ dev->gadget.speed = USB_SPEED_UNKNOWN; DMSG_INFO_UDC("CONFIG_USB_GADGET_DUALSPEED: USBC_TS_MODE_HS\n"); USBC_Dev_ConfigTransferMode(g_sunxi_udc_io.usb_bsp_hdle, USBC_TS_TYPE_BULK, USBC_TS_MODE_HS); /* Enable reset and suspend interrupt interrupts */ USBC_INT_EnableUsbMiscUint(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_SUSPEND); USBC_INT_EnableUsbMiscUint(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_RESUME); USBC_INT_EnableUsbMiscUint(g_sunxi_udc_io.usb_bsp_hdle, USBC_INTUSB_RESET); /* Enable ep0 interrupt */ USBC_INT_EnableEp(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX, 0); cfg_udc_command(SW_UDC_P_ENABLE); return ; } static void sunxi_udc_disable(struct sunxi_udc *dev) { DMSG_DBG_UDC("sunxi_udc_disable\n"); /* Disable all interrupts */ USBC_INT_DisableUsbMiscAll(g_sunxi_udc_io.usb_bsp_hdle); USBC_INT_DisableEpAll(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_RX); USBC_INT_DisableEpAll(g_sunxi_udc_io.usb_bsp_hdle, USBC_EP_TYPE_TX); /* Clear the interrupt registers */ clear_all_irq(); cfg_udc_command(SW_UDC_P_DISABLE); /* Set speed to unknown */ dev->gadget.speed = USB_SPEED_UNKNOWN; return; } static s32 usbd_start_work(void) { DMSG_INFO_UDC("usbd_start_work\n"); if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } enable_irq_udc(the_controller); USBC_Dev_ConectSwitch(g_sunxi_udc_io.usb_bsp_hdle, USBC_DEVICE_SWITCH_ON); return 0; } static s32 usbd_stop_work(void) { DMSG_INFO_UDC("usbd_stop_work\n"); if (!is_peripheral_active()) { DMSG_INFO_UDC("%s_%d: usb device is not active\n", __func__, __LINE__); return 0; } disable_irq_udc(the_controller); USBC_Dev_ConectSwitch(g_sunxi_udc_io.usb_bsp_hdle, USBC_DEVICE_SWITCH_OFF); // default is pulldown return 0; } static int sunxi_udc_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { struct sunxi_udc *udc = the_controller; /* Sanity checks */ if (!udc) { DMSG_PANIC("ERR: udc is null\n"); return -ENODEV; } if (udc->driver) { DMSG_PANIC("ERR: udc->driver is not null\n"); return -EBUSY; } /* the struct usb_gadget_driver has a little change between linux 3.0 and linux-3.3, speed->max_speed */ if (!driver->setup || driver->max_speed < USB_SPEED_FULL) { DMSG_PANIC("ERR: Invalid setup %p speed %d\n", driver->setup, driver->max_speed); return -EINVAL; } #if defined(MODULE) if (!driver->unbind) { DMSG_PANIC("Invalid driver: no unbind method\n"); return -EINVAL; } #endif /* Hook the driver */ udc->driver = driver; udc->gadget.dev.driver = &driver->driver; DMSG_INFO_UDC("[%s]: binding gadget driver '%s'\n", gadget_name, driver->driver.name); return 0; } static int sunxi_udc_stop(struct usb_gadget *g, struct usb_gadget_driver *driver) { struct sunxi_udc *udc = the_controller; if (!udc) { DMSG_PANIC("ERR: udc is null\n"); return -ENODEV; } if (!driver || driver != udc->driver || !driver->unbind) { DMSG_PANIC("ERR: driver is null\n"); return -EINVAL; } DMSG_INFO_UDC("[%s]: usb_gadget_unregister_driver() '%s'\n", gadget_name, driver->driver.name); udc->gadget.dev.driver = NULL; udc->driver = NULL; /* Disable udc */ sunxi_udc_disable(udc); return 0; } static const struct usb_gadget_ops sunxi_udc_ops = { .get_frame = sunxi_udc_get_frame, .wakeup = sunxi_udc_wakeup, .set_selfpowered = sunxi_udc_set_selfpowered, .pullup = sunxi_udc_pullup, .vbus_session = sunxi_udc_vbus_session, .vbus_draw = sunxi_udc_vbus_draw, .udc_start = sunxi_udc_start, .udc_stop = sunxi_udc_stop, }; static struct sunxi_udc sunxi_udc = { .gadget = { .ops = &sunxi_udc_ops, .ep0 = &sunxi_udc.ep[0].ep, .name = gadget_name, .dev = { .init_name = "gadget", }, }, .ep[0] = { .num = 0, .ep = { .name = ep0name, .ops = &sunxi_udc_ep_ops, .maxpacket = EP0_FIFO_SIZE, }, .dev = &sunxi_udc, }, .ep[1] = { .num = 1, .ep = { .name = ep1in_bulk_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = (USB_DIR_IN | 1), .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep[2] = { .num = 1, .ep = { .name = ep1out_bulk_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = (USB_DIR_OUT | 1), .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep[3] = { .num = 2, .ep = { .name = ep2in_bulk_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = (USB_DIR_IN | 2), .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep[4] = { .num = 2, .ep = { .name = ep2out_bulk_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = (USB_DIR_OUT | 2), .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep[5] = { .num = 3, .ep = { .name = ep3_iso_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = 3, .bmAttributes = USB_ENDPOINT_XFER_ISOC, }, .ep[6] = { .num = 4, .ep = { .name = ep4_int_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = 4, .bmAttributes = USB_ENDPOINT_XFER_INT, }, #if defined(CONFIG_ARCH_SUN50IW1) || defined(CONFIG_ARCH_SUN8IW6) \ || defined(CONFIG_ARCH_SUN8IW5) .ep[7] = { .num = 5, .ep = { .name = ep5in_bulk_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = (USB_DIR_IN | 5), .bmAttributes = USB_ENDPOINT_XFER_BULK, }, .ep[8] = { .num = 5, .ep = { .name = ep5out_bulk_name, .ops = &sunxi_udc_ep_ops, .maxpacket = SW_UDC_EP_FIFO_SIZE, }, .dev = &sunxi_udc, //.fifo_size = SW_UDC_EP_FIFO_SIZE, .bEndpointAddress = (USB_DIR_OUT | 5), .bmAttributes = USB_ENDPOINT_XFER_BULK, }, #endif }; static void sunxi_vbus_det_work(struct work_struct *work) { struct sunxi_udc *udc = NULL; /* wait for axp vbus detect ready */ msleep(100); udc = container_of(work, struct sunxi_udc, vbus_det_work); #if defined(CONFIG_AW_AXP) if (!axp_usb_is_connected() && udc->driver && udc->driver->disconnect) #else if (udc->driver && udc->driver->disconnect) #endif udc->driver->disconnect(&udc->gadget); } void __iomem *get_otgc_vbase(void) { return g_sunxi_udc_io.usb_vbase; } EXPORT_SYMBOL_GPL(get_otgc_vbase); int get_dp_dm_status_normal(void) { __u32 reg_val = 0; __u32 dp = 0; __u32 dm = 0; if(g_sunxi_udc_io.usb_vbase == NULL){ return 0; } /* USBC_EnableDpDmPullUp */ reg_val = USBC_Readl(USBC_REG_ISCR(g_sunxi_udc_io.usb_vbase)); reg_val |= (1 << USBC_BP_ISCR_DPDM_PULLUP_EN); USBC_Writel(reg_val, USBC_REG_ISCR(g_sunxi_udc_io.usb_vbase)); /* USBC_EnableIdPullUp */ reg_val = USBC_Readl(USBC_REG_ISCR(g_sunxi_udc_io.usb_vbase)); reg_val |= (1 << USBC_BP_ISCR_ID_PULLUP_EN); USBC_Writel(reg_val, USBC_REG_ISCR(g_sunxi_udc_io.usb_vbase)); msleep(10); reg_val = USBC_Readl(USBC_REG_ISCR(g_sunxi_udc_io.usb_vbase)); dp = (reg_val >> USBC_BP_ISCR_EXT_DP_STATUS) & 0x01; dm = (reg_val >> USBC_BP_ISCR_EXT_DM_STATUS) & 0x01; return ((dp << 1) | dm); } EXPORT_SYMBOL_GPL(get_dp_dm_status_normal); static int sunxi_get_udc_resource(struct platform_device *pdev, sunxi_udc_io_t *sunxi_udc_io); int sunxi_usb_device_enable(void) { struct platform_device *pdev = g_udc_pdev; struct sunxi_udc *udc = &sunxi_udc; int retval = 0; DMSG_INFO_UDC("sunxi_usb_device_enable start\n"); if (pdev == NULL) { DMSG_PANIC("pdev is null\n"); return -1; } usb_connect = 0; crq_bRequest = 0; is_controller_alive = 1; #if defined(CONFIG_ARCH_SUN8IW6) || defined(CONFIG_ARCH_SUN3IW1) \ || defined(CONFIG_ARCH_SUN8IW5) retval = sunxi_get_udc_resource(pdev, &g_sunxi_udc_io); if (retval != 0) { DMSG_PANIC("ERR: sunxi_get_udc_resource, is fail\n"); return -ENODEV; } retval = sunxi_udc_io_init(usbd_port_no, &g_sunxi_udc_io); if (retval != 0) { DMSG_PANIC("ERR: sunxi_udc_io_init failed\n"); return -1; } #endif retval = sunxi_udc_bsp_init(&g_sunxi_udc_io); if (retval != 0) { DMSG_PANIC("ERR: sunxi_udc_bsp_init failed\n"); return -1; } sunxi_udc_disable(udc); udc->irq_no = platform_get_irq(pdev, 0); if (udc->irq_no < 0) { DMSG_PANIC("%s,%d: error to get irq\n", __func__, __LINE__); return -EINVAL; } udc->sunxi_udc_io = &g_sunxi_udc_io; udc->usbc_no = usbd_port_no; strcpy((char *)udc->driver_name, gadget_name); udc->pdev = pdev; udc->controller = &(pdev->dev); #ifdef CONFIG_OF udc->controller->dma_mask = &sunxi_udc_mask; udc->controller->coherent_dma_mask = DMA_BIT_MASK(32); #endif if (is_udc_support_dma()) { retval = sunxi_udc_dma_probe(udc); if (retval != 0) { DMSG_PANIC("ERR: sunxi_udc_dma_probe failef\n"); retval = -EBUSY; goto err; } } INIT_WORK(&udc->vbus_det_work, sunxi_vbus_det_work); retval = request_irq(udc->irq_no, sunxi_udc_irq, IRQF_DISABLED, gadget_name, udc); if (retval != 0) { DMSG_PANIC("ERR: cannot get irq %i, err %d\n", udc->irq_no, retval); retval = -EBUSY; goto err; } if (udc->driver && is_udc_enable) { sunxi_udc_enable(udc); cfg_udc_command(SW_UDC_P_ENABLE); } DMSG_INFO_UDC("sunxi_usb_device_enable end\n"); return 0; err: if (is_udc_support_dma()) { sunxi_udc_dma_remove(udc); } sunxi_udc_bsp_exit(&g_sunxi_udc_io); #if defined(CONFIG_ARCH_SUN8IW6) || defined(CONFIG_ARCH_SUN8IW5) sunxi_udc_io_exit(&g_sunxi_udc_io); #endif return retval; } EXPORT_SYMBOL_GPL(sunxi_usb_device_enable); int sunxi_usb_device_disable(void) __releases(sunxi_udc.lock) __acquires(sunxi_udc.lock) { struct platform_device *pdev = g_udc_pdev; struct sunxi_udc *udc = NULL; unsigned long flags = 0; DMSG_INFO_UDC("sunxi_usb_device_disable start\n"); if (pdev == NULL) { DMSG_PANIC("pdev is null\n"); return -1; } udc = platform_get_drvdata(pdev); if (udc == NULL) { DMSG_PANIC("udc is null\n"); return -1; } /* disable usb controller */ if (udc->driver && udc->driver->disconnect) { udc->driver->disconnect(&udc->gadget); } if (is_udc_support_dma()) { spin_lock_irqsave(&udc->lock, flags); sunxi_udc_stop_dma_work(udc, 0); spin_unlock_irqrestore(&udc->lock, flags); sunxi_udc_dma_remove(udc); } free_irq(udc->irq_no, udc); sunxi_udc_bsp_exit(&g_sunxi_udc_io); spin_lock_irqsave(&udc->lock, flags); usb_connect = 0; crq_bRequest = 0; is_controller_alive = 0; spin_unlock_irqrestore(&udc->lock, flags); DMSG_INFO_UDC("sunxi_usb_device_disable end\n"); return 0; } EXPORT_SYMBOL_GPL(sunxi_usb_device_disable); int sunxi_udc_is_enable(struct platform_device *pdev) { struct device_node *usbc_np = NULL; int ret = 0; int is_enable = 0; const char *used_status; usbc_np = of_find_node_by_type(NULL, SET_USB0); ret = of_property_read_string(usbc_np, "status", &used_status); if (ret) { DMSG_PANIC("get sunxi_udc_is_enable is fail, %d\n", -ret); is_enable = 0; }else if (!strcmp(used_status, "okay")) { is_enable = 1; }else { is_enable = 0; } return is_enable; } static int sunxi_get_udc_resource(struct platform_device *pdev, sunxi_udc_io_t *sunxi_udc_io) { int retval = 0; memset(&g_sunxi_udc_io, 0, sizeof(sunxi_udc_io_t)); retval = sunxi_get_udc_base(pdev, sunxi_udc_io); if(retval != 0){ dev_err(&pdev->dev, "can't get udc base\n"); goto err0; } retval = sunxi_get_udc_clock(pdev, sunxi_udc_io); if(retval != 0){ dev_err(&pdev->dev, "can't get udc clock\n"); goto err0; } return 0; err0: return retval; } static const struct of_device_id sunxi_udc_match[] = { {.compatible = "allwinner,sunxi-udc", }, {}, }; MODULE_DEVICE_TABLE(of, sunxi_udc_match); static int sunxi_udc_probe_otg(struct platform_device *pdev) { struct sunxi_udc *udc = &sunxi_udc; int retval = 0; g_udc_pdev = pdev; spin_lock_init (&udc->lock); if(!sunxi_udc_is_enable(pdev)){ DMSG_INFO("sunxi udc is no enable\n"); return -ENODEV; } udc->gadget.dev.parent = &pdev->dev; #ifdef CONFIG_OF udc->gadget.dev.dma_mask = &sunxi_udc_mask; udc->gadget.dev.coherent_dma_mask = DMA_BIT_MASK(32); #endif retval = sunxi_get_udc_resource(pdev, &g_sunxi_udc_io); if(retval != 0){ DMSG_PANIC("ERR: sunxi_get_udc_resource, is fail\n"); return -ENODEV; } sunxi_udc_io_init(usbd_port_no, &g_sunxi_udc_io); sunxi_udc_reinit(udc); the_controller = udc; platform_set_drvdata(pdev, udc); retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (retval != 0) { dev_err(&pdev->dev, "can't usb_add_gadget_udc\n"); return retval; } device_create_file(&pdev->dev, &dev_attr_otg_ed_test); device_create_file(&pdev->dev, &dev_attr_queue_debug); device_create_file(&pdev->dev, &dev_attr_dma_debug); device_create_file(&pdev->dev, &dev_attr_write_debug); device_create_file(&pdev->dev, &dev_attr_read_debug); device_create_file(&pdev->dev, &dev_attr_irq_debug); device_create_file(&pdev->dev, &dev_attr_msc_read_debug); device_create_file(&pdev->dev, &dev_attr_msc_write_debug); return 0; } static int sunxi_udc_remove_otg(struct platform_device *pdev) { struct sunxi_udc *udc = NULL; g_udc_pdev = NULL; udc = platform_get_drvdata(pdev); if (udc->driver) { DMSG_PANIC("ERR: invalid argment, udc->driver(0x%p)\n", udc->driver); return -EBUSY; } sunxi_udc_io_exit(&g_sunxi_udc_io); memset(&g_sunxi_udc_io, 0, sizeof(sunxi_udc_io_t)); return 0; } static int sunxi_udc_probe(struct platform_device *pdev) { return sunxi_udc_probe_otg(pdev); } static int sunxi_udc_remove(struct platform_device *pdev) { return sunxi_udc_remove_otg(pdev); } #ifdef CONFIG_PM static int sunxi_udc_suspend(struct device *dev) { struct sunxi_udc *udc = the_controller; DMSG_INFO_UDC("sunxi_udc_suspend start\n"); device_insmod_delay = 0; atomic_set(&thread_suspend_flag, 1); if(udc == NULL){ DMSG_INFO_UDC("udc is NULL, need not enter to suspend\n"); return 0; } if (!is_peripheral_active()) { DMSG_INFO("udc is disable, need not enter to suspend\n"); return 0; } #if 0 /* if USB is not connected to PC, then can enter suspend * if USB is connected to PC, then not enter suspend */ if(usb_connect){ DMSG_PANIC("ERR: usb is connect to PC, can not suspend\n"); return -EBUSY; } #endif /* soft disconnect */ cfg_udc_command(SW_UDC_P_DISABLE); /* disable usb controller */ sunxi_udc_disable(udc); /* close USB clock */ close_usb_clock(&g_sunxi_udc_io); DMSG_INFO_UDC("sunxi_udc_suspend end\n"); return 0; } static int sunxi_udc_resume(struct device *dev) { struct sunxi_udc *udc = the_controller; DMSG_INFO_UDC("sunxi_udc_resume start\n"); device_insmod_delay = 0; atomic_set(&thread_suspend_flag, 0); if(udc == NULL){ DMSG_INFO_UDC("udc is NULL, need not enter to suspend\n"); return 0; } if (!is_peripheral_active()) { DMSG_INFO("udc is disable, need not enter to resume\n"); return 0; } sunxi_udc_bsp_init(&g_sunxi_udc_io); if(is_udc_enable){ /* enable usb controller */ sunxi_udc_enable(udc); /* soft connect */ cfg_udc_command(SW_UDC_P_ENABLE); } DMSG_INFO_UDC("sunxi_udc_resume end\n"); return 0; } static const struct dev_pm_ops sunxi_udc_pm_ops = { .suspend = sunxi_udc_suspend, .resume = sunxi_udc_resume, }; #define UDC_PM_OPS (&sunxi_udc_pm_ops) #else /* !CONFIG_PM */ #define UDC_PM_OPS NULL #endif /* CONFIG_PM */ static struct platform_driver sunxi_udc_driver = { .driver = { .name = (char *)gadget_name, .pm = UDC_PM_OPS, .bus = &platform_bus_type, .owner = THIS_MODULE, .of_match_table = sunxi_udc_match, }, .probe = sunxi_udc_probe, .remove = sunxi_udc_remove, }; static void cfg_udc_command(enum sunxi_udc_cmd_e cmd) { struct sunxi_udc *udc = the_controller; switch (cmd) { case SW_UDC_P_ENABLE: { if (udc->driver) { usbd_start_work(); } else { DMSG_INFO_UDC("udc->driver is null, udc is need not start\n"); } } break; case SW_UDC_P_DISABLE: { if (udc->driver) { usbd_stop_work(); } else { DMSG_INFO_UDC("udc->driver is null, udc is need not stop\n"); } } break; case SW_UDC_P_RESET : DMSG_PANIC("ERR: reset is not support\n"); break; default: DMSG_PANIC("ERR: unkown cmd(%d)\n",cmd); break; } return ; } static void cfg_vbus_draw(unsigned int ma) { return; } static int udc_init(void) { int retval = 0; //DMSG_INFO_UDC("udc_init: version %s\n", DRIVER_VERSION); usb_connect = 0; atomic_set(&vfs_read_flag, 0); atomic_set(&vfs_write_flag, 0); /* driver register */ retval = platform_driver_register(&sunxi_udc_driver); if (retval) { DMSG_PANIC("ERR: platform_driver_register failed\n"); retval = -1; goto err; } return 0; err: return retval; } static void udc_exit(void) { DMSG_INFO_UDC("udc_exit: version %s\n", DRIVER_VERSION); platform_driver_unregister(&sunxi_udc_driver); return ; } fs_initcall(udc_init); module_exit(udc_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:softwinner-usbgadget");