www.pudn.com > Linux2410_device.rar > pxa.c
/* * linux/drivers/usb/device/bi/pxa.c -- Intel PXA250 USB Device Controller driver. * * Copyright (c) 2000, 2001, 2002 Lineo * Copyright (c) 2002, 2003 RTSoft * * By: * Stuart Lynne, * Tom Rushworth , * * Minor fixes and basic DMA support by: * Dmitry Antipov * * 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. * */ /* * Testing notes: * * This code was developed on the Intel Lubbock with Cotulla PXA250 processor. * Note DMA OUT seems works starting from C0 chip revision only. */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../usbd-export.h" #include "../usbd-build.h" #include "../usbd-module.h" #include "../usbd.h" #include "../usbd-func.h" #include "../usbd-bus.h" #include "../usbd-inline.h" #include "usbd-bi.h" #include "pxa.h" MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com, antipov@rtsoft.msk.ru"); MODULE_DESCRIPTION ("Xscale USB Device Bus Interface"); MODULE_LICENSE ("GPL"); USBD_MODULE_INFO ("pxa_bi 0.1-alpha"); #ifdef CONFIG_USBD_PXA_DMA_IN #if defined(CONFIG_USBD_SERIAL_IN_PKTSIZE) #define DMA_IN_BUFSIZE CONFIG_USBD_SERIAL_IN_PKTSIZE #elif defined(CONFIG_USBD_NET_IN_PKTSIZE) #define DMA_IN_BUFSIZE CONFIG_USBD_NET_IN_PKTSIZE #else #error "Can't determine buffer size for DMA IN" #endif #if defined(CONFIG_USBD_SERIAL_IN_ENDPOINT) #define IN_ENDPOINT CONFIG_USBD_SERIAL_IN_ENDPOINT #elif defined(CONFIG_USBD_NET_IN_ENDPOINT) #define IN_ENDPOINT CONFIG_USBD_NET_IN_ENDPOINT #else #error "Can't determine IN endpoint" #endif #endif /* CONFIG_USBD_PXA_DMA_IN */ #ifdef CONFIG_USBD_PXA_DMA_OUT #if defined(CONFIG_USBD_SERIAL_OUT_PKTSIZE) #define DMA_OUT_BUFSIZE CONFIG_USBD_SERIAL_OUT_PKTSIZE #elif defined(CONFIG_USBD_NET_OUT_PKTSIZE) #define DMA_OUT_BUFSIZE CONFIG_USBD_NET_OUT_PKTSIZE #else #error "Can't determine buffer size for DMA OUT" #endif #if defined(CONFIG_USBD_SERIAL_OUT_ENDPOINT) #define OUT_ENDPOINT CONFIG_USBD_SERIAL_OUT_ENDPOINT #elif defined(CONFIG_USBD_NET_OUT_ENDPOINT) #define OUT_ENDPOINT CONFIG_USBD_NET_IN_ENDPOINT #else #error "Can't determine OUT endpoint" #endif #endif /* CONFIG_USBD_PXA_DMA_OUT */ #if defined(CONFIG_USBD_PXA_DMA_IN) || defined(CONFIG_USBD_PXA_DMA_OUT) /* #define DMA_TRACEMSG(fmt, args...) printk (fmt, ## args) */ #define DMA_TRACEMSG(fmt, args...) /* This datatype contains all information required for DMA transfers */ typedef struct usb_pxa_dma { int dmach; struct usb_endpoint_instance *endpoint; int size; int ep; unsigned char *dma_buf; dma_addr_t dma_buf_phys; } usb_pxa_dma_t; #endif unsigned int int_oscr; #undef PXA_TRACE #ifdef PXA_TRACE typedef enum pxa_trace_type { pxa_regs, pxa_setup, pxa_xmit, pxa_ccr, pxa_iro } pxa_trace_type_t; typedef struct pxa_regs { u32 cs0; char * msg; } pxa_regs_t; typedef struct pxa_xmit { u32 size; } pxa_xmit_t; typedef struct pxa_ccr { u32 ccr; char * msg; } pxa_ccr_t; typedef struct pxa_iro { u32 iro; char * msg; } pxa_iro_t; typedef struct pxa_trace { pxa_trace_type_t trace_type; u32 interrupts; u32 ocsr; u64 jiffies; union { pxa_regs_t regs; pxa_xmit_t xmit; pxa_ccr_t ccr; pxa_iro_t iro; struct usb_device_request setup; } trace; } pxa_trace_t; #define TRACE_MAX 10000 int trace_next; pxa_trace_t *pxa_traces; static __inline__ void PXA_REGS(u32 cs0, char *msg) { if ((trace_next < TRACE_MAX) && pxa_traces) { pxa_trace_t *p = pxa_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = pxa_regs; p->trace.regs.cs0 = cs0; p->trace.regs.msg = msg; } } static __inline__ void PXA_SETUP(struct usb_device_request *setup) { if ((trace_next < TRACE_MAX) && pxa_traces) { pxa_trace_t *p = pxa_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = pxa_setup; memcpy(&p->trace.setup, setup, sizeof(struct usb_device_request)); } } static __inline__ void PXA_XMIT(u32 size) { if ((trace_next < TRACE_MAX) && pxa_traces) { pxa_trace_t *p = pxa_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = pxa_xmit; p->trace.xmit.size = size; } } static __inline__ void PXA_CCR(u32 ccr, char *msg) { if ((trace_next < TRACE_MAX) && pxa_traces) { pxa_trace_t *p = pxa_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = pxa_ccr; p->trace.ccr.ccr = ccr; p->trace.ccr.msg = msg; } } static __inline__ void PXA_IRO(u32 iro, char *msg) { if ((trace_next < TRACE_MAX) && pxa_traces) { pxa_trace_t *p = pxa_traces + trace_next++; p->ocsr = OSCR; p->jiffies = jiffies; p->interrupts = udc_interrupts; p->trace_type = pxa_iro; p->trace.iro.iro = iro; p->trace.iro.msg = msg; } } #else static __inline__ void PXA_REGS(u32 cs0, char *msg) { } static __inline__ void PXA_SETUP(struct usb_device_request *setup) { } static __inline__ void PXA_XMIT(u32 size) { } static __inline__ void PXA_CCR(u32 ccr, char *msg) { } static __inline__ void PXA_IRO(u32 iro, char *msg) { } #endif static int udc_suspended; static struct usb_device_instance *udc_device; // required for the interrupt handler /* * ep_endpoints - map physical endpoints to logical endpoints */ static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS]; static struct urb *ep0_urb; extern unsigned int udc_interrupts; #if 0 int jifs(void) { static unsigned long jiffies_last; int elapsed = jiffies - jiffies_last; jiffies_last = jiffies; return elapsed; } #else int jifs(void) { static unsigned long jiffies_last; int elapsed = OSCR - jiffies_last; jiffies_last = OSCR; return elapsed; } #endif /* ********************************************************************************************* */ /* IO */ /* * Map logical to physical */ typedef enum ep { ep_control, ep_bulk_in, ep_bulk_out, ep_iso_in, ep_iso_out, ep_interrupt } ep_t; /* * PXA has lots of endpoints, but they have fixed address and type * so logical to physical map is limited to masking top bits so we * can find appropriate info. */ u32 _UDDRN[16] = { 0x40600080, 0x40600100, 0x40600180, 0x40600200, 0x40600400, 0x406000A0, 0x40600600, 0x40600680, 0x40600700, 0x40600900, 0x406000C0, 0x40600B00, 0x40600B80, 0x40600C00, 0x40600E00, 0x406000E0, }; u32 _UBCRN[16] = { 0, 0, 0x40600068, 0, 0x4060006c, 0, 0, 0x40600070, 0, 0x40600074, 0, 0, 0x40600078, 0, 0x4060007c, 0, }; #define UDCCSN(x) __REG2(0x40600010, (x) << 2) #define UDDRN(x) __REG(_UDDRN[x]) #define UBCRN(x) __REG(_UBCRN[x]) struct ep_map { int logical; ep_t eptype; int size; int dma_chan; }; static struct ep_map ep_maps[16] = { { logical: 0, eptype: ep_control, size: 16, }, { logical: 1, eptype: ep_bulk_in, size: 64, }, { logical: 2, eptype: ep_bulk_out, size: 64, }, { logical: 3, eptype: ep_iso_in, size: 256, }, { logical: 4, eptype: ep_iso_out, size: 256, }, { logical: 5, eptype: ep_interrupt , size: 8, }, { logical: 6, eptype: ep_bulk_in, size: 64, }, { logical: 7, eptype: ep_bulk_out, size: 64, }, { logical: 8, eptype: ep_iso_in, size: 256, }, { logical: 9, eptype: ep_iso_out, size: 256, }, { logical: 10, eptype: ep_interrupt, size: 8, }, { logical: 11, eptype: ep_bulk_in, size: 64, }, { logical: 12, eptype: ep_bulk_out, size: 64, }, { logical: 13, eptype: ep_iso_in, size: 256, }, { logical: 14, eptype: ep_iso_out, size: 256, }, { logical: 15, eptype: ep_interrupt, size: 8, }, }; static __inline__ void pxa_enable_ep_interrupt(int ep) { ep &= 0xf; #ifdef CONFIG_USBD_PXA_DMA_OUT if (ep == CONFIG_USBD_NET_OUT_ENDPOINT) { DMA_TRACEMSG ("PXA: skip enabling EP%d due to DMA\n", CONFIG_USBD_NET_OUT_ENDPOINT); return; } #endif if (ep < 8) { UICR0 &= ~(1< > 8) ^ crc32_table[((fcs) ^ (c)) & 0xff]) /* fcs_compute32 - memcpy and calculate fcs */ static __u32 __inline__ fcs_compute32(unsigned char *sp, int len, __u32 fcs) { for (;len-- > 0; fcs = CRC32_FCS(fcs, *sp++)); return fcs; } static usb_pxa_dma_t usb_pxa_dma_out = {-1, NULL, 0, 0, NULL, 0}; /* Do some h/w setup for DMA OUT */ static inline void pxa_rx_dma_hw_init (void) { DCSR(usb_pxa_dma_out.dmach) |= DCSR_NODESC; DCMD(usb_pxa_dma_out.dmach) = DCMD_FLOWSRC | DCMD_INCTRGADDR | DCMD_WIDTH1 | DCMD_BURST32 | DCMD_ENDIRQEN | DMA_OUT_BUFSIZE; DSADR(usb_pxa_dma_out.dmach) = _UDDRN[usb_pxa_dma_out.ep]; DTADR(usb_pxa_dma_out.dmach) = usb_pxa_dma_out.dma_buf_phys; DRCMR26 = usb_pxa_dma_out.dmach | DRCMR_MAPVLD; } static void pxa_rx_dma_irq (int dmach, void *dev_id, struct pt_regs *regs) { int dcsr = DCSR(usb_pxa_dma_out.dmach); DMA_TRACEMSG ("PXA: out dma IRQ [%d %d], DCSR 0x%x\n", UBCRN(usb_pxa_dma_out.ep), ep_endpoints[usb_pxa_dma_out.ep]->rcv_packetSize, dcsr); /* Stop DMA */ DCSR(usb_pxa_dma_out.dmach) &= ~DCSR_RUN; /* TODO: busy-wait until DMA is really stopped ? */ if (dcsr & DCSR_BUSERR) /* Is there a way to handle this error somehow ? */ printk (KERN_WARNING "PXA: Bus error in USB RX DMA " "- USB data transfer may be incorrect\n"); else if (dcsr & DCSR_ENDINTR) { DMA_TRACEMSG ("PXA: normal IRQ\n"); if (!usb_pxa_dma_out.endpoint->rcv_urb) { usb_pxa_dma_out.endpoint->rcv_urb = first_urb_detached (&usb_pxa_dma_out.endpoint->rdy); } if (usb_pxa_dma_out.endpoint->rcv_urb) { int recvsize; unsigned char *cp = usb_pxa_dma_out.endpoint->rcv_urb->buffer + usb_pxa_dma_out.endpoint->rcv_urb->actual_length; /* Copy all new data to current urb buffer */ memcpy (cp, usb_pxa_dma_out.dma_buf, DMA_OUT_BUFSIZE); if (usb_pxa_dma_out.dma_buf[DMA_OUT_BUFSIZE - 1] == 0xff) { /* May be end-of-urb data - must calculate FCS and compare with FCS calculated on the host side */ __u32 oldfcs, newfcs; /* See usbdnet.c for explanation of this */ oldfcs = (usb_pxa_dma_out.dma_buf[DMA_OUT_BUFSIZE - 2] << 24) | (usb_pxa_dma_out.dma_buf[DMA_OUT_BUFSIZE - 3] << 16) | (usb_pxa_dma_out.dma_buf[DMA_OUT_BUFSIZE - 4] << 8) | (usb_pxa_dma_out.dma_buf[DMA_OUT_BUFSIZE - 5]); newfcs = fcs_compute32 (usb_pxa_dma_out.endpoint->rcv_urb->buffer, usb_pxa_dma_out.endpoint->rcv_urb->actual_length + DMA_OUT_BUFSIZE - 5, CRC32_INITFCS); newfcs = ~newfcs; /* If FCSs are equal, the last byte of new data is 0xff mark and will be ignored */ recvsize = (oldfcs == newfcs) ? (DMA_OUT_BUFSIZE - 1) : DMA_OUT_BUFSIZE; } else recvsize = DMA_OUT_BUFSIZE; DMA_TRACEMSG ("PXA: urb exists, real size %d\n", recvsize); usbd_rcv_complete_irq (usb_pxa_dma_out.endpoint, recvsize, 0); } } /* clear RPC and interrupt */ UDCCSN(usb_pxa_dma_out.ep) |= UDCCS_BO_RPC; pxa_ep_reset_irs(usb_pxa_dma_out.ep); /* Clear end-of-interrupt */ DCSR(usb_pxa_dma_out.dmach) |= DCSR_ENDINTR; /* Reinitialize and restart DMA */ pxa_rx_dma_hw_init (); DCSR(usb_pxa_dma_out.dmach) |= DCSR_RUN; } #endif /* CONFIG_USBD_PXA_DMA_OUT */ static __inline__ void pxa_out_n(int ep, struct usb_endpoint_instance *endpoint) { if (UDCCSN(ep) & UDCCS_BO_RPC) { if (endpoint) { if (!endpoint->rcv_urb) { endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); } if (endpoint->rcv_urb) { int len = 0; unsigned char *cp = endpoint->rcv_urb->buffer + endpoint->rcv_urb->actual_length; if (cp) { // read available bytes (max packetsize) into urb buffer int count = MIN(UBCRN(ep) + 1, endpoint->rcv_packetSize); while (count--) { len++; *cp++ = UDDRN(ep); } } else { printk(KERN_INFO"read[%d:%d] bad arguements\n", udc_interrupts, jifs()); pxa_out_flush(ep); } // fall through if error of any type, len = 0 usbd_rcv_complete_irq (endpoint, len, 0); } else { pxa_out_flush(ep); } } } // clear RPC and interrupt UDCCSN(ep) = UDCCS_BO_RPC; pxa_ep_reset_irs(ep); } /* ********************************************************************************************* */ /* Bulk IN (tx) */ #ifdef CONFIG_USBD_PXA_DMA_IN static usb_pxa_dma_t usb_pxa_dma_in = {-1, NULL, 0, 0, NULL, 0}; static void pxa_tx_dma_irq (int dmach, void *dev_id, struct pt_regs *regs) { int dcsr = DCSR(usb_pxa_dma_in.dmach); /* Stop DMA */ DCSR(usb_pxa_dma_in.dmach) &= ~DCSR_RUN; DMA_TRACEMSG ("PXA: in dma IRQ, DCSR 0x%x\n", dcsr); if (dcsr & DCSR_BUSERR) /* I don't know better way to handle DMA bus error... */ printk (KERN_WARNING "PXA: Bus error in USB TX DMA " "- USB data transfer may be incorrect\n"); else if (dcsr & DCSR_ENDINTR) { /* All seems ok, checking for we have short packet transmitted */ if ((usb_pxa_dma_in.size < usb_pxa_dma_in.endpoint->tx_packetSize) || ((usb_pxa_dma_in.endpoint->tx_urb->actual_length - usb_pxa_dma_in.endpoint->sent ) == usb_pxa_dma_in.size)) { UDCCSN(usb_pxa_dma_in.ep) |= UDCCS_BI_TSP; } usb_pxa_dma_in.endpoint->last += usb_pxa_dma_in.size; } /* Clear end-of-interrupt */ DCSR(usb_pxa_dma_in.dmach) |= DCSR_ENDINTR; /* Now we can re-enable irq */ pxa_enable_ep_interrupt (usb_pxa_dma_in.ep); } static void __inline__ pxa_start_n_dma_hw (int ep, struct usb_endpoint_instance *endpoint, char *mem, int size) { /* Prepare for DMA */ memcpy (usb_pxa_dma_in.dma_buf, mem, size); usb_pxa_dma_in.ep = ep; usb_pxa_dma_in.endpoint = endpoint; usb_pxa_dma_in.size = size; /* Setup DMA */ DCSR(usb_pxa_dma_in.dmach) |= DCSR_NODESC; DCMD(usb_pxa_dma_in.dmach) = DCMD_FLOWTRG | DCMD_INCSRCADDR | DCMD_WIDTH1 | DCMD_BURST32 | DCMD_ENDIRQEN | size; DSADR(usb_pxa_dma_in.dmach) = usb_pxa_dma_in.dma_buf_phys; DTADR(usb_pxa_dma_in.dmach) = _UDDRN[ep]; /* Map DMA (FIXME: assume ep is 1) */ DRCMR25 = usb_pxa_dma_in.dmach | DRCMR_MAPVLD; /* Start DMA */ DCSR(usb_pxa_dma_in.dmach) |= DCSR_RUN; } static void pxa_start_n_dma (unsigned int ep, struct usb_endpoint_instance *endpoint) { /* We must disable irq for this endpoint */ pxa_disable_ep_interrupt (ep); if (endpoint->tx_urb) { struct urb *urb = endpoint->tx_urb; int last = MIN(urb->actual_length - (endpoint->sent + endpoint->last), endpoint->tx_packetSize); if (last) { pxa_start_n_dma_hw (ep, endpoint, urb->buffer + endpoint->sent + endpoint->last, last); return; } } /* Do nothing with DMA - re-enable irq */ pxa_enable_ep_interrupt (ep); } #endif /* CONFIG_USBD_PXA_DMA_IN */ static void __inline__ pxa_start_n (unsigned int 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; unsigned char *cp = urb->buffer + endpoint->sent + endpoint->last; while (size--) { UDDRN(ep) = *cp++; } if (( last < endpoint->tx_packetSize ) || ( (endpoint->tx_urb->actual_length - endpoint->sent ) == last )) { UDCCSN(ep) = UDCCS_BI_TSP; } endpoint->last += last; } } } static void __inline__ pxa_in_n (unsigned int ep, struct usb_endpoint_instance *endpoint) { int udccsn; pxa_ep_reset_irs(ep); // if TPC update tx urb and clear TPC if ((udccsn = UDCCSN(ep)) & UDCCS_BI_TPC) { UDCCSN(ep) = UDCCS_BI_TPC; usbd_tx_complete_irq(endpoint, 0); } if (udccsn & UDCCS_BI_TFS) { pxa_start_n(ep, endpoint); } // clear underrun, not much we can do about it if (udccsn & UDCCS_BI_TUR) { UDCCSN(ep) = UDCCS_BI_TUR; } } /* ********************************************************************************************* */ /* Control (endpoint zero) */ #ifdef CONFIG_USBD_EP0_SUPPORT /* Changed by Stanley */ void pxa_ep0recv(struct usb_endpoint_instance *endpoint, volatile u32 udccs0) { int size=0; struct urb *urb = endpoint->rcv_urb; unsigned char* cp = (unsigned char*) (urb->buffer + urb->actual_length); PXA_REGS(udccs0, " <-- setup data recv"); #ifdef EP0_DEBUG printk("%d %x %x\n", urb->actual_length, urb->buffer, cp ); printk("udccs0=0x%x\n", udccs0 ); #endif // check for premature status stage if ( !(udccs0&UDCCS0_OPR) && !(udccs0&UDCCS0_IPR) ) { if ( urb->device_request.wLength == urb->actual_length ) { #ifdef EP0_DEBUG printk(KERN_INFO"pxa_ep0recv: UDC ep0 receive all data.\n"); #endif usbd_recv_setup(ep0_urb); } else { #ifdef EP0_DEBUG printk(KERN_INFO"pxa_ep0recv: Get a premature status in stage.\n"); printk(KERN_INFO"pxa_ep0recv: request.wLength=0x%x, actual_length=0x%x\n", urb->device_request.wLength, urb->actual_length ); #endif if ( urb->buffer ) kfree( urb->buffer ); } endpoint->state = WAIT_FOR_SETUP; } // receive more data if (( udccs0 & UDCCS0_OPR ) && !(UDCCS0 & UDCCS0_SA) ) { while ( UDCCS0&UDCCS0_RNE ) { *cp++ = UDDRN(0); urb->actual_length++; } /* Do we need this section? */ /* need more tests! -Stanley */ /* Without the section, the driver still works */ #if 1 if ( urb->actual_length == urb->device_request.wLength ) { #ifdef EP0_DEBUG printk(KERN_INFO"pxa_ep0recv under test: UDC ep0 receive all data.\n"); #endif usbd_recv_setup(ep0_urb); endpoint->state = WAIT_FOR_SETUP; } #endif UDCCS0 |= UDCCS0_OPR; // to allow to enter a premature STATUS IN stage UDCCS0 |= UDCCS0_IPR; } return; } #endif void pxa_ep0xmit(struct usb_endpoint_instance *endpoint, volatile u32 udccs0) { int short_packet; int size; struct urb *urb = endpoint->tx_urb; PXA_REGS(udccs0, " --> xmit"); //printk(KERN_INFO"tx[%d:%d] CS0[%02x]\n", udc_interrupts, jifs(), UDCCS0); // check for premature status stage - host abandoned previous IN if ((udccs0 & UDCCS0_OPR) && !(UDCCS0 & UDCCS0_SA) ) { // clear tx fifo and opr UDCCS0 = UDCCS0_FTF | UDCCS0_OPR; endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; PXA_REGS(UDCCS0, " <-- xmit premature status"); return; } // check for stall if (udccs0 & UDCCS0_SST) { // clear stall and tx fifo UDCCS0 = UDCCS0_SST | UDCCS0_FTF; endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; PXA_REGS(UDCCS0, " <-- xmit stall"); return; } /* How much are we sending this time? (May be zero!) (Note that later call of tx_complete() will add last to sent.) */ if (NULL == urb) { size = 0; } else { endpoint->last = size = MIN (urb->actual_length - endpoint->sent, endpoint->tx_packetSize); } /* Will this be a short packet? (It may be the last, but still be full size, in which case we will need a ZLP later.) */ short_packet = (size < endpoint->tx_packetSize); PXA_XMIT(size); if (size > 0 && urb->buffer) { // Stuff the FIFO unsigned char *cp = urb->buffer + endpoint->sent; while (size--) { UDDRN(0) = *cp++; } } // Is this the end of the data state? (We've sent all the data, plus any required ZLP.) if (!endpoint->tx_urb || (endpoint->last < endpoint->tx_packetSize)) { // Tell the UDC we are at the end of the packet. UDCCS0 = UDCCS0_IPR; endpoint->state = WAIT_FOR_OUT_STATUS; PXA_REGS(UDCCS0, " <-- xmit wait for status"); } else if ((endpoint->last == endpoint->tx_packetSize) && ((endpoint->last + endpoint->sent) == ep0_urb->actual_length) && ((ep0_urb->actual_length) < le16_to_cpu(ep0_urb->device_request.wLength)) ) { // Tell the UDC we are at the end of the packet. endpoint->state = DATA_STATE_NEED_ZLP; PXA_REGS(UDCCS0, " <-- xmit need zlp"); } else { PXA_REGS(UDCCS0, " <-- xmit not finished"); } } void __inline__ pxa_ep0setup(struct usb_endpoint_instance *endpoint, volatile u32 udccs0) { if ((udccs0 & (UDCCS0_SA | UDCCS0_OPR | UDCCS0_RNE)) == (UDCCS0_SA | UDCCS0_OPR | UDCCS0_RNE)) { int len = 0; int max = 8; unsigned char *cp = (unsigned char *)&ep0_urb->device_request; PXA_REGS(udccs0, " --> setup"); //memset(cp, 0, max); while (max-- /*&& (UDCCS0 & UDCCS0_RNE)*/) { len++; *cp++ = UDDR0; } PXA_SETUP(&ep0_urb->device_request); #ifdef CONFIG_USBD_EP0_SUPPORT /* This section is added to give some supports for setup data IN mode. It is necessary * to call usbd_recv_setup * routine at end of receiving all data * - Stanley */ #ifdef EP0_DEBUG printk( KERN_INFO"URB request:\n" ); printk( KERN_INFO"ep0.bmRequestType: 0x%x\n", ep0_urb->device_request.bmRequestType ); printk( KERN_INFO"ep0.bRequest: 0x%x\n", ep0_urb->device_request.bRequest ); printk( KERN_INFO"ep0.wlength: 0x%x\n", ep0_urb->device_request.wLength ); #endif // Controll Write - Maybe we will receive more data from the host if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE && le16_to_cpu(ep0_urb->device_request.wLength)!=0 ) { #ifdef EP0_DEBUG printk("HOST2DEVICE\n"); #endif endpoint->state = DATA_STATE_RECV; // allocate new memory for such a request; ep0_urb->buffer = kmalloc( ep0_urb->device_request.wLength, GFP_ATOMIC ); if ( !ep0_urb->buffer ) { panic("pxa_ep0setup: Out of memory!\n"); return; } ep0_urb->buffer_length = ep0_urb->device_request.wLength; ep0_urb->actual_length = 0; // the bugs endpoint->rcv_urb = ep0_urb; UDCCS0 = UDCCS0_SA | UDCCS0_OPR; PXA_REGS(UDCCS0," <-- setup recv"); return; } #endif // process setup packet if (usbd_recv_setup(ep0_urb)) { // setup processing failed UDCCS0 = UDCCS0_FST; endpoint->state = WAIT_FOR_SETUP; PXA_REGS(udccs0, " --> bad setup FST"); return; } // check direction if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { // Control Write - we are receiving more data from the host #ifdef EP0_DEBUG printk("HOST2DEVICE - zero length\n"); #endif // should we setup to receive data // Skip this condition; - Stanley if (le16_to_cpu (ep0_urb->device_request.wLength)) { //printk(KERN_INFO"sl11_ep0: read data %d\n", // le16_to_cpu(ep0_urb->device_request.wLength)); endpoint->rcv_urb = ep0_urb; endpoint->rcv_urb->actual_length = 0; endpoint->state = DATA_STATE_RECV; // XXX this has not been tested // pxa_out_0 (0, endpoint); PXA_REGS(UDCCS0," <-- setup no response"); return; } // allow for premature IN (c.f. 12.5.3 #8) UDCCS0 = UDCCS0_IPR; PXA_REGS(UDCCS0," <-- setup nodata"); } else { // Control Read - we are sending data to the host #ifdef EP0_DEBUG printk("DEVICE2HOST\n"); #endif // verify that we have non-zero request length if (!le16_to_cpu (ep0_urb->device_request.wLength)) { udc_stall_ep (0); PXA_REGS(UDCCS0," <-- setup stalling zero wLength"); return; } // verify that we have non-zero length response if (!ep0_urb->actual_length) { udc_stall_ep (0); PXA_REGS(UDCCS0," <-- setup stalling zero response"); return; } // start sending endpoint->tx_urb = ep0_urb; endpoint->sent = 0; endpoint->last = 0; endpoint->state = DATA_STATE_XMIT; pxa_ep0xmit(endpoint, UDCCS0); // Clear SA and OPR bits UDCCS0 = UDCCS0_SA | UDCCS0_OPR; PXA_REGS(UDCCS0," <-- setup data"); } } } static __inline__ void pxa_ep0(struct usb_endpoint_instance *endpoint, volatile u32 udccs0) { int j = 0; PXA_REGS(udccs0," --> ep0"); if (udccs0 & UDCCS0_SST) { pxa_ep_reset_irs(0); UDCCS0 = UDCCS0_SST; PXA_REGS(udccs0," --> ep0 clear SST"); if (endpoint) { endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; } return; } if (!endpoint) { printk(KERN_INFO"ep0[%d:%d] endpoint Zero is NULL CS0[%02x]\n", udc_interrupts, jifs(), UDCCS0); pxa_ep_reset_irs(0); UDCCS0 = UDCCS0_IPR | UDCCS0_OPR | UDCCS0_SA; PXA_REGS(UDCCS0," 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 != WAIT_FOR_SETUP) && (udccs0 & UDCCS0_SA)) { PXA_REGS(udccs0," --> ep0 early SA"); endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; } switch (endpoint->state) { case DATA_STATE_NEED_ZLP: UDCCS0 = UDCCS0_IPR; endpoint->state = WAIT_FOR_OUT_STATUS; break; case WAIT_FOR_OUT_STATUS: if ((udccs0 & (UDCCS0_OPR | UDCCS0_SA)) == UDCCS0_OPR) { UDCCS0 |= UDCCS0_OPR; } PXA_REGS(UDCCS0," --> ep0 WAIT for STATUS"); endpoint->state = WAIT_FOR_SETUP; case WAIT_FOR_SETUP: do { pxa_ep0setup(endpoint, UDCCS0); if (udccs0 & UDCCS0_SST) { pxa_ep_reset_irs(0); UDCCS0 = UDCCS0_SST | UDCCS0_OPR | UDCCS0_SA; PXA_REGS(udccs0," --> ep0 clear SST"); if (endpoint) { endpoint->state = WAIT_FOR_SETUP; endpoint->tx_urb = NULL; } return; } if (j++ > 2) { u32 udccs0 = UDCCS0; PXA_REGS(udccs0," ep0 wait"); if ((udccs0 & (UDCCS0_OPR | UDCCS0_SA | UDCCS0_RNE)) == (UDCCS0_OPR | UDCCS0_SA)) { UDCCS0 = UDCCS0_OPR | UDCCS0_SA; PXA_REGS(UDCCS0," ep0 force"); } else { UDCCS0 = UDCCS0_OPR | UDCCS0_SA; PXA_REGS(UDCCS0," ep0 force and return"); break; } } } while (UDCCS0 & (UDCCS0_OPR | UDCCS0_RNE)); break; case DATA_STATE_XMIT: pxa_ep0xmit(endpoint, UDCCS0); break; #ifdef CONFIG_USBD_EP0_SUPPORT case DATA_STATE_RECV: pxa_ep0recv(endpoint, UDCCS0); break; #endif } pxa_ep_reset_irs(0); PXA_REGS(UDCCS0," <-- ep0"); } /* ********************************************************************************************* */ /* Interrupt Handler */ /** * int_hndlr - interrupt handler * */ static void int_hndlr (int irq, void *dev_id, struct pt_regs *regs) { int usiro; int udccr; int ep; int_oscr = OSCR; udc_interrupts++; // check for common, high priority interrupts first, i.e. per endpoint service requests // XXX if ISO supported it might be necessary to give the ISO endpoints priority while ((usiro = USIR0)) { u32 udccs0 = UDCCS0; PXA_IRO(usiro, "------------------------> Interrupt"); for (ep = 0; usiro; usiro >>= 1, ep++) { if (usiro & 1) { switch (ep_maps[ep].eptype) { case ep_control: pxa_ep0(ep_endpoints[0], udccs0); //PXA_IRO(USIRO, "<-- Interrupt"); break; case ep_bulk_in: case ep_interrupt: pxa_in_n(ep, ep_endpoints[ep]); break; case ep_bulk_out: #ifdef CONFIG_USBD_PXA_DMA_OUT printk (KERN_WARNING "PXA: bulk out with DMA out, ep %d - may be a bug\n", ep); #else pxa_out_n(ep, ep_endpoints[ep]); #endif pxa_ep_reset_irs(ep); break; case ep_iso_in: case ep_iso_out: pxa_ep_reset_irs(ep); break; } } } } // sof interrupt if (UFNHR & UFNHR_SIR) { UFNHR = UFNHR_SIR; } // uncommon interrupts if ((udccr = UDCCR) & (UDCCR_RSTIR | UDCCR_RESIR | UDCCR_SUSIR)) { // UDC Reset if (udccr & UDCCR_RSTIR) { PXA_CCR(udccr, "------------------------> Reset"); //printk(KERN_INFO"int_hndlr[%d:%d] Reset\n", udc_interrupts, jifs()); udc_suspended = 0; usbd_device_event (udc_device, DEVICE_RESET, 0); usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); UDCCR |= UDCCR_RSTIR; } // UDC Resume if (udccr & UDCCR_RESIR) { PXA_CCR(udccr, "------------------------> Resume"); if (udc_suspended) { udc_suspended = 0; usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0); } UDCCR |= UDCCR_RESIR; } // UDC Suspend if (udccr & UDCCR_SUSIR) { PXA_CCR(udccr, "------------------------> Suspend"); if (!udc_suspended) { udc_suspended = 1; usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0); } UDCCR |= UDCCR_SUSIR; } } PXA_CCR(UDCCR, "<-- Interrupt"); } /* ********************************************************************************************* */ /* ********************************************************************************************* */ /* * Start of 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) { if (UDCCSN(endpoint->endpoint_address & 0xf) & UDCCS_BI_TFS) { #ifdef CONFIG_USBD_PXA_DMA_IN if (usb_pxa_dma_in.dma_buf) /* Buffer ok, use DMA */ pxa_start_n_dma (endpoint->endpoint_address & 0xf, endpoint); else /* Buffer is not allocated, switch to polling mode */ pxa_start_n (endpoint->endpoint_address & 0xf, endpoint); #else pxa_start_n (endpoint->endpoint_address & 0xf, endpoint); #endif } } /** * udc_init - initialize * * Return non-zero if we cannot see device. **/ int udc_init (void) { udc_disable_interrupts (NULL); #ifdef CONFIG_USBD_PXA_DMA_IN if (!(usb_pxa_dma_in.dma_buf = (unsigned char *)consistent_alloc (GFP_KERNEL, DMA_IN_BUFSIZE, &usb_pxa_dma_in.dma_buf_phys))) { /* Can't allocate DMAable memory... Make a warning and not use DMA */ printk (KERN_WARNING "PXA: can't allocate consistent memory for USB DMA IN\n"); printk (KERN_WARNING "PXA: USB DMA IN is disabled\n"); } #endif #ifdef CONFIG_USBD_PXA_DMA_OUT if (!(usb_pxa_dma_out.dma_buf = (unsigned char *)consistent_alloc (GFP_KERNEL, DMA_OUT_BUFSIZE, &usb_pxa_dma_out.dma_buf_phys))) { /* The same as above */ printk (KERN_WARNING "PXA: can't allocate consistent memory for USB DMA OUT\n"); printk (KERN_WARNING "PXA: USB DMA OUT is disabled\n"); } #endif 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 ep) { if (ep < UDC_MAX_ENDPOINTS) { // stall } } /** * udc_reset_ep - reset endpoint * @ep: physical endpoint * reset the endpoint. * * returns : 0 if ok, -1 otherwise */ void udc_reset_ep (unsigned int ep) { if (ep < UDC_MAX_ENDPOINTS) { } } /** * udc_endpoint_halted - is endpoint halted * @ep: * * Return non-zero if endpoint is halted */ int udc_endpoint_halted (unsigned int 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 logical_endpoint, int packetsize) { // XXX check ep table return ( ((logical_endpoint & 0xf) >= UDC_MAX_ENDPOINTS) || (packetsize > 64)) ? 0 : (logical_endpoint & 0xf); } /** * 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 ep, struct usb_endpoint_instance *endpoint) { if (ep < UDC_MAX_ENDPOINTS) { ep_endpoints[ep] = endpoint; // ep0 if (ep == 0) { } // IN else if (endpoint->endpoint_address & 0x80) { #ifdef CONFIG_USBD_PXA_DMA_IN if (ep == IN_ENDPOINT && usb_pxa_dma_in.dmach == -1 && /* No DMA IN if we can't allocate DMAable buffer in udc_init() */ usb_pxa_dma_in.dma_buf) { /* Get DMA irq */ if ((usb_pxa_dma_in.dmach = pxa_request_dma ("USB DMA IN", DMA_PRIO_HIGH, pxa_tx_dma_irq, (void *)&usb_pxa_dma_in)) < 0) { printk (KERN_WARNING "PXA: can't allocate DMA IRQ for USB DMA IN\n"); printk (KERN_WARNING "PXA: USB DMA IN is disabled\n"); consistent_free (usb_pxa_dma_in.dma_buf, DMA_IN_BUFSIZE, usb_pxa_dma_in.dma_buf_phys); usb_pxa_dma_in.dma_buf = NULL; } else DMA_TRACEMSG (KERN_DEBUG "PXA: USB IN DMA channel %d\n", usb_pxa_dma_in.dmach); } #endif /* CONFIG_USBD_PXA_DMA_IN */ } // OUT else if (endpoint->endpoint_address) { usbd_fill_rcv (device, endpoint, 5); endpoint->rcv_urb = first_urb_detached (&endpoint->rdy); #ifdef CONFIG_USBD_PXA_DMA_OUT if (ep == OUT_ENDPOINT && usb_pxa_dma_out.dmach == -1 && /* Again, no DMA OUT if we can't allocate DMAable buffer in udc_init() */ usb_pxa_dma_out.dma_buf) { /* Get DMA irq */ if ((usb_pxa_dma_out.dmach = pxa_request_dma ("USB DMA OUT", DMA_PRIO_HIGH, pxa_rx_dma_irq, (void *)&usb_pxa_dma_out)) < 0) { printk (KERN_WARNING "PXA: can't allocate DMA IRQ for USB DMA OUT\n"); printk (KERN_WARNING "PXA: USB DMA OUT is disabled\n"); consistent_free (usb_pxa_dma_out.dma_buf, DMA_OUT_BUFSIZE, usb_pxa_dma_out.dma_buf_phys); usb_pxa_dma_out.dma_buf = NULL; } else { DMA_TRACEMSG (KERN_DEBUG "PXA: USB OUT DMA channel %d\n", usb_pxa_dma_out.dmach); usb_pxa_dma_out.ep = ep; usb_pxa_dma_out.endpoint = endpoint; usb_pxa_dma_out.size = DMA_OUT_BUFSIZE; pxa_rx_dma_hw_init (); /* Enable DME for BULK OUT endpoint */ UDCCS2 |= UDCCS_BO_DME; DMA_TRACEMSG ("PXA: enable DME\n"); /* Start DMA */ DCSR(usb_pxa_dma_out.dmach) |= DCSR_RUN; DMA_TRACEMSG ("PXA: OUT DMA started [ep %d]\n", ep); } } #endif /* CONFIG_USBD_PXA_DMA_OUT */ } pxa_enable_ep_interrupt(ep_endpoints[ep]->endpoint_address); } } /** * udc_disable_ep - disable endpoint * @ep: * * Disable specified endpoint */ void udc_disable_ep (unsigned int ep) { if (ep < UDC_MAX_ENDPOINTS) { struct usb_endpoint_instance *endpoint; if ((endpoint = ep_endpoints[ep])) { ep_endpoints[ep] = NULL; usbd_flush_ep (endpoint); } if (ep == 0) { //printk(KERN_INFO"udc_setup_ep: 0 do nothing\n"); } // IN else if (endpoint && endpoint->endpoint_address & 0x80) { #ifdef CONFIG_USBD_PXA_DMA_IN if (ep == IN_ENDPOINT && usb_pxa_dma_in.dmach != -1) { pxa_free_dma (usb_pxa_dma_in.dmach); usb_pxa_dma_in.dmach = -1; } #endif } // OUT else if (endpoint && endpoint->endpoint_address) { #ifdef CONFIG_USBD_PXA_DMA_OUT if (ep == OUT_ENDPOINT && usb_pxa_dma_out.dmach != -1) { pxa_free_dma (usb_pxa_dma_out.dmach); usb_pxa_dma_out.dmach = -1; } #endif } } } /* ********************************************************************************************* */ /** * udc_connected - is the USB cable connected * * Return non-zeron if cable is connected. */ int udc_connected () { return 1; } /** * udc_connect - enable pullup resistor * * Turn on the USB connection by enabling the pullup resistor. */ void udc_connect (void) { #ifdef CONFIG_SABINAL_DISCOVERY /* Set GPIO pin function to I/O */ GPAFR1_U &= ~(0x3 << ((USBD_CONNECT_GPIO & 0xF) << 1)); /* Set pin direction to output */ GPDR(1) |= GPIO_GPIO(USBD_CONNECT_GPIO); #if defined(USBD_CONNECT_HIGH) /* Set GPIO pin high to connect */ GPSR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); #else /* Set GPIO pin low to connect */ GPCR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); #endif #else #warning NO USB Device connect #endif } /** * udc_disconnect - disable pullup resistor * * Turn off the USB connection by disabling the pullup resistor. */ void udc_disconnect (void) { #ifdef CONFIG_SABINAL_DISCOVERY /* Set GPIO pin function to I/O */ GPAFR1_U &= ~(0x3 << ((USBD_CONNECT_GPIO & 0xF) << 1)); /* Set pin direction to output */ GPDR(1) |= GPIO_GPIO(USBD_CONNECT_GPIO); #if defined(USBD_CONNECT_HIGH) /* Set GPIO pin low to disconnect */ GPCR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); #else /* Set GPIO pin high to disconnect */ GPSR(1) = GPIO_GPIO(USBD_CONNECT_GPIO); #endif #else #warning NO USB Device connect #endif } #if 0 /** * udc_int_hndlr_cable - interrupt handler for cable */ static void udc_int_hndlr_cable (int irq, void *dev_id, struct pt_regs *regs) { // GPIOn interrupt } #endif /* ********************************************************************************************* */ /** * udc_enable_interrupts - enable interrupts * * Switch on UDC interrupts. * */ void udc_all_interrupts (struct usb_device_instance *device) { int i; UDCCR &= ~UDCCR_SRM; // enable suspend interrupt UDCCR |= UDCCR_REM; // disable resume interrupt // XXX SOF UFNHR &= ~UFNHR_SIM; // always enable control endpoint pxa_enable_ep_interrupt(0); for (i = 1; i < UDC_MAX_ENDPOINTS; i++) { if (ep_endpoints[i] && ep_endpoints[i]->endpoint_address) { pxa_enable_ep_interrupt(ep_endpoints[i]->endpoint_address); } } } /** * udc_suspended_interrupts - enable suspended interrupts * * Switch on only UDC resume interrupt. * */ void udc_suspended_interrupts (struct usb_device_instance *device) { UDCCR &= ~UDCCR_REM; // enable resume interrupt UDCCR |= UDCCR_SRM; // disable suspend interrupt } /** * udc_disable_interrupts - disable interrupts. * * switch off interrupts */ void udc_disable_interrupts (struct usb_device_instance *device) { UICR0 = UICR1 = 0xff; // disable endpoint interrupts UFNHR |= UFNHR_SIM; // disable sof interrupt UDCCR |= UDCCR_REM | UDCCR_SRM; // disable suspend and resume interrupt } /* ********************************************************************************************* */ /** * 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) { // save the device structure pointer udc_device = device; // ep0 urb if (!ep0_urb) { if (!(ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) { printk (KERN_ERR "udc_enable: usbd_alloc_urb failed\n"); } } else { printk (KERN_ERR "udc_enable: ep0_urb already allocated\n"); } // enable UDC // c.f. 3.6.2 Clock Enable Register CKEN |= CKEN11_USB; // c.f. 12.4.11 GPIOn and GPIOx // enable cable interrupt // c.f. 12.5 UDC Operation - after reset on EP0 interrupt is enabled. // c.f. 12.6.1.1 UDC Enable UDCCR |= UDCCR_UDE | UDCCR_REM; } /** * udc_disable - disable the UDC * * Switch off the UDC */ void udc_disable (void) { printk ("PXA: call udc_disable\n"); // disable UDC // c.f. 12.6.1.1 UDC Enable UDCCR &= ~( UDCCR_UDE | UDCCR_REM ); // c.f. 3.6.2 Clock Enable Register CKEN &= ~CKEN11_USB; // disable cable interrupt // 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"); } #ifdef CONFIG_USBD_PXA_DMA_IN if (usb_pxa_dma_in.dma_buf) { consistent_free (usb_pxa_dma_in.dma_buf, DMA_IN_BUFSIZE, usb_pxa_dma_in.dma_buf_phys); usb_pxa_dma_in.dma_buf = NULL; } #endif #ifdef CONFIG_USBD_PXA_DMA_OUT if (usb_pxa_dma_out.dma_buf) { consistent_free (usb_pxa_dma_out.dma_buf, DMA_OUT_BUFSIZE, usb_pxa_dma_out.dma_buf_phys); usb_pxa_dma_out.dma_buf = NULL; } #endif } /** * udc_startup - allow udc code to do any additional startup */ void udc_startup_events (struct usb_device_instance *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); } /* ********************************************************************************************* */ #ifdef PXA_TRACE #ifdef CONFIG_USBD_PROCFS /* Proc Filesystem *************************************************************************** */ /* * * pxa_proc_read - implement proc file system read. * @file * @buf * @count * @pos * * Standard proc file system read function. */ static ssize_t pxa_proc_read (struct file *file, char *buf, size_t count, loff_t * pos) { unsigned long page; int len = 0; int index; 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; pxa_trace_t *p = pxa_traces + index; unsigned char *cp; if (index > 0) { u32 ocsr = pxa_traces[index-1].ocsr; ticks = (p->ocsr > ocsr) ? (p->ocsr - ocsr) : (ocsr - p->ocsr) ; jifs = p->jiffies - pxa_traces[index-1].jiffies; } len += sprintf ((char *) page + len, "%8d %8d %8lu ", 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 pxa_regs: len += sprintf ((char *) page + len, "CS0[%02x] %s\n", p->trace.regs.cs0, p->trace.regs.msg); break; case pxa_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 pxa_xmit: len += sprintf ((char *) page + len, " -- sending [%02x]\n", p->trace.xmit.size); break; case pxa_ccr: len += sprintf ((char *) page + len, "CCR[%02x] %s\n", p->trace.ccr.ccr, p->trace.ccr.msg); break; case pxa_iro: len += sprintf ((char *) page + len, "IRO[%02x] %s\n", p->trace.iro.iro, p->trace.iro.msg); 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; } /* * * pxa_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 pxa_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos) { return count; } static struct file_operations pxa_proc_operations_functions = { read:pxa_proc_read, write:pxa_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 () { // request IRQ and IO region if (request_irq (IRQ_USB, 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 () { #ifdef XXXX_CABLE_IRQ // request IRQ and IO region if (request_irq (XXXX_CABLE_IRQ, int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, UDC_NAME " Cable", NULL) != 0) { printk (KERN_INFO "usb_ctl: Couldn't request USB irq\n"); return -EINVAL; } #endif return 0; } /** * udc_request_udc_io - request UDC io region * * Return non-zero if not successful. */ int udc_request_io () { #ifdef PXA_TRACE #ifdef CONFIG_USBD_PROCFS if (!(pxa_traces = vmalloc(sizeof(pxa_trace_t) * TRACE_MAX))) { printk(KERN_ERR"PXA_TRACE malloc failed %p %d\n", pxa_traces, sizeof(pxa_trace_t) * TRACE_MAX); } else { printk(KERN_ERR"PXA_TRACE malloc ok %p\n", pxa_traces); } PXA_REGS(0,"init"); PXA_REGS(0,"test"); { struct proc_dir_entry *p; // create proc filesystem entries if ((p = create_proc_entry ("pxa", 0, 0)) == NULL) { printk(KERN_INFO"PXA PROC FS failed\n"); } else { printk(KERN_INFO"PXA PROC FS Created\n"); p->proc_fops = &pxa_proc_operations_functions; } } #endif #endif return 0; } /** * udc_release_release_io - release UDC io region */ void udc_release_io () { #ifdef PXA_TRACE #ifdef CONFIG_USBD_PROCFS unsigned long flags; save_flags_cli (flags); remove_proc_entry ("pxa", NULL); if (pxa_traces) { pxa_trace_t *p = pxa_traces; pxa_traces = 0; vfree(p); } restore_flags (flags); #endif #endif } /** * udc_release_udc_irq - release UDC irq */ void udc_release_udc_irq () { free_irq (IRQ_USB, NULL); } /** * udc_release_cable_irq - release Cable irq */ void udc_release_cable_irq () { #ifdef XXXX_IRQ free_irq (XXXX_CABLE_IRQ, NULL); #endif } /** * udc_regs - dump registers * * Dump registers with printk */ void udc_regs (void) { printk ("[%d:%d] CCR[%02x] UICR[%02x %02x] UFNH[%02x %02x] UDCCS[%02x %02x %02x %02x]\n", udc_interrupts, jifs(), UDCCR, UICR1, UICR0, UFNHR, UFNLR, UDCCS0, UDCCS1, UDCCS2, UDCCS5); } /* * dma_regs - dump DMA state * G.N.U., but may be useful for DMA debugging. */ static void dma_regs (void) { #ifdef CONFIG_USBD_PXA_DMA_IN printk (" DMA IN: DCSR:%x DCMD:%x DSADR:%x DTADR:%x\n", DCSR(usb_pxa_dma_in.dmach), DCMD(usb_pxa_dma_in.dmach), DSADR(usb_pxa_dma_in.dmach), DTADR(usb_pxa_dma_in.dmach)); #endif #ifdef CONFIG_USBD_PXA_DMA_OUT printk (" DMA OUT: DCSR:%x DCMD:%x DSADR:%x DTADR:%x\n", DCSR(usb_pxa_dma_out.dmach), DCMD(usb_pxa_dma_out.dmach), DSADR(usb_pxa_dma_out.dmach), DTADR(usb_pxa_dma_out.dmach)); #endif }