www.pudn.com > Linux2410_device.rar > wmmx.c
/* * wmmx.c -- Bulverde USB Device Controller driver. * * 09/04/2003 * Stanley Cai (stanley.cai@intel.com) ported the driver to Bulverde * Mainstone. * * Changes Copyright (c) 2003 MontaVista Software Inc. * Author: MontaVista Software, Inc. * source@mvista.com * * Based on work: * Copyright (c) 2000, 2001, 2002 Lineo * * By: * Stuart Lynne, * Tom Rushworth , * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * */ /* * This code was developed on the Intel Mainstone with Bulverde processor */ #include #include #include "../usbd-export.h" #include "../usbd-build.h" #include "../usbd-module.h" #include "../usbd-debug.h" MODULE_DESCRIPTION ("WMMX USB Device Bus Interface"); USBD_MODULE_INFO ("wmmx_bi 0.2-alpha"); MODULE_LICENSE("GPL"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../usbd.h" #include "../usbd-func.h" #include "../usbd-bus.h" #include "../usbd-inline.h" #include "usbd-bi.h" #include "wmmx.h" #define WMMX_TRACE #undef WMMX_TRACE #define WMMX_XFER_DIR_OUT 0 #define WMMX_XFER_DIR_IN 1 #ifdef WMMX_TRACE #define TRACE_MAX 10000 #define WMMX_XFER_MAX_TRACE_SIZE 16 typedef enum wwmx_trace_type { wmmx_regs, wmmx_setup, wmmx_xmit, wmmx_ccr, wmmx_iro, wmmx_xfer } wmmx_trace_type_t; typedef struct wmmx_regs { u32 cs0; char * msg; } wmmx_regs_t; typedef struct wmmx_xmit { u32 size; } wmmx_xmit_t; typedef struct wmmx_ccr { u32 ccr; char * msg; } wmmx_ccr_t; typedef struct wmmx_iro { u32 iro; char * msg; } wmmx_iro_t; typedef struct wmmx_xfer { char dir; u32 size; char data[WMMX_XFER_MAX_TRACE_SIZE]; } wmmx_xfer_t; typedef struct wmmx_trace { wmmx_trace_type_t trace_type; u32 interrupts; u32 ocsr; u64 jiffies; union { wmmx_regs_t regs; wmmx_xmit_t xmit; wmmx_ccr_t ccr; wmmx_iro_t iro; wmmx_xfer_t xfer; struct usb_device_request setup; } trace; } wmmx_trace_t; int trace_next; wmmx_trace_t *wmmx_traces; static __inline__ void WMMX_REGS(u32 cs0, char *msg) { if ((trace_next < TRACE_MAX) && wmmx_traces) { wmmx_trace_t *p = wmmx_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = wmmx_regs; p->trace.regs.cs0 = cs0; p->trace.regs.msg = msg; } } static __inline__ void WMMX_SETUP(struct usb_device_request *setup) { if ((trace_next < TRACE_MAX) && wmmx_traces) { wmmx_trace_t *p = wmmx_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = wmmx_setup; memcpy(&p->trace.setup, setup, sizeof(struct usb_device_request)); } } static __inline__ void WMMX_XMIT(u32 size) { if ((trace_next < TRACE_MAX) && wmmx_traces) { wmmx_trace_t *p = wmmx_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = wmmx_xmit; p->trace.xmit.size = size; } } static __inline__ void WMMX_CCR(u32 ccr, char *msg) { if ((trace_next < TRACE_MAX) && wmmx_traces) { wmmx_trace_t *p = wmmx_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = wmmx_ccr; p->trace.ccr.ccr = ccr; p->trace.ccr.msg = msg; } } static __inline__ void WMMX_IRO(u32 iro, char *msg) { if ((trace_next < TRACE_MAX) && wmmx_traces) { wmmx_trace_t *p = wmmx_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = wmmx_iro; p->trace.iro.iro = iro; p->trace.iro.msg = msg; } } static __inline__ void WMMX_XFER(char dir, u32 size, char *data) { if ((trace_next < TRACE_MAX) && wmmx_traces) { wmmx_trace_t *p = wmmx_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = wmmx_xfer; p->trace.xfer.dir = dir; p->trace.xfer.size = size; if (size > WMMX_XFER_MAX_TRACE_SIZE) size = WMMX_XFER_MAX_TRACE_SIZE; memcpy(&p->trace.xfer.data, data, size); } } #else static __inline__ void WMMX_REGS(u32 cs0, char *msg) { } static __inline__ void WMMX_SETUP(struct usb_device_request *setup) { } static __inline__ void WMMX_XMIT(u32 size) { } static __inline__ void WMMX_CCR(u32 ccr, char *msg) { } static __inline__ void WMMX_IRO(u32 iro, char *msg) { } static __inline__ void WMMX_XFER(char dir, u32 size, char *data) { } #endif #define WMMX_XFER_DIR_OUT 0 #define WMMX_XFER_DIR_IN 1 static int winhost_flag = 0; static int udcbegin_flag = 1; static int udc_suspended; static struct usb_device_instance *udc_device; /* * physical endpoints to logical endpoints */ static struct usb_endpoint_instance *phys_ep_to_endpoints[UDC_MAX_ENDPOINTS]; static int logic_to_phys_eps[MAX_LOGICAL_CONFIGURATIONS][MAX_LOGICAL_ENDPOINTS]; static struct urb *ep0_urb; extern unsigned int udc_interrupts; static int jifs(void) { static unsigned long jiffies_last; int elapsed = OSCR - jiffies_last; jiffies_last = OSCR; return elapsed; } /* * Map logical to physical */ typedef enum ep { ep_control = 0x0, ep_bulk_in = 0x5, ep_bulk_out = 0x4, ep_iso_in = 0x3, ep_iso_out = 0x2, ep_interrupt = 0x7 } ep_t; struct ep_map { int logical; ep_t eptype; int size; int dma_chan; volatile u32 * drcmr; int accum_size; }; static struct ep_map ep_maps[UDC_MAX_ENDPOINTS]={ { logical: 0, eptype: ep_control, size: 16, }, }; struct wmmx_usb_dma_config { int dma_enabled; int dma_channel; int ep; /* physical endpoints */ struct usb_endpoint_instance* endpoint; /* endpoint instance */ int size; u8* dma_buffer; /* DMA data buffer */ dma_addr_t dma_phys_buffer; }; struct wmmx_usb_dma_config wmmx_usb_dma_configs[UDC_MAX_ENDPOINTS]; struct wmmx_usb_dma_config *wmmx_dmach_to_configs[16]; #define DMA_BUFFER_SIZE 4096 unsigned char* wmmx_usb_dma_buffer,*wmmx_usb_dma_next; dma_addr_t wmmx_usb_dma_phys_buffer; #define WMMX_STORAGE_ACCUM_SIZE 512 /* Note: Please don't add any instruction to try print some messages in the * function. Any careless operation will cause the timing runing away. */ static __inline__ int wmmx_logic_to_phys_ep(int logic) { int config; config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S; return logic_to_phys_eps[config][logic]; } static __inline__ void wmmx_enable_ep_interrupt(int phys_ep, u8 irq_type) { irq_type &= (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP); if (phys_ep < 16) { UDCICR0 |= irq_type << (phys_ep << 1); } else { UDCICR1 |= irq_type << ((phys_ep - 16) << 1); } } static __inline__ void wmmx_disable_ep_interrupt(int phys_ep, u8 irq_type) { irq_type &= (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP); if (phys_ep < 16) { UDCICR0 &= ~(irq_type << (phys_ep << 1)); } else { UDCICR1 &= ~(irq_type << ((phys_ep - 16) << 1)); } } static __inline__ void wmmx_ep_reset_interrupt_status(int phys_ep) { const int intstat = (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP); if (phys_ep < 16) { UDCISR0 = (intstat << (phys_ep << 1)); } else { UDCISR1 = (intstat << ((phys_ep - 16) << 1)); } } static __inline__ void wmmx_out_flush(int phys_ep) { u32 data; int len = UDCBCN(phys_ep); WMMX_XMIT(len); while (len > 0) { data = UDCDN(phys_ep); len -= 4; } } static void wmmx_out_n(int phys_ep, struct usb_endpoint_instance *endpoint) { wmmx_ep_reset_interrupt_status(phys_ep); if (UDCCSN(phys_ep) & UDCCSR_PC) { if (endpoint) { if (!endpoint->rcv_urb) { endpoint->rcv_urb = first_urb_detached(&endpoint->rdy); } if (endpoint->rcv_urb) { int len = 0; int stop_accumulation; u32 *cp = (u32*)(endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length); if (cp) { int count = MIN(UDCBCN(phys_ep), endpoint->rcv_packetSize); len = count; while (count > 0) { *cp++ = UDCDN(phys_ep); count -= 4; } /* clear PC and interrupt */ UDCCSN(phys_ep) = UDCCSR_PC | (UDCCSR_WR_MASK & UDCCSN(phys_ep)); } else { wmmx_out_flush(phys_ep); } stop_accumulation = ((ep_maps[phys_ep].accum_size > 0) && (endpoint->rcv_urb->actual_length + len == ep_maps[phys_ep].accum_size)); WMMX_XFER(WMMX_XFER_DIR_OUT, len, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length); /* fall through if error of any type, len = 0 */ usbd_rcv_complete_irq (endpoint, len, 0); /* simulate ZLP to stop accumulating the data and * send the received data to the storage driver */ if (stop_accumulation) { usbd_rcv_complete_irq (endpoint, 0, 0); } return; } else { wmmx_out_flush(phys_ep); } } } UDCCSN(phys_ep) = (UDCCSN(phys_ep) & UDCCSR_WR_MASK) | UDCCSR_PC; } static void wmmx_start_n_dma(unsigned int phys_ep, struct usb_endpoint_instance *endpoint) { if (endpoint->tx_urb) { int last; struct wmmx_usb_dma_config* config = &wmmx_usb_dma_configs[phys_ep]; int channel = config->dma_channel; struct urb* urb = endpoint->tx_urb; if ((last = MIN(urb->actual_length - (endpoint->sent + endpoint->last), endpoint->tx_packetSize))) { config->size = last; WMMX_XFER(WMMX_XFER_DIR_IN, last, urb->buffer + endpoint->sent); memcpy(config->dma_buffer, urb->buffer + endpoint->sent, last); /* Enable End of Receive IRQ */ DCSR(channel) = DCSR_NODESC | DCSR_EORIRQEN; DTADR(channel) = PHYS_UDCDN(phys_ep); DSADR(channel) = config->dma_phys_buffer; DCMD(channel) = DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_ENDIRQEN | DCMD_BURST32 | DCMD_WIDTH4 | (last & DCMD_LENGTH); DRCMRUDC(phys_ep) = DRCMR_MAPVLD | channel; DCSR(channel) |= DCSR_RUN; } else { usbd_tx_complete_irq (endpoint, 0); } } } static void wmmx_usb_dma_tx_irq(int dmach, void* dev_id, struct pt_regs *regs) { unsigned int dcsr = DCSR(dmach); struct wmmx_usb_dma_config* config = wmmx_dmach_to_configs[dmach]; struct usb_endpoint_instance* endpoint = config->endpoint; static int timeout; DCSR(dmach) &= ~DCSR_RUN; if (dcsr & DCSR_BUSERR) { DCSR(dmach) |= DCSR_BUSERR; printk(KERN_ERR "BUS error.\n"); } else if (dcsr & DCSR_ENDINTR) { DCSR(dmach) |= DCSR_ENDINTR; if (config->size < endpoint->tx_packetSize) { UDCCSN(config->ep) = (UDCCSN(config->ep) & UDCCSR_WR_MASK) | UDCCSR_SP; #if 0 /* It may be incorrect to wait in the interrupt handler * until the FIFO will have enough room for at least * one packet */ /* Here we should add some delays */ timeout = 0; while (!(UDCCSN(config->ep) & UDCCSR_FS)) { if (timeout++ == 100) { printk(KERN_ERR "UDCCSN(%d)=%08x\n", config->ep, UDCCSN(config->ep)); break; } udelay(1); } #endif } endpoint->last += config->size; } else printk(KERN_ERR"DCSR(%d) = 0x%08x\n", dmach, DCSR(dmach)); usbd_tx_complete_irq(endpoint, 0); if (config->size < endpoint->tx_packetSize) return; else { wmmx_start_n_dma(config->ep, endpoint); } } static void wmmx_start_n(unsigned int phys_ep, struct usb_endpoint_instance *endpoint) { if (endpoint->tx_urb) { int last; struct urb *urb = endpoint->tx_urb; if ((last = MIN(urb->actual_length - (endpoint->sent + endpoint->last), endpoint->tx_packetSize))) { int size = last; u32 *data = (u32*)(urb->buffer + endpoint->sent + endpoint->last); u32 *cp; int remain = 0; u8 *rd; WMMX_XFER(WMMX_XFER_DIR_IN, last, (char *)data); cp = data; remain = size & 0x03; size &= ~0x03; while (size > 0) { UDCDN(phys_ep) = *cp++; size -= 4; } if (remain) { volatile u8 *reg = (volatile u8 *)PUDCDN(phys_ep); rd = (u8 *)cp; while (remain--) { *reg = *rd++; } } if ((last < endpoint->tx_packetSize) || ((endpoint->tx_urb->actual_length - endpoint->sent) == last)) { if (last != endpoint->tx_packetSize) { UDCCSN(phys_ep) = (UDCCSN(phys_ep) & UDCCSR_WR_MASK) | UDCCSR_SP; } } endpoint->last += last; } else { usbd_tx_complete_irq (endpoint, 0); } } } static void wmmx_in_n(unsigned int phys_ep, struct usb_endpoint_instance *endpoint) { int udccsn; wmmx_ep_reset_interrupt_status(phys_ep); /* if TPC update tx urb and clear TPC */ if ((udccsn = UDCCSN(phys_ep)) & UDCCSR_PC) { UDCCSN(phys_ep) = (UDCCSN(phys_ep) & UDCCSR_WR_MASK) | UDCCSR_PC; usbd_tx_complete_irq(endpoint, 0); } if (udccsn & UDCCSR_FS) { wmmx_start_n(phys_ep, endpoint); } /* clear underrun, not much we can do about it */ if (udccsn & UDCCSR_TRN) { UDCCSN(phys_ep) = (UDCCSN(phys_ep) & UDCCSR_WR_MASK) | UDCCSR_TRN; } } static void wmmx_usb_dma_rx_irq(int dmach, void* dev_id, struct pt_regs *regs) { unsigned int dcsr = DCSR(dmach); struct wmmx_usb_dma_config *config = wmmx_dmach_to_configs[dmach]; struct usb_endpoint_instance *endpoint = config->endpoint; unsigned int phys_ep = config->ep; DCSR(dmach) &= ~DCSR_RUN; if (dcsr & DCSR_BUSERR) { DCSR(dmach) |= DCSR_BUSERR; printk(KERN_ERR"DMA bus error\n"); } else if (dcsr & DCSR_ENRINTR) { DCSR(dmach) |= DCSR_ENDINTR; if (!endpoint->rcv_urb) { endpoint->rcv_urb = first_urb_detached(&endpoint->rdy); } if (endpoint->rcv_urb) { int len; int stop_accumulation; unsigned char* cp = endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length; if (UDCCSN(config->ep) & UDCCSR_SP) { len = endpoint->rcv_packetSize - (DCMD(dmach) & DCMD_LENGTH); } else { len = endpoint->rcv_packetSize; } memcpy(cp, config->dma_buffer, len); stop_accumulation = ((ep_maps[phys_ep].accum_size > 0) && (endpoint->rcv_urb->actual_length + len == ep_maps[phys_ep].accum_size)); WMMX_XFER(WMMX_XFER_DIR_OUT, len, endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length); usbd_rcv_complete_irq (endpoint, len, 0); /* simulate ZLP to stop accumulating the data */ if (stop_accumulation) { usbd_rcv_complete_irq (endpoint, 0, 0); } if (len == 0) { UDCCSN(config->ep) = UDCCSR_SP | UDCCSR_PC | ((UDCCSN(config->ep) & UDCCSR_WR_MASK)); } else if (len < endpoint->rcv_packetSize) { UDCCSN(config->ep) = UDCCSR_PC | ((UDCCSN(config->ep) & UDCCSR_WR_MASK)); } } } else printk(KERN_ERR"DCSR(%d) = 0x%08x\n", dmach, DCSR(dmach)); /* Listen to the request */ DCSR(dmach) = DCSR_NODESC | DCSR_EORIRQEN; DTADR(dmach) = wmmx_usb_dma_configs[config->ep].dma_phys_buffer; DSADR(dmach) = PHYS_UDCDN(config->ep); DCMD(dmach) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_ENDIRQEN | DCMD_BURST32 | DCMD_WIDTH4 | (endpoint->rcv_packetSize & DCMD_LENGTH); DRCMRUDC(config->ep) = dmach | DRCMR_MAPVLD; DCSR(dmach) |= DCSR_RUN; } void wmmx_ep0recv(struct usb_endpoint_instance *endpoint, volatile u32 udccsr0) { struct urb *urb = endpoint->rcv_urb; int retval; WMMX_REGS(udccsr0, " <-- setup data recv"); /* check for premature status stage */ if (!(udccsr0 & UDCCSR0_OPC) && !(udccsr0 & UDCCSR0_IPR)) { if (urb->device_request.wLength == urb->actual_length) { retval = usbd_recv_setup(ep0_urb); if (retval != 0) { UDCCSR0 = UDCCSR0_FST; endpoint->state = WAIT_FOR_SETUP; WMMX_REGS(udccsr0, " --> bad setup FST"); } } else { urb->actual_length = 0; } endpoint->state = WAIT_FOR_SETUP; } /* receive more data */ if ((udccsr0 & UDCCSR0_OPC) && !(udccsr0 & UDCCSR0_SA)) { int size = urb->device_request.wLength; u32* cp = (u32*) (urb->buffer + urb->actual_length); while (size > 0) { *cp++ = UDCDN(0); size -= 4; } /* resize the actual length of URB structure. */ urb->actual_length += urb->device_request.wLength; UDCCSR0 |= UDCCSR0_OPC; /* to allow to enter a premature STATUS IN stage */ UDCCSR0 |= UDCCSR0_IPR; } return; } void wmmx_ep0xmit(struct usb_endpoint_instance *endpoint, volatile u32 udccsr0) { int short_packet; int size; struct urb *urb = endpoint->tx_urb; WMMX_REGS(udccsr0, " --> xmit"); /* check for premature status stage - host abandoned previous IN */ if ((udccsr0 & UDCCSR0_OPC) && !(udccsr0 & UDCCSR0_SA)) { /* clear tx fifo and opr */ UDCCSR0 |= UDCCSR0_FTF | UDCCSR0_OPC; endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; endpoint->rcv_urb = NULL; WMMX_REGS(UDCCSR0, " <-- xmit premature status"); return; } /* check for stall */ if (udccsr0 & UDCCSR0_SST) { /* clear stall and tx fifo */ UDCCSR0 |= UDCCSR0_SST | UDCCSR0_FTF; endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; endpoint->rcv_urb = NULL; WMMX_REGS(UDCCSR0, " <-- xmit stall"); return; } if (NULL == urb) { size = 0; } else { endpoint->last = size = MIN(urb->actual_length - endpoint->sent, endpoint->tx_packetSize); } short_packet = (size < endpoint->tx_packetSize); WMMX_XMIT(size); if (size > 0 && urb->buffer) { u32* cp = (u32*)(urb->buffer + endpoint->sent); int remain = 0; u8 *rd; remain = size & 0x03; size &= ~0x3; while (size) { UDCDN(0) = *cp++; size -= 4; } if (remain) { volatile u8 *reg = (volatile u8 *)PUDCDN(0); rd = (u8 *)cp; while (remain--) { *reg = *rd++; } } } if (!endpoint->tx_urb || (endpoint->last < endpoint->tx_packetSize)) { /* Tell the UDC we are at the end of the packet. */ UDCCSR0 |= UDCCSR0_IPR; endpoint->state = WAIT_FOR_OUT_STATUS; WMMX_REGS(UDCCSR0, " <-- xmit wait for status"); } else if ((endpoint->last == endpoint->tx_packetSize) && ((endpoint->last + endpoint->sent) == endpoint->tx_urb->actual_length) && ((endpoint->tx_urb->actual_length) < le16_to_cpu(endpoint->tx_urb->device_request.wLength))) { endpoint->state = DATA_STATE_NEED_ZLP; WMMX_REGS(UDCCSR0, " <-- xmit need zlp"); } else { WMMX_REGS(UDCCSR0, " <-- xmit not finished"); } } void wmmx_ep0setup(struct usb_endpoint_instance *endpoint, u32 udccsr0) { if ((udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC | UDCCSR0_RNE)) == (UDCCSR0_SA | UDCCSR0_OPC | UDCCSR0_RNE)) { u32 dummy; int count; int len; int retval; u32 *cp = (u32 *)&ep0_urb->device_request; WMMX_REGS(udccsr0, " --> setup"); count = len = UDCBCN(0); if (count > 8) { dbg_rx(6, "Received a setup packet(len(%d)>8\n", len); if (!(winhost_flag || udcbegin_flag)) { printk(KERN_INFO "Received a setup packet(len(%d)>8)\n", len); } count = 8; } while (count > 0) { *cp++ = UDCDN(0); count -= 4; len -= 4; } while (len > 0) { dummy = UDCDN(0); len -= 4; } WMMX_SETUP(&ep0_urb->device_request); if (((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) && le16_to_cpu(ep0_urb->device_request.wLength) != 0) { endpoint->state = DATA_STATE_RECV; ep0_urb->actual_length = 0; endpoint->rcv_urb = ep0_urb; UDCCSR0 = UDCCSR0_SA | UDCCSR0_OPC; WMMX_REGS(udccsr0, " <-- setup recv"); return; } retval = usbd_recv_setup(ep0_urb); if (retval) { UDCCSR0 = UDCCSR0_FST; endpoint->state = WAIT_FOR_SETUP; WMMX_REGS(udccsr0, " --> bad SETUP FST"); } if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { UDCCSR0 |= UDCCSR0_IPR; WMMX_REGS(UDCCSR0," <-- setup nodata"); } else { /* Control Read */ if (!le16_to_cpu(ep0_urb->device_request.wLength)) { udc_stall_ep(0); WMMX_REGS(UDCCSR0, " <-- setup stalling " "zero wLength"); return; } /* verify that we have non-zero length response */ if (!ep0_urb->actual_length) { udc_stall_ep (0); WMMX_REGS(UDCCSR0," <-- setup stalling " "zero response"); return; } endpoint->tx_urb = ep0_urb; endpoint->sent = 0; endpoint->last = 0; endpoint->state = DATA_STATE_XMIT; wmmx_ep0xmit(endpoint, UDCCSR0); /* Clear SA and OPR bits */ UDCCSR0 = UDCCSR0_SA | UDCCSR0_OPC; WMMX_REGS(UDCCSR0," <-- setup data"); } } } static void wmmx_ep0(struct usb_endpoint_instance *endpoint, u32 udccsr0) { int j = 0; WMMX_REGS(udccsr0," --> ep0"); if ((endpoint->state != DATA_STATE_PENDING_XMIT) && (udccsr0 & UDCCSR0_SST)) { wmmx_ep_reset_interrupt_status(0); UDCCSR0 |= UDCCSR0_SST; WMMX_REGS(udccsr0," --> ep0 clear SST"); if (endpoint) { endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; endpoint->rcv_urb = NULL; } return; } if (!endpoint) { wmmx_ep_reset_interrupt_status(0); UDCCSR0 |= UDCCSR0_IPR | UDCCSR0_OPC | UDCCSR0_SA; WMMX_REGS(UDCCSR0," ep0 NULL"); return; } if (endpoint->tx_urb) { usbd_tx_complete_irq (endpoint, 0); if (!endpoint->tx_urb) { if (endpoint->state != DATA_STATE_NEED_ZLP) { endpoint->state = WAIT_FOR_OUT_STATUS; } } } if ((endpoint->state != DATA_STATE_PENDING_XMIT) && (endpoint->state != WAIT_FOR_SETUP) && (udccsr0 & UDCCSR0_SA)) { WMMX_REGS(udccsr0," --> ep0 early SA"); endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; endpoint->rcv_urb = NULL; } WMMX_REGS(endpoint->state," -- endpoint state"); switch (endpoint->state) { case DATA_STATE_NEED_ZLP: UDCCSR0 |= UDCCSR0_IPR; endpoint->state = WAIT_FOR_OUT_STATUS; break; case WAIT_FOR_OUT_STATUS: if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA)) == UDCCSR0_OPC) { UDCCSR0 |= UDCCSR0_OPC; } WMMX_REGS(UDCCSR0," --> ep0 WAIT for STATUS"); endpoint->state = WAIT_FOR_SETUP; case WAIT_FOR_SETUP: do { wmmx_ep0setup(endpoint, UDCCSR0); if (endpoint->state == DATA_STATE_PENDING_XMIT) { if (udccsr0 & UDCCSR0_SST) { wmmx_ep_reset_interrupt_status(0); UDCCSR0 = UDCCSR0_SST | UDCCSR0_OPC | UDCCSR0_SA; return; } break; } if (udccsr0 & UDCCSR0_SST) { wmmx_ep_reset_interrupt_status(0); UDCCSR0 |= UDCCSR0_SST | UDCCSR0_OPC | UDCCSR0_SA; WMMX_REGS(udccsr0," --> ep0 clear SST"); if (endpoint) { endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; endpoint->rcv_urb = NULL; } return; } if (j++ > 2) { u32 udccsr0 = UDCCSR0; WMMX_REGS(udccsr0," ep0 wait"); if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) == (UDCCSR0_OPC | UDCCSR0_SA)) { UDCCSR0 |= (UDCCSR0_OPC | UDCCSR0_SA); WMMX_REGS(UDCCSR0," ep0 force"); } else { UDCCSR0 |= (UDCCSR0_OPC | UDCCSR0_SA); WMMX_REGS(UDCCSR0," ep0 force and return"); break; } } } while (UDCCSR0 & (UDCCSR0_OPC | UDCCSR0_RNE)); break; case DATA_STATE_XMIT: wmmx_ep0xmit(endpoint, UDCCSR0); break; case DATA_STATE_PENDING_XMIT: wmmx_ep0xmit(endpoint, UDCCSR0); case DATA_STATE_RECV: wmmx_ep0recv(endpoint, UDCCSR0); break; } wmmx_ep_reset_interrupt_status(0); WMMX_REGS(endpoint->state," -- endpoint state result"); WMMX_REGS(UDCCSR0," <-- ep0"); } static __inline__ int endpoints_have_interrupt(void) { u32 udcisr0, udcisr1; udcisr0 = UDCISR0; udcisr1 = UDCISR1; if (udcisr0) return 1; if (udcisr1) return 1; return 0; } /* * Bulverde UDC Interrupt Handler */ static void wmmx_int_hndlr (int irq, void *dev_id, struct pt_regs *regs) { u32 udcisr0, udcisr1; int udccr = UDCCR; int ep; udc_interrupts++; udcisr0 = UDCISR0; udcisr1 = UDCISR1 & 0xffff; while (udcisr0 || udcisr1) { u32 udccsr0 = UDCCSR0; WMMX_IRO(udcisr0, "------------------------> Interrupt"); /* For Endpoint0, Endpoint A-P */ for (ep = 0; udcisr0 != 0 && ep < 16 ; udcisr0 >>= 2, ep++) { if (udcisr0 & UDC_INT_PACKETCMP) { switch (ep_maps[ep].eptype) { case ep_control: wmmx_ep0(phys_ep_to_endpoints[0], udccsr0); break; case ep_bulk_in: case ep_interrupt: if (wmmx_usb_dma_configs[ep].dma_enabled) { wmmx_ep_reset_interrupt_status(ep); break; } wmmx_in_n(ep, phys_ep_to_endpoints[ep]); break; case ep_bulk_out: if (wmmx_usb_dma_configs[ep].dma_enabled) { wmmx_ep_reset_interrupt_status(ep); break; } while (UDCCSN(ep) & UDCCSR_PC) { wmmx_out_n(ep, phys_ep_to_endpoints[ep]); } break; case ep_iso_in: case ep_iso_out: wmmx_ep_reset_interrupt_status(ep); break; } } if (udcisr0 & UDC_INT_FIFOERROR) { printk("FIFO error on %d endpoint\n", ep); } } /* For Endpoint Q-X */ for ( ; udcisr1 != 0 && ep < 24; udcisr1 >>= 2, ep++) { /* I will rebuild ep_maps array */ if (udcisr1 & UDC_INT_PACKETCMP) { switch (ep_maps[ep].eptype) { case ep_bulk_in: case ep_interrupt: wmmx_in_n(ep, phys_ep_to_endpoints[ep]); break; case ep_bulk_out: while (UDCCSN(ep) & UDCCSR_PC) { wmmx_out_n(ep, phys_ep_to_endpoints[ep]); } break; case ep_iso_in: case ep_iso_out: wmmx_ep_reset_interrupt_status(ep); break; default: break; } } } udcisr0 = UDCISR0; udcisr1 = UDCISR1 & 0xffff; } /* Reset, Suspend, Resume interrupts */ udcisr1 = UDCISR1; /* UDC Reset */ if (udcisr1 & UDCISR1_IERS) { WMMX_CCR(udccr, "------------------------> Reset"); udc_suspended = 0; usbd_device_event (udc_device, DEVICE_RESET, 0); usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); UDCISR1 = UDCISR1_IERS; } /* UDC Resume */ if (udcisr1 & UDCISR1_IERU) { WMMX_CCR(udccr, "------------------------> Resume"); if (udc_suspended) { udc_suspended = 0; usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); } UDCISR1 = UDCISR1_IERU; } /* UDC Suspend */ if (udcisr1 & UDCISR1_IESU) { WMMX_CCR(udccr, "------------------------> Suspend"); if (!udc_suspended) { udc_suspended = 1; usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); } UDCISR1 = UDCISR1_IESU; } /* Configuration Change Interrupt */ if (udcisr1 & UDCISR1_IECC) { WMMX_CCR(udccr, "------------------------> Configuration " "Changed"); UDCCR |= UDCCR_SMAC; udc_device->configuration = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S; udc_device->interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S; udc_device->alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S; usbd_device_event(udc_device, DEVICE_CONFIGURED, 0); UDCISR1 = UDCISR1_IECC; } WMMX_CCR(UDCCR, "<-- Interrupt"); } /* * Setting the endpoint configuration register */ static int udc_set_endpoint(struct usb_endpoint_description* endpoint, int configuration, int interface, int alternate, int eps, int accum_size) { u32 config_reg; static int phys_endpoints = 0; phys_endpoints ++; config_reg = ((configuration << UDCCONR_CN_S) & UDCCONR_CN) | ((interface << UDCCONR_IN_S) & UDCCONR_IN) | ((alternate << UDCCONR_AISN_S) & UDCCONR_AISN) | ((eps << UDCCONR_EN_S) & UDCCONR_EN); logic_to_phys_eps[configuration][endpoint->bEndpointAddress & 0xf] = phys_endpoints; /* Double Buffering Enabled and Enable the EP */ config_reg |= UDCCONR_EE | UDCCONR_DE; if (endpoint->direction == IN) { config_reg |= UDCCONR_ED; /* Direction: IN */ } else { config_reg &= ~UDCCONR_ED; /* Direction: OUT*/ } config_reg |= ((endpoint->bmAttributes << UDCCONR_ET_S) & UDCCONR_ET) | ((endpoint->wMaxPacketSize << UDCCONR_MPS_S) & UDCCONR_MPS); UDCCN(phys_endpoints) = config_reg; ep_maps[phys_endpoints].logical = endpoint->bEndpointAddress; ep_maps[phys_endpoints].eptype = (((config_reg & UDCCONR_ET) >> UDCCONR_ET_S) << 1) | !!(config_reg & UDCCONR_ED); ep_maps[phys_endpoints].size = endpoint->wMaxPacketSize; ep_maps[phys_endpoints].accum_size = accum_size; return 0; } /* * Check the function driver and set UDC Endpoint A-X Configuration Registers */ static int udc_check_function(struct usb_function_instance* function) { int total = 1; int i,j,k,m; struct usb_configuration_description* configuration; struct usb_interface_description* interface; struct usb_alternate_description* alternate; struct usb_endpoint_description* endpoint; if (function->function_driver->configurations > MAX_LOGICAL_CONFIGURATIONS) { printk(KERN_ERR"Too many configurations.\n"); return -1; } for (i = 0; i < function->function_driver->configurations; i++) { configuration = function->function_driver->configuration_description + i; for (j = 0; j < configuration->interfaces; j++) { int logical_endpoints = 1; interface = configuration->interface_list + j; for (k = 0; k < interface->alternates; k++) { alternate = interface->alternate_list + k; for (m = 0; m < alternate->endpoints; m++) { if (alternate->endpoints > MAX_LOGICAL_ENDPOINTS) { printk(KERN_ERR "Too many logical endpoints.\n"); return -1; } endpoint = alternate->endpoint_list + m; if (udc_set_endpoint(endpoint, i+1, j, k, logical_endpoints++, (interface->bInterfaceClass == USB_CLASS_MASS_STORAGE) ? WMMX_STORAGE_ACCUM_SIZE : 0) < 0) { printk(KERN_ERR "udc_set_endpoint() error.\n"); return -1; } total++; } } } } return total; } /* * Enable UDC configuration setting */ static int udc_configuration_enable(struct usb_device_instance* device) { struct usb_function_instance* function; int count, i; count = i = 0; function = device->function_instance_array + i; if (!function) { printk(KERN_ERR"No valid function driver!\n"); return -1; } count = udc_check_function(function); if (count < 0) { printk(KERN_ERR"udc_check_function(...) failed.\n"); return -1; } return 0; } /* * Public Functions. */ /* * udc_start_in_irq - start transmit * @eendpoint: endpoint instance * * Called by bus interface driver to see if we need to * start a data transmission. */ void udc_start_in_irq (struct usb_endpoint_instance *endpoint) { int phys_ep = wmmx_logic_to_phys_ep(endpoint->endpoint_address & 0xf); if (endpoint->endpoint_address == 0) { struct urb* urb = endpoint->tx_urb; if (!urb) { printk(KERN_INFO "udc_start_in_irq() urb is NULL\n"); return; } wmmx_ep0xmit(endpoint, UDCCSR0); } else if (UDCCSN(phys_ep) & UDCCSR_FS) { if (wmmx_usb_dma_configs[phys_ep].dma_enabled) wmmx_start_n_dma(phys_ep, endpoint); else wmmx_start_n(phys_ep, endpoint); } } /* * udc_init - initialize * * Return non-zero if we cannot see device. */ int udc_init (void) { int i; dbg_tx(6, "udc_init:\n"); udc_disable_interrupts (NULL); for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { memset(&wmmx_usb_dma_configs[i], 0, sizeof(struct wmmx_usb_dma_config)); } for (i = 0; i < 16; i++) { wmmx_dmach_to_configs[i] = NULL; } wmmx_usb_dma_next = wmmx_usb_dma_buffer = consistent_alloc(GFP_KERNEL, DMA_BUFFER_SIZE, &wmmx_usb_dma_phys_buffer); if (!wmmx_usb_dma_buffer) { printk(KERN_ERR"udc_init: consistent_alloc() failed\n"); return 1; } return 0; } /* * udc_start_in - start transmit * @eendpoint: endpoint instance * * Called by bus interface driver to see if we need to start * a data transmission. */ void udc_start_in (struct usb_endpoint_instance *endpoint) { if (endpoint) { unsigned long flags; local_irq_save (flags); udc_start_in_irq(endpoint); local_irq_restore (flags); } } /* * udc_stall_ep - stall endpoint * @ep: physical endpoint * * Stall the endpoint. */ void udc_stall_ep (unsigned int phys_ep) { dbg_usbe(5, "usc_stall_ep ep=%d\n", phys_ep); if (phys_ep < UDC_MAX_ENDPOINTS) { UDCCSN(phys_ep) |= UDCCSR_FST; } } /* * udc_reset_ep - reset endpoint * @ep: physical endpoint * reset the endpoint. * * returns : 0 if ok, -1 otherwise */ void udc_reset_ep (unsigned int phys_ep) { if (phys_ep == 0) { UDCCSN(phys_ep) |= ((UDCCSN(phys_ep) & UDCCSR_DME) | UDCCSR_FEF); } } /* * udc_endpoint_halted - is endpoint halted * @ep: * * Return non-zero if endpoint is halted */ int udc_endpoint_halted (unsigned int phys_ep) { 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. */ void udc_set_address (unsigned char address) { } /* * udc_serial_init - set a serial number if available */ int __init udc_serial_init (struct usb_bus_instance *bus) { return -EINVAL; } /* * udc_max_endpoints - max physical endpoints * * Return number of physical endpoints. */ int udc_max_endpoints (void) { return UDC_MAX_ENDPOINTS; } /* * udc_check_ep - check logical endpoint * @lep: * * Return physical endpoint number to use for this logical * endpoint or zero if not valid. */ int udc_check_ep(int logic_ep, int packetsize) { if (packetsize > 64) return 0; return wmmx_logic_to_phys_ep(logic_ep & 0x0f); } static int wmmx_usb_dma_init(unsigned int phys_ep, struct usb_endpoint_instance* endpoint) { wmmx_usb_dma_configs[phys_ep].ep = phys_ep; wmmx_usb_dma_configs[phys_ep].endpoint = endpoint; if (ep_maps[phys_ep].eptype == ep_bulk_in) { wmmx_usb_dma_configs[phys_ep].dma_channel = pxa_request_dma("WMMX DMA IN", 0, wmmx_usb_dma_tx_irq, &wmmx_usb_dma_configs[phys_ep]); if (wmmx_usb_dma_configs[phys_ep].dma_channel < 0) { printk(KERN_ERR "pxa_request_dma(WMMX DMA IN) " "failed %d\n", wmmx_usb_dma_configs[phys_ep].dma_channel); return 1; } if ((wmmx_usb_dma_next - wmmx_usb_dma_buffer) > DMA_BUFFER_SIZE) { printk(KERN_ERR"Out of memory\n"); return 1; } wmmx_usb_dma_configs[phys_ep].dma_buffer = wmmx_usb_dma_next; wmmx_usb_dma_configs[phys_ep].dma_phys_buffer = wmmx_usb_dma_phys_buffer + (wmmx_usb_dma_next - wmmx_usb_dma_buffer); wmmx_usb_dma_next = (void *)(((u32)wmmx_usb_dma_next + endpoint->tx_packetSize + 3) & ~3); wmmx_dmach_to_configs[wmmx_usb_dma_configs[phys_ep].dma_channel] = &wmmx_usb_dma_configs[phys_ep]; wmmx_usb_dma_configs[phys_ep].dma_enabled = 1; } else if (ep_maps[phys_ep].eptype == ep_bulk_out) { wmmx_usb_dma_configs[phys_ep].dma_channel = pxa_request_dma("WMMX DMA OUT", 0, wmmx_usb_dma_rx_irq, &wmmx_usb_dma_configs[phys_ep]); if (wmmx_usb_dma_configs[phys_ep].dma_channel < 0) { printk(KERN_ERR"pxa_request_dma(WMMX_DMA_OUT) failed %d\n", wmmx_usb_dma_configs[phys_ep].dma_channel); return 1; } wmmx_usb_dma_configs[phys_ep].dma_buffer = wmmx_usb_dma_next; wmmx_usb_dma_configs[phys_ep].dma_phys_buffer = wmmx_usb_dma_phys_buffer + (wmmx_usb_dma_next - wmmx_usb_dma_buffer); wmmx_usb_dma_next = (void *)(((u32)wmmx_usb_dma_next + endpoint->tx_packetSize + 3) & ~3); wmmx_dmach_to_configs[wmmx_usb_dma_configs[phys_ep].dma_channel] = &wmmx_usb_dma_configs[phys_ep]; wmmx_usb_dma_configs[phys_ep].dma_enabled = 1; } else { printk(KERN_ERR"wmmx_usb_dma_init(): invalid endpoint type.\n"); wmmx_usb_dma_configs[phys_ep].dma_enabled = 0; return 1; } return 0; } /* * udc_set_ep - setup endpoint * @ep: * @endpoint: * * Associate a physical endpoint with endpoint_instance */ void udc_setup_ep (struct usb_device_instance *device, unsigned int phys_ep, struct usb_endpoint_instance *endpoint) { if (phys_ep < UDC_MAX_ENDPOINTS) { phys_ep_to_endpoints[phys_ep] = endpoint; if (phys_ep == 0) { printk(KERN_DEBUG "udc_setup_ep: 0 do nothing\n"); wmmx_enable_ep_interrupt(0, (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP)); } else if (endpoint->endpoint_address & 0x80) { /* IN */ /* only support bulk in DMA mode now */ /* When you implement your function driver, you should decide whether the endpoint will use DMA mode or not */ #ifdef CONFIG_USBD_WMMX_DMA_IN if ((ep_maps[phys_ep].eptype == ep_bulk_in) && (!wmmx_usb_dma_configs[phys_ep].dma_enabled)) { if (!wmmx_usb_dma_init(phys_ep, endpoint)) { UDCCSN(phys_ep) |= UDCCSR_DME; } } #endif /* if dma can't be initialized successfully, we enable the interrupt. */ if (!wmmx_usb_dma_configs[phys_ep].dma_enabled) { wmmx_enable_ep_interrupt(phys_ep, (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP)); } } else if (endpoint->endpoint_address) { /* OUT */ usbd_fill_rcv (device, endpoint, 5); endpoint->rcv_urb = first_urb_detached(&endpoint->rdy); /* When you implement your function driver, you should decide whether the endpoint will use DMA mode or not */ #ifdef CONFIG_USBD_WMMX_DMA_OUT if ((ep_maps[phys_ep].eptype == ep_bulk_out) && (!wmmx_usb_dma_configs[phys_ep].dma_enabled)) { if (!wmmx_usb_dma_init(phys_ep, endpoint)) { int channel; channel = wmmx_usb_dma_configs[phys_ep].dma_channel; UDCCSN(phys_ep) |= UDCCSR_DME; wmmx_usb_dma_configs[phys_ep].size = endpoint->rcv_packetSize; /* Listen to the request */ DCSR(channel) = DCSR_NODESC | DCSR_EORIRQEN; DTADR(channel) = wmmx_usb_dma_configs[phys_ep].dma_phys_buffer; DSADR(channel) = PHYS_UDCDN(phys_ep); DCMD(channel) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_ENDIRQEN | DCMD_BURST32 | DCMD_WIDTH4 | endpoint->rcv_packetSize; DRCMRUDC(phys_ep) = channel | DRCMR_MAPVLD; DCSR(channel) |= DCSR_RUN; } } #endif if (!wmmx_usb_dma_configs[phys_ep].dma_enabled) { wmmx_enable_ep_interrupt(phys_ep, (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP)); } } } } /* * udc_disable_ep - disable endpoint * @ep: * * Disable specified endpoint */ void udc_disable_ep (unsigned int phys_ep) { dbg_init(1, "udc_disable_ep: disable endpoint %d", phys_ep); /* if the endpoint use PIO mode, we should disable the endpoint interrupts too. */ if (phys_ep < UDC_MAX_ENDPOINTS) { struct usb_endpoint_instance *endpoint; dbg_init(1, "endpoint descr 0x%08x", phys_ep_to_endpoints[phys_ep]); if ((endpoint = phys_ep_to_endpoints[phys_ep])) { phys_ep_to_endpoints[phys_ep] = NULL; usbd_flush_ep (endpoint); } else { return; } if (phys_ep == 0) { dbg_init(1, "ep0 - ignore"); } else if (endpoint->endpoint_address & 0x80) { /* IN */ if (wmmx_usb_dma_configs[phys_ep].dma_enabled) { if (wmmx_usb_dma_configs[phys_ep].dma_buffer) { consistent_free(wmmx_usb_dma_configs[phys_ep].dma_buffer, wmmx_usb_dma_configs[phys_ep].endpoint->tx_packetSize, wmmx_usb_dma_configs[phys_ep].dma_phys_buffer); wmmx_usb_dma_configs[phys_ep].dma_buffer = NULL; } pxa_free_dma(wmmx_usb_dma_configs[phys_ep].dma_channel); wmmx_dmach_to_configs[wmmx_usb_dma_configs[phys_ep].dma_channel] = NULL; wmmx_usb_dma_configs[phys_ep].dma_enabled = 0; } } else if (endpoint->endpoint_address) { /* OUT */ if (wmmx_usb_dma_configs[phys_ep].dma_enabled) { if (wmmx_usb_dma_configs[phys_ep].dma_buffer) { consistent_free(wmmx_usb_dma_configs[phys_ep].dma_buffer, wmmx_usb_dma_configs[phys_ep].endpoint->rcv_packetSize, wmmx_usb_dma_configs[phys_ep].dma_phys_buffer); wmmx_usb_dma_configs[phys_ep].dma_buffer = NULL; } pxa_free_dma(wmmx_usb_dma_configs[phys_ep].dma_channel); wmmx_dmach_to_configs[wmmx_usb_dma_configs[phys_ep].dma_channel] = NULL; wmmx_usb_dma_configs[phys_ep].dma_enabled = 0; } } } } /* * udc_connected - is the USB cable connected * * Return non-zeron if cable is connected. */ int udc_connected (void) { return 1; } /* * udc_connect - enable pullup resistor * * Turn on the USB connection by enabling the pullup resistor. */ void udc_connect (void) { } /* * udc_disconnect - disable pullup resistor * * Turn off the USB connection by disabling the pullup resistor. */ void udc_disconnect (void) { } /* * udc_enable_interrupts - enable interrupts * * Switch on UDC interrupts. * */ void udc_all_interrupts (struct usb_device_instance *device) { int i; UDCICR1 = (UDCICR1_IESU | UDCICR1_IERU | UDCICR1_IERS | UDCICR1_IECC) & ~UDCICR1_IESOF; /* always enable control endpoint */ wmmx_enable_ep_interrupt(0, /*UDC_INT_FIFOERROR |*/ UDC_INT_PACKETCMP); for (i = 1; i < UDC_MAX_ENDPOINTS; i++) { if (phys_ep_to_endpoints[i] && phys_ep_to_endpoints[i]->endpoint_address) { wmmx_enable_ep_interrupt(i, UDC_INT_FIFOERROR | UDC_INT_PACKETCMP); } } } /* * udc_suspended_interrupts - enable suspended interrupts * * Switch on only UDC resume interrupt. * */ void udc_suspended_interrupts (struct usb_device_instance *device) { UDCICR1 = (UDCICR1 | UDCICR1_IERS | UDCICR1_IERU | UDCICR1_IECC | UDCICR1_IESU) & ~UDCICR1_IESOF; } /* * udc_disable_interrupts - disable interrupts. * * switch off interrupts */ void udc_disable_interrupts (struct usb_device_instance *device) { /* disable sof interrupt */ /* disable suspend and resume interrupt */ /* disable endpoint interrupts */ UDCICR0 = UDCICR1 = 0x00000000; } /* * udc_ep0_packetsize - return ep0 packetsize */ int udc_ep0_packetsize (void) { return EP0_PACKETSIZE; } /* * udc_enable - enable the UDC * * Switch on the UDC */ void udc_enable (struct usb_device_instance *device) { udc_device = device; dbgENTER(dbgflg_usbdbi_init, 1); if (!ep0_urb) { ep0_urb = usbd_alloc_urb(device, device->function_instance_array, 0, 512); if (!ep0_urb) { printk(KERN_ERR "udc_enable: usbd_alloc_urb failed\n"); } } else { printk (KERN_ERR "udc_enable: ep0_urb already allocated\n"); } /* enable UDC clock */ CKEN |= CKEN11_USB; /* enable UDC controller */ UDCCR = UDCCR_UDE; /* * If UDCCR_EMCE is set, we should reduce the number of the * configuration automatically or just yelled. */ if ((UDCCR & UDCCR_EMCE) == UDCCR_EMCE) { printk(KERN_ERR "udc_enable: Endpoint memory configure " "error\n"); UDCCR = UDCCR_EMCE; } } /* * udc_disable - disable the UDC * * Switch off the UDC */ void udc_disable (void) { dbgENTER(dbgflg_usbdbi_init, 1); /* disable UDC controller */ UDCCR &= ~(UDCCR_UDE); /* disable UDC clock */ CKEN &= ~CKEN11_USB; /* reset device pointer */ udc_device = NULL; /* ep0 urb */ if (ep0_urb) { usbd_dealloc_urb (ep0_urb); ep0_urb = 0; } else printk (KERN_ERR "udc_disable: ep0_urb already NULL\n"); } /* * udc_startup_events - allow udc code to do any additional startup */ void udc_startup_events (struct usb_device_instance *device) { dbgENTER(dbgflg_usbdbi_init, 1); udc_configuration_enable(device); usbd_device_event(device, DEVICE_INIT, 0); usbd_device_event(device, DEVICE_CREATE, 0); usbd_device_event(device, DEVICE_HUB_CONFIGURED, 0); usbd_device_event(device, DEVICE_RESET, 0); } /* Proc Filesystem */ #ifdef WMMX_TRACE #ifdef CONFIG_USBD_PROCFS /* * wmmx_proc_read - implement proc file system read. * @file * @buf * @count * @pos * * Standard proc file system read function. */ static ssize_t wmmx_proc_read(struct file *file, char *buf, size_t count, loff_t * pos) { unsigned long page; int len = 0; int index; int i; MOD_INC_USE_COUNT; /* get a page, max 4095 bytes of data... */ if (!(page = get_free_page (GFP_KERNEL))) { MOD_DEC_USE_COUNT; return -ENOMEM; } len = 0; index = (*pos)++; if (index == 0) { len += sprintf ( (char *) page + len, " Index Ints Jifs Ticks\n"); } if (index < trace_next) { u64 jifs = 1111; u32 ticks = 2222; wmmx_trace_t *p = wmmx_traces + index; unsigned char *cp; if (index > 0) { u32 ocsr = wmmx_traces[index-1].ocsr; ticks = (p->ocsr > ocsr) ? (p->ocsr - ocsr) : (ocsr - p->ocsr); jifs = p->jiffies - wmmx_traces[index-1].jiffies; } len += sprintf((char *)page + len, "%8d %8d %8llu ", index, p->interrupts, jifs); if (ticks > 1024 * 1024) { len += sprintf((char *)page + len, "%8dM ", ticks>>20); } else { len += sprintf((char *)page + len, "%8d ", ticks); } switch (p->trace_type) { case wmmx_regs: len += sprintf((char *) page + len, "CS0[%02x] %s\n", p->trace.regs.cs0, p->trace.regs.msg); break; case wmmx_setup: cp = (unsigned char *)&p->trace.setup; len += sprintf((char *)page + len, " -- request " "[%02x %02x %02x %02x %02x %02x %02x " "%02x]\n", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]); break; case wmmx_xmit: len += sprintf((char *) page + len, " -- sending [%02x]\n", p->trace.xmit.size); break; case wmmx_ccr: len += sprintf((char *) page + len, "CCR[%02x] %s\n", p->trace.ccr.ccr, p->trace.ccr.msg); break; case wmmx_iro: len += sprintf((char *) page + len, "IRO[%02x] %s\n", p->trace.iro.iro, p->trace.iro.msg); break; case wmmx_xfer: cp = (unsigned char *)&p->trace.xfer.data; len += sprintf((char *)page + len, "%s xfer of %d bytes:", (p->trace.xfer.dir == WMMX_XFER_DIR_OUT) ? "OUT" : "IN", p->trace.xfer.size); for (i = 0; i < MIN(p->trace.xfer.size, WMMX_XFER_MAX_TRACE_SIZE); i++) { len += sprintf((char *)page + len, " %02x", (p->trace.xfer.data[i]) & 0xff); } len += sprintf((char *)page + len, "\n"); break; } } if (len > count) { len = -EINVAL; } else if (len > 0 && copy_to_user (buf, (char *) page, len)) { len = -EFAULT; } free_page (page); MOD_DEC_USE_COUNT; return len; } /* * wmmx_proc_write - implement proc file system write. * @file * @buf * @count * @pos * * Proc file system write function, used to signal monitor actions complete. * (Hotplug script (or whatever) writes to the file to signal the completion * of the script.) An ugly hack. */ static ssize_t wmmx_proc_write(struct file *file, const char *buf, size_t count, loff_t * pos) { return count; } static struct file_operations wmmx_proc_operations_functions = { read:wmmx_proc_read, write:wmmx_proc_write, }; #endif #endif /* * udc_name - return name of USB Device Controller */ char *udc_name (void) { return UDC_NAME; } /* * udc_request_udc_irq - request UDC interrupt * * Return non-zero if not successful. */ int udc_request_udc_irq () { dbgENTER(dbgflg_usbdbi_init, 1); if (request_irq(IRQ_USB, wmmx_int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME " USBD Bus Interface", NULL) != 0) { printk (KERN_INFO "usb_ctl: Couldn't request USB irq\n"); return -EINVAL; } return 0; } /* * udc_request_cable_irq - request Cable interrupt * * Return non-zero if not successful. */ int udc_request_cable_irq() { return 0; } /* * udc_request_udc_io - request UDC io region * * Return non-zero if not successful. */ int udc_request_io () { #ifdef WMMX_TRACE #ifdef CONFIG_USBD_PROCFS if (!(wmmx_traces = vmalloc(sizeof(wmmx_trace_t) * TRACE_MAX))) { printk(KERN_ERR "WMMX_TRACE malloc failed %p %d\n", wmmx_traces, sizeof(wmmx_trace_t) * TRACE_MAX); } else { printk(KERN_ERR "WMMX_TRACE malloc ok %p\n", wmmx_traces); } WMMX_REGS(0,"init"); WMMX_REGS(0,"test"); { struct proc_dir_entry *p; if ((p = create_proc_entry("wmmx", 0, 0)) == NULL) { printk(KERN_INFO"WMMX PROC FS failed\n"); } else { printk(KERN_INFO"WMMX PROC FS Created\n"); p->proc_fops = &wmmx_proc_operations_functions; } } #endif #endif return 0; } /* * udc_release_release_io - release UDC io region */ void udc_release_io () { #ifdef WMMX_TRACE #ifdef CONFIG_USBD_PROCFS unsigned long flags; dbgENTER(dbgflg_usbdbi_init, 1); save_flags_cli (flags); remove_proc_entry ("wmmx", NULL); if (wmmx_traces) { wmmx_trace_t *p = wmmx_traces; wmmx_traces = 0; vfree(p); dbg_init(1, "trace memory is released"); } restore_flags (flags); #endif #endif } /* * udc_release_udc_irq - release UDC irq */ void udc_release_udc_irq () { dbgENTER(dbgflg_usbdbi_init, 1); free_irq (IRQ_USB, NULL); } /* * udc_release_cable_irq - release Cable irq */ void udc_release_cable_irq () { } /* * udc_regs - dump registers * * Dump registers with printk */ void udc_regs (void) { printk ("[%d:%d] CCR[%08x] UDUICR[%08x %08x] UFN[%08x] UDCCS[%08x]\n", udc_interrupts, jifs(), UDCCR, UDCICR0, UDCICR1, UDCFNR, UDCCSR0); } void wmmx_print(void) { printk("%08x %08x\n", UDCCSN(1), UDCCSN(2)); }