📄 tc86c001.c
字号:
/* * linux/drivers/usbd/bi/tc86c001.c -- USB Device Controller driver. * * Copyright (c) 2000, 2001, 2002 Lineo * * By: * Stuart Lynne <sl@lineo.com>, * Tom Rushworth <tbr@lineo.com>, * Bruce Balden <balden@lineo.com> * * Copyright (C) 2002 Toshiba Corporation * * 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. * *//***************************************************************************/#include <linux/config.h>#include <linux/module.h>#include "../usbd-export.h"#include "../usbd-build.h"#include "../usbd-module.h"MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com, TOSHIBA Corporation");MODULE_LICENSE("GPL");MODULE_DESCRIPTION ("USB Device TC86C001 Bus Interface");USBD_MODULE_INFO ("tc86c001_bi 0.1-alpha");#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/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 "../usbd.h"#include "../usbd-debug.h"#include "../usbd-func.h"#include "../usbd-bus.h"#include "../usbd-inline.h"#include "usbd-bi.h"#define EP0_PACKETSIZE 0x8#define UDC_MAX_ENDPOINTS 4#define UDC_NAME "TC86C001 USBD"/* offset 0x000-0x1ff */struct tc_udc_regs{ volatile u32 IntStatus; volatile u32 IntEnable; volatile u32 MstSetting; volatile u32 MstWrStart; volatile u32 MstWrEnd; /* 0x010 */ volatile u32 MstWrCurr; volatile u32 MstRdStart; volatile u32 MstRdEnd; volatile u32 MstRdCurr; /* 0x020 */ volatile u32 PowerDetect;};/* offset 0x200-0x7ff */struct tc_udc_udcregs{ volatile u32 EPxFIFO[8]; /* 0x00 */ volatile u32 EPxMode[8]; /* 0x20 (0:reserved) */ volatile u32 EPxStatus[8]; /* 0x40 */ volatile u32 EPxSizeLA[8]; /* 0x60 */ volatile u32 EPxSizeLB[8]; /* 0x80 (0:reserved) */ volatile u32 EPxSizeHA[8]; /* 0xa0 (0:reserved) */ volatile u32 EPxSizeHB[8]; /* 0xc0 (0:reserved) */ volatile u32 unused1[8]; volatile u32 bmReqType; /* 0x100 */ volatile u32 bRequest; volatile u32 wValueL; volatile u32 wValueH; volatile u32 wIndexL; volatile u32 wIndexH; volatile u32 wLengthL; volatile u32 wLengthH; volatile u32 SetupRecv; /* 0x120 */ volatile u32 CurrConfig; volatile u32 StdRequest; volatile u32 Request; volatile u32 DataSet[2]; volatile u32 UsbState; volatile u32 EOP; volatile u32 Command; /* 0x140 */ volatile u32 EPxSingle[2]; volatile u32 EPxBCS[2]; volatile u32 unused2; volatile u32 IntControl; volatile u32 unused3; volatile u32 StdReqMode; /* 0x160 */ volatile u32 ReqMode; volatile u32 unused4[6]; volatile u32 PortStatus; /* 0x180 */ volatile u32 unused5[2]; volatile u32 Address; volatile u32 BuffTest; volatile u32 unused6; volatile u32 UsbReady; volatile u32 unused7; volatile u32 SetDescStall; /* 0x1a0 */};/* MstSetting.MST_connection == 0 */#define UDC_MSTWR_ENDPOINT 1#define UDC_MSTRD_ENDPOINT 2#define UDC_DESCRAM_SIZE 0x80/* offset 0x800-0x9ff */struct tc_udc_ramregs{ volatile u32 DescRam[UDC_DESCRAM_SIZE];};/* IntStatus/IntEnable */#define INT_SUSPEND 0x00001#define INT_USBRESET 0x00002#define INT_ENDPOINT0 0x00004#define INT_SETUP 0x00008#define INT_STATUS 0x00010#define INT_STATUSNAK 0x00020#define INT_EP1DATASET 0x00040#define INT_EP2DATASET 0x00080#define INT_EP3DATASET 0x00100#define INT_EPxDATASET(n) (0x00040 << ((n) - 1))#define INT_EP1NAK 0x00200#define INT_EP2NAK 0x00400#define INT_EP3NAK 0x00800#define INT_EPnNAK(n) (0x00200 < ((n) - 1))#define INT_SOF 0x01000#define INT_ERR 0x02000#define INT_MSTWRSET 0x04000#define INT_MSTWREND 0x08000#define INT_MSTWRTMOUT 0x10000#define INT_MSTRDEND 0x20000#define INT_SYSERROR 0x40000#define INT_PWRDETECT 0x80000/* MstSetting */#define MST_EOPB_DIS 0x0800#define MST_EOPB_ENA 0x0400#define MST_TIMEOUT_DIS 0x0200#define MST_TIMEOUT_ENA 0x0100#define MST_RD_EOPB 0x0080#define MST_RD_RESET 0x0040#define MST_WR_RESET 0x0020#define MST_RD_ENA 0x0004 /* 1:start, 0:ignore */#define MST_WR_ENA 0x0002 /* 1:start, 0:ignore */#define MST_CONNECTION 0x0001#define MST_RW_BITS 0x0f67 /* read/write bits *//* PowerDetect */#define PW_DETECT 0x04#define PW_RESETB 0x02#define PW_PLLUPENB 0x01/* udcregs StdRequest/StdReqMode */#define STDREQ_S_INTERFACE 0x80#define STDREQ_G_INTERFACE 0x40#define STDREQ_S_CONFIG 0x20#define STDREQ_G_CONFIG 0x10#define STDREQ_G_DESCRIPT 0x08#define STDREQ_S_FEATURE 0x04#define STDREQ_C_FEATURE 0x02#define STDREQ_G_STATUS 0x01/* udcregs EPxStatus */#define EPxSTATUS_TOGGLE 0x40#define EPxSTATUS_SUSPEND 0x20#define EPxSTATUS_EP_MASK 0x1c#define EPxSTATUS_EP_READY 0x00#define EPxSTATUS_EP_DATAIN 0x04#define EPxSTATUS_EP_FULL 0x08#define EPxSTATUS_EP_TX_ERR 0x0c#define EPxSTATUS_EP_RX_ERR 0x10#define EPxSTATUS_EP_BUSY 0x14#define EPxSTATUS_EP_STALL 0x18#define EPxSTATUS_EP_INVALID 0x1c#define EPxSTATUS_FIFO_DISABLE 0x02#define EPxSTATUS_STAGE_ERROR 0x01/* udcregs Command */#define COMMAND_SETDATA0 2#define COMMAND_RESET 3#define COMMAND_STALL 4#define COMMAND_INVALID 5#define COMMAND_EP(n) ((n) << 4)/* udcregs UsbState */#define USBSTATE_CONFIGURED 0x04#define USBSTATE_ADDRESSED 0x02#define USBSTATE_DEFAULT 0x01static struct pci_dev *udc_pci_dev; /* only one instance */static struct tc_udc_regs *tc_regs;static struct tc_udc_udcregs *tc_udcregs;static struct tc_udc_ramregs *tc_ramregs;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 dma_addr_t ep_dmaaddrs[UDC_MAX_ENDPOINTS];static struct urb *ep0_urb;extern unsigned int udc_interrupts;static int nodma;MODULE_PARM (nodma, "i");MODULE_PARM_DESC (nodma, "Disable DMA");/* *********************************************************************** *//* IO *//** * tc_write_buffer - write a buffer to the tc fifo * @ep: endpoint * @b: pointer to buffer to write * @size: number of bytes to write */static voidtc_write_buffer (unsigned char ep, unsigned char *b, unsigned char size){ while (size--) { writel (*b++, &tc_udcregs->EPxFIFO[ep]); }}/** * tc_read_buffer - fill a buffer from the tc fifo * @ep: endpoint * @b: pointer to buffer to fill * @size: number of bytes to read */static voidtc_read_buffer (unsigned char ep, unsigned char *b, unsigned char size){ while (size--) { *b++ = readl (&tc_udcregs->EPxFIFO[ep]); }}/** * tc_read_epxsize - read data count in FIFO of the endpoint. * @ep: endpoint */static inttc_read_epxsize (unsigned int ep){ int low = readl (&tc_udcregs->EPxSizeLA[ep]); int size; /* SizeH: DATA[9:7], SizeH: DATA[6:0] */ if (low & 0x80) { size = ((readl (&tc_udcregs->EPxSizeHA[ep]) & 0x03) << 7) | (low & 0x7f); } else { low = readl (&tc_udcregs->EPxSizeLB[ep]); size = ((readl (&tc_udcregs->EPxSizeHB[ep]) & 0x03) << 7) | (low & 0x7f); } return size;}static voidcopy_descram (const void *data, int len, int *ofs){ const unsigned char *p = (const unsigned char *) data; int i; if (*ofs <= UDC_DESCRAM_SIZE && *ofs + len > UDC_DESCRAM_SIZE) printk (KERN_ERR UDC_NAME ": too big descriptor\n"); if (*ofs + len <= UDC_DESCRAM_SIZE) { for (i = 0; i < len; i++) writel (*p++, &tc_ramregs->DescRam[(*ofs) + i]); } *ofs += len;}/* *********************************************************************** *//* Control (endpoint zero) *//** * tc_in_ep0 - start transmit * @ep: */static voidtc_in_ep0 (struct usb_endpoint_instance *endpoint){ if (!endpoint) return; if (endpoint->tx_urb) { struct urb *urb = endpoint->tx_urb; dbg_ep0 (2, "length: %d sent %d", endpoint->tx_urb->actual_length, endpoint->sent); if ((urb->actual_length - endpoint->sent) > 0) { if (readl (&tc_udcregs->DataSet[0]) & 1) { printk (KERN_ERR UDC_NAME ": ep0 FIFO not empty\n"); return; } endpoint->last = min_t (int, urb->actual_length - endpoint->sent, endpoint->tx_packetSize); tc_write_buffer (0, urb->buffer + endpoint->sent, endpoint->last); if (endpoint->last < endpoint->tx_packetSize) { dbg_ep0 (2, "EOP"); writel (~1, &tc_udcregs->EOP); } } else { // XXX ZLP endpoint->last = 0; dbg_ep0 (2, "EOP"); writel (~1, &tc_udcregs->EOP); } } else if (endpoint->last == 0 || endpoint->last == endpoint->tx_packetSize) { // XXX ZLP dbg_ep0 (2, "EOP (last %d)", endpoint->last); endpoint->last = 0; writel (~1, &tc_udcregs->EOP); }}static voidtc_out_ep0 (struct usb_endpoint_instance *endpoint){ printk (KERN_ERR UDC_NAME "tc_out_ep0: not supported.\n");}static voidtc_ep0_setup (void){ struct usb_device_request *req = &ep0_urb->device_request; u8 *buf = (u8 *) req; struct usb_endpoint_instance *endpoint = ep_endpoints[0]; if (!endpoint) { dbg_udc (0, "no endpoint 0"); return; } /* store in little endian (see ep0_recv_setup) */ buf[0] = readl (&tc_udcregs->bmReqType); buf[1] = readl (&tc_udcregs->bRequest); buf[2] = readl (&tc_udcregs->wValueL); buf[3] = readl (&tc_udcregs->wValueH); buf[4] = readl (&tc_udcregs->wIndexL); buf[5] = readl (&tc_udcregs->wIndexH); buf[6] = readl (&tc_udcregs->wLengthL); buf[7] = readl (&tc_udcregs->wLengthH); writel (0, &tc_udcregs->SetupRecv); dbg_ep0 (1, "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x", req->bmRequestType, req->bRequest, le16_to_cpu (req->wValue), le16_to_cpu (req->wIndex), le16_to_cpu (req->wLength)); if (udc_device->device_state == STATE_DEFAULT && ((req->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE || (req->bmRequestType & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS) && (readl (&tc_udcregs->Address) & 0xff)) { /* first Host-to-Device setup or Class setup packet */ u8 new_address = readl (&tc_udcregs->Address) & 0xff; /* SET_ADDRESS are processed by hardware only */ dbg_ep0 (1, "address assigned (%x)", new_address); if (udc_device->address && udc_device->address != new_address) printk (KERN_ERR "address changed\n"); udc_device->address = new_address; usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0); } if (usbd_recv_setup (ep0_urb)) { dbg_ep0 (0, "usb_recv_setup failed, stalling"); udc_stall_ep (0); return; } // check data direction if ((req->bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) { // should we setup to receive data if (le16_to_cpu (req->wLength)) { dbg_ep0 (1, "setup to read data %d", le16_to_cpu (req->wLength)); endpoint->rcv_urb = ep0_urb; endpoint->rcv_urb->actual_length = 0; tc_out_ep0 (endpoint); return; } if (req->bRequest == USB_REQ_SET_CONFIGURATION) { switch (req->wValue) { case 0: /* configured --> addressed */ usbd_device_event (udc_device, DEVICE_DE_CONFIGURED, 0); writel (0, &tc_udcregs->UsbState); dbg_udc (1, "UsbState %x", readl (&tc_udcregs->UsbState)); break; default: /* addressed --> configured */ //usbd_device_event(udc_device, DEVICE_CONFIGURED, 0); writel (USBSTATE_CONFIGURED, &tc_udcregs->UsbState); dbg_udc (1, "UsbState %x", readl (&tc_udcregs->UsbState)); } } dbg_ep0 (2, "EOP"); writel (~1, &tc_udcregs->EOP); return; } // we should be sending data back // check request length, zero is not legal if (!le16_to_cpu (ep0_urb->device_request.wLength)) { dbg_ep0 (0, "wLength zero, stall"); udc_stall_ep (0); return; } // check that we have some data to send back, zero should not be possible if (!ep0_urb->actual_length) { dbg_ep0 (0, "no data, stall"); udc_stall_ep (0); return; } // start sending endpoint->tx_urb = ep0_urb; endpoint->sent = 0; endpoint->last = 0; tc_in_ep0 (endpoint);}/* *********************************************************************** *//* Bulk OUT (recv) */static voidtc_mstrd_start (u32 start, u32 len, int eop){ u32 mst = readl (&tc_regs->MstSetting); mst &= MST_RW_BITS & ~(MST_WR_ENA | MST_EOPB_ENA | MST_EOPB_DIS); mst |= eop ? MST_EOPB_ENA : MST_EOPB_DIS; mst |= MST_RD_ENA; dbg_tx (2, "mstrd 0x%x 0x%x 0x%x", start, len, mst); writel (start, &tc_regs->MstRdStart); writel (start + len - 1, &tc_regs->MstRdEnd); writel (mst, &tc_regs->MstSetting);}static voidtc_mstwr_start (u32 start, u32 len){ u32 mst = readl (&tc_regs->MstSetting); mst &= MST_RW_BITS & ~(MST_RD_ENA); mst |= MST_WR_ENA; dbg_rx (2, "mstwr 0x%x 0x%x 0x%x", start, len, mst); writel (start, &tc_regs->MstWrStart); writel (start + len - 1, &tc_regs->MstWrEnd); writel (mst | MST_WR_ENA, &tc_regs->MstSetting);}static unsigned long tc_do_task_ep;static void tc_start_out (unsigned int ep);static voidtc_do_task (void *data) // runs as process{ unsigned long flags; unsigned int ep; if (!udc_device || udc_device->status != USBD_OK) return; for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) { if (test_and_clear_bit (ep, &tc_do_task_ep)) { local_irq_save (flags); while (!ep_endpoints[ep]->rcv_urb && !(ep_endpoints[ep]->rcv_urb = first_urb_detached_irq (&(ep_endpoints[ep]->rdy)))) { local_irq_restore (flags); /* usbd_recycle_urb will be called via device_bh */ run_task_queue (&tq_immediate); local_irq_save (flags); } dbg_rx (1, "ep%d rcv urb available.", ep); tc_start_out (ep); local_irq_restore (flags); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -