📄 l7205.c
字号:
/* * linux/drivers/usbd/l7205_bi/udc.c -- L7205 USB controller driver. * * Copyright (c) 2000, 2001, 2002 Lineo * * By: * Stuart Lynne <sl@lineo.com>, * Tom Rushworth <tbr@lineo.com>, * Bruce Balden <balden@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. * */// Debug enabling defines//#define TRIGGER 1//#define FLAG_F1ERR 1// End of debug enabling defines#include <linux/config.h>#include <linux/module.h>#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/tqueue.h>#include <linux/netdevice.h>#include <linux/version.h>#include <linux/pci.h>#include <linux/cache.h>#include <asm/dma.h>#include <asm/mach/dma.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/hardware.h>#include <asm/types.h>#include <asm/uaccess.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/pgalloc.h>#include <linux/delay.h>#include "../usbd.h"#include "../usbd-func.h"#include "../usbd-bus.h"#include "../usbd-inline.h"#include "usbd-bi.h"#ifdef CONFIG_IRIS#include <asm/arch/hardware.h>#include <asm/arch/gpio.h>#include <asm/arch/fpga.h>#include <asm/arch/iris_serialno.h>#endif#include "hardware.h"#include "l7205.h"#include "udc.h"int sof_saw_tx_active;int sof_saw_rx_f1ne; // track if we have seen F1NE at SOFint host_sus_interrupt_disabled; // set if SUS interrupt received and disabled, waiting for SOFint host_reset_interrupt_disabled; // set if HRST interrupt received and disabled, waiting for SOFint ep2_active; // set if transmit on ep2 is active, used to track missing interruptsextern unsigned int udc_interrupts;extern unsigned int udc_interrupts_last;static struct usb_device_instance *udc_device; // for interrupt handlerstruct usb_device_instance *ep1_device;struct usb_endpoint_instance *ep1_endpoint;#ifdef CONFIG_IRIS#define USB_PULLUP_GPIO bitPB6// USB pullup resistor control.// USB cable and AC power connect statusextern int iris_read_usb_sync (void);extern int iris_read_ac_sync (void);extern void iris_AUXPLL_ON_for_usb (void);extern void iris_AUXPLL_OFF_for_usb (void);#endif/* * ep_endpoints - map physical endpoints to logical endpoints */static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS];struct usb_device_instance *ep2_device;struct usb_endpoint_instance *ep2_endpoint;#define MIN(a,b) ((a)<(b))?(a):(b)/* ep1 functions ******************************************************************************* *//** * ep1_clear * * Clear ep1 fifo by reading all data and resetting F1BCNT to 32 */static /* __inline__ */ void ep1_clear (void){ int j; volatile unsigned long junk; for (j = 0; j < 8; j++) { junk = IO_USBF_FIFO1; } IO_USBF_F1BCNT = 32;}/* * Endpoint 1 interrupts will be directed here. * */static __inline__ void ep1_int_hndlr (void){ if (ep1_endpoint) { if (!ep1_endpoint->rcv_urb) { ep1_endpoint->rcv_urb = first_urb_detached (&ep1_endpoint->rdy); // XXX could we call usbd_fill_rcv() here? } if (ep1_endpoint->rcv_urb) { int len = IO_USBF_F1BCNT; unsigned long *lp = (unsigned long *) (ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length); // copy 4 bytes at a time, fetching more than available seems ok, use unrolled loop lp[0] = IO_USBF_FIFO1; lp[1] = IO_USBF_FIFO1; lp[2] = IO_USBF_FIFO1; lp[3] = IO_USBF_FIFO1; lp[4] = IO_USBF_FIFO1; lp[5] = IO_USBF_FIFO1; lp[6] = IO_USBF_FIFO1; lp[7] = IO_USBF_FIFO1; usbd_rcv_complete_irq (ep1_endpoint, len, 0); // reset F1BCNT IO_USBF_F1BCNT = 32; return; } // fall through if error of any type usbd_rcv_complete_irq (ep1_endpoint, 0, 0); } ep1_clear ();}/* * Endpoint 1 interrupts will be directed here if there was an error. * */void ep1_int_hndlr_error (void){#if 1 int i; volatile unsigned char *cp = (volatile unsigned char *) IO_USBF_FIFO_RX; printk (KERN_DEBUG "ep1_int_hndlr: ERROR "); for (i = 0; i < 32; i++) { printk ("%02x ", cp[i]); } printk ("\n");#endif if (ep1_endpoint) { usbd_rcv_complete_irq (ep1_endpoint, 0, 0); } ep1_clear ();}/* ep2 functions ******************************************************************************* */static int __inline__ ep2_fill (unsigned char *cp, int len, int delay){ unsigned long *buf = (unsigned long *) cp; volatile unsigned long *p2 = (volatile unsigned long *) IO_USBF_FIFO_TX; // unrolled loop to fill fifo IO_USBF_FIFO2 = buf[0]; IO_USBF_FIFO2 = buf[1]; IO_USBF_FIFO2 = buf[2]; IO_USBF_FIFO2 = buf[3]; IO_USBF_FIFO2 = buf[4]; IO_USBF_FIFO2 = buf[5]; IO_USBF_FIFO2 = buf[6]; IO_USBF_FIFO2 = buf[7]; if ((buf[0] != p2[0]) || (buf[1] != p2[1]) || (buf[2] != p2[2]) || (buf[3] != p2[3]) || (buf[4] != p2[4]) || (buf[5] != p2[5]) || (buf[6] != p2[6]) || (buf[7] != p2[7])) { udelay (8); // // reset fifo read pointer l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_F2CLR); // unrolled loop to fill fifo IO_USBF_FIFO2 = buf[0]; IO_USBF_FIFO2 = buf[1]; IO_USBF_FIFO2 = buf[2]; IO_USBF_FIFO2 = buf[3]; IO_USBF_FIFO2 = buf[4]; IO_USBF_FIFO2 = buf[5]; IO_USBF_FIFO2 = buf[6]; IO_USBF_FIFO2 = buf[7]; return (buf[0] != p2[0]) || (buf[1] != p2[1]) || (buf[2] != p2[2]) || (buf[3] != p2[3]) || (buf[4] != p2[4]) || (buf[5] != p2[5]) || (buf[6] != p2[6]) || (buf[7] != p2[7]); } return 0;}/** * ep2_start * * Fill ep2 fifo with data. */static void ep2_start (void){ int len; if (ep2_endpoint->tx_urb && (len = MIN ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent), ep2_endpoint->tx_urb->endpoint->tx_packetSize))) { ep2_active = 1; // fill the FIFO, if this fails send a short packet to end of this // attempt of the BULK transfer and force the restart of this urb at // th beginning, the host will see a CRC error and drop the // intermediate results if (ep2_fill (ep2_endpoint->tx_urb->buffer + ep2_endpoint->sent, len, 0)) { // ep2_fill failed, reset bulk transfer and decrement len to // send current usb packet one byte short to guarantee host will // terminate bulk transfer and it will fail CRC check IO_USBF_F2BCNT = len - 1; ep2_endpoint->last = ep2_endpoint->sent = 0; } else { ep2_endpoint->last = IO_USBF_F2BCNT = len; } } else { ep2_active = 0; }}/** * ep2_int_hndlr - transmit interrupt handler, fast version */static __inline__ void ep2_int_hndlr (unsigned int status){ // if we have active buffer, umap the buffer and update position if previous send was successful if (!(status & (USBF_STATUS_F2ERR | USBF_STATUS_F2BSY))) { if (ep2_endpoint->tx_urb) { ep2_endpoint->sent += ep2_endpoint->last; ep2_endpoint->last = 0; // if current buffer is finished call urb sent and advance to next urb if ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent) <= 0) { usbd_urb_sent_irq (ep2_endpoint->tx_urb, SEND_FINISHED_OK); ep2_endpoint->tx_urb = NULL; } ep2_start (); } }}/** * ep2_int_hndlr_error - transmit interrupt handler, slow version */static void ep2_int_hndlr_error (unsigned int status, int error, int skip){ if (!skip) { if ((status & (USBF_STATUS_F2ERR | USBF_STATUS_F2BSY | USBF_STATUS_F2NE)) || error) { if (!(status & USBF_STATUS_F2BSY)) { dbg_tx (0, "[%d]: F2ERR %08lx", udc_interrupts, IO_USBF_STATUS); l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_F2CLR); } } else { ep2_int_hndlr (status); } }}/* * l7205 */void l7205_dump_fifos (char *msg, int max){ int i; dbg_udc (9, " "); dbg_udc (9, "*********** %s ***********", msg); if (7 <= dbgflg_usbdbi_udc) { for (i = 0; i < max; i++) { if ((i % 32) == 0) { printk ("\nFF[%02x]: ", i); } printk ("%02x ", __IOB (USBF_FIFOS + i)); } printk ("\n"); }}void l7205_regs (char *msg){ if (1 <= dbgflg_usbdbi_udc) { int i; printk (KERN_DEBUG "\n"); printk (KERN_DEBUG "*********** %s ***********\n", msg); printk (KERN_DEBUG "\n"); printk (KERN_DEBUG "udc: Rev: %08lx Ctl: %08lx Sts: %08lx\n", IO_USBF_REVISION, IO_USBF_CONTROL, IO_USBF_STATUS); printk (KERN_DEBUG "udc: IntE: %08lx IntD: %08lx Ep0: %08lx\n", IO_USBF_INTENA, IO_USBF_INTDIS, IO_USBF_ENDPTBUF0); printk (KERN_DEBUG "udc: Ep1: %08lx Ep2: %08lx Ep3: %08lx\n", IO_USBF_ENDPTBUF1, IO_USBF_ENDPTBUF2, IO_USBF_ENDPTBUF3); printk (KERN_DEBUG "udc: CFG: %08lx ST0: %08lx ST1: %08lx\n", IO_USBF_CONFIGBUF1, IO_USBF_STRINGBUF0, IO_USBF_STRINGBUF1); printk (KERN_DEBUG "udc: ST2: %08lx ST3: %08lx ST4: %08lx\n", IO_USBF_STRINGBUF2, IO_USBF_STRINGBUF3, IO_USBF_STRINGBUF4); printk (KERN_DEBUG "\n"); printk (KERN_DEBUG "CLK ENA: %08lx AUX: %08lx SEL: %08lx\n", IO_SYS_CLOCK_ENABLE, IO_SYS_CLOCK_AUX, IO_SYS_CLOCK_SELECT); printk (KERN_DEBUG "\n"); printk (KERN_DEBUG "udc: CFG: %02lx:%02lx ST0: %02lx:%02lx ST1: %02lx:%02lx\n", __IOL (USBF_CONFIGBUF1) >> 9, __IOL (USBF_CONFIGBUF1) & 0x1ff, __IOL (USBF_STRINGBUF0) >> 9, __IOL (USBF_STRINGBUF0) & 0x1ff, __IOL (USBF_STRINGBUF1) >> 9, __IOL (USBF_STRINGBUF1) & 0x1ff); printk (KERN_DEBUG "udc: ST2: %02lx:%02lx ST3: %02lx:%02lx ST4: %02lx:%02lx\n", __IOL (USBF_STRINGBUF2) >> 9, __IOL (USBF_STRINGBUF2) & 0x1ff, __IOL (USBF_STRINGBUF3) >> 9, __IOL (USBF_STRINGBUF3) & 0x1ff, __IOL (USBF_STRINGBUF4) >> 9, __IOL (USBF_STRINGBUF4) & 0x1ff); for (i = 0; i < 168; i++) { if ((i % 32) == 0) { printk ("\nDS[%02x]: ", i); } printk ("%02x ", __IOB (USBF_DESCRIPTORS + i)); } printk ("\n"); printk ("\n"); }}void udc_regs (void){ if (1 <= dbgflg_usbdbi_tick) { printk (KERN_DEBUG "%u Ctl: %08lx Sts: %08lx Raw: %08lx Ena: %08lx EP0: %08lx EP1: %08lx EP2: %08lx " "int: %2d\n", udc_interrupts, IO_USBF_CONTROL, IO_USBF_STATUS, IO_USBF_RAWSTATUS, IO_USBF_INTENA, IO_USBF_F0BCNT, IO_USBF_F1BCNT, IO_USBF_F2BCNT, udc_interrupts - udc_interrupts_last); udc_interrupts_last = udc_interrupts; }}/* * l7205_copy_descriptor * * Copy descriptor to shared SRAM and optionally set descriptor register to point * to where it is located. * * Note that reg CAN be NULL (there is no device descriptor register it is * always at zero offset) */static int l7205_copy_descriptor (unsigned char *descriptors, volatile unsigned long reg, int offset, struct usb_descriptor *descriptor, int size){ // set default if (reg) { __IOL (reg) = 0; } if (!descriptors || !descriptor) { return offset; } if (!size) { if (!(size = descriptor->descriptor.generic.bLength)) { return offset; } } if (((offset + size) >= (USBF_DESCRIPTORS_MAX - 4))) { dbg_udc (0, "string too large to copy: offset: %x size: %d", offset, size); if (reg) { __IOL (reg) = (4 << 9) | (USBF_DESCRIPTORS_MAX - 4); } descriptors[USBF_DESCRIPTORS_MAX - 4] = 4; descriptors[USBF_DESCRIPTORS_MAX - 3] = 3; descriptors[USBF_DESCRIPTORS_MAX - 2] = 'A'; descriptors[USBF_DESCRIPTORS_MAX - 1] = 0; return offset; } dbg_udc (3, "reg: %08lx offset: %02x descriptor: %p size: %02x", reg, offset, descriptor, size); // set size and offset if we have a register if (reg) { __IOL (reg) = (size << 9) | offset; dbg_udc (3, "reg: %8lx %8lx size: %2lx off: %2lx", reg, __IOL (reg), __IOL (reg) >> 9, __IOL (reg) & 0x1ff); } memcpy (descriptors + offset, descriptor, size); return (offset + size + 3) & 0x1fc; // 4}static int l7205_copy_request (unsigned char *descriptors, struct usb_device_instance *device, volatile unsigned long reg, int offset, int request){ struct urb *udc_urb; usb_device_state_t device_state; /* * fake a setup request to get descriptors */ if (!device || !(udc_urb = usbd_alloc_urb (device, NULL, 0, 512))) { dbg_udc (0, "cannot alloc urb"); return offset; } udc_urb->device_request.bmRequestType = 0x80; udc_urb->device_request.bRequest = USB_REQ_GET_DESCRIPTOR; udc_urb->device_request.wValue = cpu_to_le16 ((request << 8)); udc_urb->device_request.wLength = cpu_to_le16 (200); // max 168? device_state = device->device_state; device->device_state = STATE_ADDRESSED; if (usbd_recv_setup (udc_urb)) { dbg_udc (0, "cannot process setup request"); usbd_dealloc_urb (udc_urb); return offset; } device->device_state = device_state; offset = l7205_copy_descriptor (descriptors, reg, offset, (struct usb_descriptor *) udc_urb->buffer, udc_urb->actual_length); usbd_dealloc_urb (udc_urb); return offset;}/** * udc_init - initialize L7205 USB Controller *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -