📄 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 <sl@lineo.com>, * Tom Rushworth <tbr@lineo.com>, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * *//* * This code was developed on the Intel Mainstone with Bulverde processor */#include <linux/config.h>#include <linux/module.h>#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 <linux/kernel.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/atomic.h>#include <asm/io.h>#include <linux/proc_fs.h>#include <linux/vmalloc.h>#include <linux/netdevice.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/types.h>#include <asm/uaccess.h>#include <asm/io.h>#include <linux/delay.h>#include <asm/hardware.h>#include <asm/dma.h>#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 16typedef 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); }}#elsestatic __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 1static 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 4096unsigned 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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -