📄 edb7312-usb.c
字号:
/* * EDB7312 support for PDIUSBD12 / mass-storage. * * (c) Copyright 2003 Petko Manolov <petkan@nucleusys.com> * (c) Copyright 2003 Vladimir Ivanov <vladitx@nucleusys.com> * * TODO: * - more SCSI commands for various OS needs * - make this endian-independent with respect to USB structs, * as it is currently LE * * 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. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <asm/arch/hardware.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/delay.h>#include <asm/semaphore.h>#include <asm/signal.h>#include <asm/system.h>#include <asm/types.h>#include <linux/completion.h>#include <linux/config.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/blkdev.h>#include <linux/fs.h>#include <linux/kdev_t.h>#include <linux/locks.h>/* * XXX: Here's a good thing to think about * * According to USB 1.1, "Set Address" device request is the only one which * should be processed differently and must be completed _after_ successfull * status state ... do you really believe specs? We usually don't, but it * takes some time to find exactly what we won't believe. *//* ------------------------------------------------------------------------- *//* * debug mode */#undef DBG#define ID "usbd12"/* * hardware description */#define IRQ_D12 IRQ_EINT1#define REG_D12_DAT (EDB7312_VIRT_PDIUSBD12 + 0)#define REG_D12_CMD (EDB7312_VIRT_PDIUSBD12 + 1)/* * XXX: * * It seems that different EDB7312 revisions have the SUSPEND signal of * PDIUSBD12 routed to different locations. This is specific to P7 revision. */#define GPIO_D12_SUSP ((__raw_readb(EP7312_VIRT_CS3 + 0x0004) & 0x04) ? 1 : 0)#define __delay() { \ __raw_readb(EP7312_VIRT_CS3 + 0x0004); \ __raw_readb(EP7312_VIRT_CS3 + 0x0004); \}/* * atomic operations */#define d12_lock(p) disable_irq((p)->irq)#define d12_unlock(p) enable_irq((p)->irq)/* * PDIUSBD12 commands and data *//* --- "Select endpoint" --- */#define CMD_SELECT_ENDP 0x00#define D_SELECT_ENDP_FULL 0x01#define D_SELECT_ENDP_STALL 0x02/* --- "Set endpoint status" --- */#define CMD_SET_ENDP_STATUS 0x40/* --- "Read last transaction status" --- */#define CMD_READ_LTRNS 0x40#define D_READ_LTRNS_SUCCESS 0x01#define D_READ_LTRNS_ERR_M 0x1E#define D_READ_LTRNS_SETUP 0x20#define D_READ_LTRNS_DATA1 0x40#define D_READ_LTRNS_PREV 0x80#define D_READ_LTRNS_ERR_NO (0 << 1)#define D_READ_LTRNS_ERR_PID_ENC (1 << 1)#define D_READ_LTRNS_ERR_PID_UNK (2 << 1)#define D_READ_LTRNS_ERR_UNEXP_PK (3 << 1)#define D_READ_LTRNS_ERR_TOKCRC (4 << 1)#define D_READ_LTRNS_ERR_DATACRC (5 << 1)#define D_READ_LTRNS_ERR_TIMEOUT (6 << 1)#define D_READ_LTRNS_ERR_NEVER (7 << 1)#define D_READ_LTRNS_ERR_UNEXP_EOP (8 << 1)#define D_READ_LTRNS_ERR_NAK (9 << 1)#define D_READ_LTRNS_ERR_STALL (10 << 1)#define D_READ_LTRNS_ERR_OVF (11 << 1)#define D_READ_LTRNS_ERR_BITSTUFF (13 << 1)#define D_READ_LTRNS_ERR_WRONG_DPID (15 << 1)/* --- "Read endpoint status" --- */#define CMD_READ_ENDP_STATUS 0x80#define D_READ_ENDP_STAT_BUF0 0x20#define D_READ_ENDP_STAT_BUF1 0x40/* --- "Set address / enable" --- */#define CMD_SET_ADDR_EN 0xD0/* --- "Set endpoint enable" --- */#define CMD_SET_ENDP_EN 0xD8/* --- "Read buffer" --- */#define CMD_READ_BUFFER 0xF0/* --- "Write buffer" --- */#define CMD_WRITE_BUFFER 0xF0/* --- "Acknowledge setup" --- */#define CMD_ACKNOWLEDGE_SETUP 0xF1/* --- "Clear buffer" --- */#define CMD_CLEAR_BUFFER 0xF2/* --- "Set mode" --- */#define CMD_MODE 0xF3#define D_MODE0_NOLAZYCLK 0x02#define D_MODE0_CLKRUNNING 0x04#define D_MODE0_INTMODE 0x08#define D_MODE0_SOFTCONNECT 0x10#define D_MODE0_EP_NONISO (0 << 6)#define D_MODE1_CLK4M 0x0B#define D_MODE1_SETTOONE 0x40#define D_MODE1_SOFONLY 0x80/* * default CLKOUT mode is: * - lazy clock during standby * - no clock running during standby * - 4 MHz */#define D_MODE0_DEFAULT D_MODE0_EP_NONISO#define D_MODE1_DEFAULT (D_MODE1_SETTOONE | D_MODE1_CLK4M)/* --- "Read interrupt register" --- */#define CMD_READ_INT 0xF4#define D_READ_INT_EP0_O 0x0001#define D_READ_INT_EP0_I 0x0002#define D_READ_INT_EP1_O 0x0004#define D_READ_INT_EP1_I 0x0008#define D_READ_INT_EP2_O 0x0010#define D_READ_INT_EP2_I 0x0020#define D_READ_INT_BUSRESET 0x0040#define D_READ_INT_SUSPENDCHG 0x0080#define D_READ_INT_DMAEOT 0x0100/* --- "Read current frame number" --- */#define CMD_READ_FRAMENUM 0xF5/* --- "Validate buffer" --- */#define CMD_VALIDATE_BUFFER 0xFA/* --- "Set DMA" --- */#define CMD_DMA 0xFB#define D_DMA_NODMA 0x00#define D_DMA_INTMODE 0x20#define D_DMA_EPX4IE 0x40#define D_DMA_EPX5IE 0x80#define D_DMA_DEFAULT D_DMA_NODMA/* * general constants */#define TIME_OUT (HZ * 5)#define EP_CTRL 0#define EP_FREE 1#define EP_BULK 2#define EPX_CTRL_O 0#define EPX_CTRL_I 1#define EPX_FREE_O 2#define EPX_FREE_I 3#define EPX_BULK_O 4#define EPX_BULK_I 5#define EP_CTRL_SIZE 16#define EP_FREE_SIZE 16#define EP_BULK_SIZE 64/* --- */#define CONFIG_VAL 1#define DEF_LANG 0x0409/* ------------------------------------------------------------------------- *//* USB 1.1 */#define REQTYPE_DIR_M 0x80#define REQTYPE_DIR_OUT (0 << 7)#define REQTYPE_DIR_IN (1 << 7)#define REQTYPE_TYPE_M 0x60#define REQTYPE_TYPE_STANDARD (0 << 5)#define REQTYPE_TYPE_CLASS (1 << 5)#define REQTYPE_TYPE_VENDOR (2 << 5)#define REQTYPE_RECP_M 0x1F#define REQTYPE_RECP_DEVICE (0 << 0)#define REQTYPE_RECP_INTERFACE (1 << 0)#define REQTYPE_RECP_ENDPOINT (2 << 0)struct devreq { __u8 bmRequestType; __u8 bRequest; __u16 wValue; __u16 wIndex; __u16 wLength;} __attribute__ ((packed));#define DESC_TYPE_DEVICE 1#define DESC_TYPE_CONFIGURATION 2#define DESC_TYPE_STRING 3#define DESC_TYPE_INTERFACE 4#define DESC_TYPE_ENDPOINT 5#define CLASS_MASS_STORAGE 0x08#define SUBCLASS_SCSI 0x06#define PROTOCOL_BULK 0x50#define STATUS_DEVICE_SELF_POWERED 0x01#define STATUS_DEVICE_REMOTE_WAKEUP 0x02#define STATUS_ENDPOINT_HALT 0x01#define FEATURE_ENDPOINT_HALT 0#define FEATURE_DEVICE_REMOTE_WAKEUP 1/* mass-storage, bulk-only */#define CBW_SIGNATURE 0x43425355#define CBW_FLAGS_DIR_M 0x80#define CBW_FLAGS_DIR_OUT (0 << 7)#define CBW_FLAGS_DIR_IN (1 << 7)struct cbw { __u32 dCBWSignature; __u32 dCBWTag; __u32 dCBWDataTransferLength; __u8 bmCBWFlags; __u8 bCBWLUN; __u8 bCBWCBLength; __u8 CBWCB[16];} __attribute__ ((packed));#define CSW_SIGNATURE 0x53425355#define CSW_STAT_OK 0#define CSW_STAT_FAILED 1#define CSW_STAT_PHASE_ERROR 2struct csw { __u32 dCSWSignature; __u32 dCSWTag; __u32 dCSWDataResidue; __u8 bCSWStatus;} __attribute__ ((packed));/* ------------------------------------------------------------------------- *//* SCSI SPC-2 */#define SCSI_SKEY_NO_SENSE 0#define SCSI_SKEY_NOT_READY 2#define SCSI_SKEY_MEDIUM_ERROR 3#define SCSI_SKEY_ILLEGAL_REQUEST 5#define SCSI_ASC_NO 0x0000#define SCSI_ASC_LOGICAL_UNIT_NOT_READY 0x0400#define SCSI_ASC_WRITE_ERROR 0x0C02#define SCSI_ASC_UNRECOVERED_READ_ERROR 0x1100#define SCSI_ASC_INVALID_CMD 0x2000#define SCSI_ASC_LBA_OUT_OF_RANGE 0x2100#define SCSI_ASC_INVALID_FIELD_IN_CDB 0x2400#define SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED 0x2500#define MAX_SCSI_DATA_SENSE 18#define MAX_SCSI_DATA_INQUIRY 36#define MAX_SCSI_DATA_CAPACITY10 8#define MAX_SCSI_DATA_CAPACITY16 12/* ------------------------------------------------------------------------- *//* device I/O request */struct dioreq { int cmd; /* device i/o command */ int lun; __u32 lba; __u32 blks; __u32 nblks; void *buf;};/* device context */#define FSM_DEV_POWERED 0#define FSM_DEV_DEFAULT 1#define FSM_DEV_ADDRESS 2#define FSM_DEV_CONFIGURED 3#define FSM_CTL_IDLE 0#define FSM_CTL_DATA_IN 1#define FSM_CTL_STATUS_OUT 2#define FSM_CTL_STATUS_IN 3#define FSM_BULK_IDLE 0#define FSM_BULK_DATA_OUT 1#define FSM_BULK_DATA_OUT_WAIT 2#define FSM_BULK_DATA_IN 3#define FSM_BULK_DATA_IN_WAIT 4#define FSM_BULK_STALL_TO_CSW 5#define MAX_DBUF 256#define MAX_CHUNKBUF 65536#define ATD_STAT_READY (1 << 0)#define ATD_STAT_RO (1 << 1)#define DIO_CMD_EXIT 1#define DIO_CMD_READ 2#define DIO_CMD_WRITE 3#define DIO_CMD_VERIFY 4struct d12 { unsigned int reg_cmd; unsigned int reg_dat; unsigned int irq; const void *desc_dev; int desc_dev_len; const void *desc_cfg; int desc_cfg_len; int desc_str_max; const void * const *desc_str; const int *desc_str_len; /* --- */ int max_lun; const void *scsi_str_vendor[16]; const void *scsi_str_product[16]; const void *scsi_str_revision[16]; kdev_t atd_dev[16]; /* attached device */ int atd_blk[16]; /* block size */ int atd_chunkblk[16]; /* blocks per chunk */ __u32 atd_lba_capacity[16]; /* capacity in blocks */ int atd_stat[16]; /* status */ struct block_device *bdev[16]; /* --- */ int suspend; /* 1- in suspend mode */ int feat_wakeup; /* remote wakeup */ int fsm_dev; /* device state */ int fsm_ctl; /* control endpoint state */ int fsm_ctl_lastpkt; /* last packet sent */ struct devreq drq; /* device requests */ unsigned char dbuf[MAX_DBUF]; /* data buffer */ int dlen, dptr; int fsm_bulk; /* bulk endpoint state */ unsigned char *bbuf; /* bulk buffer chunk */ int blen, bptr; void (*bhd)(struct d12 *, int, int); /* chunk handler */ int bxferlen, bxferptr; /* total transfer length */ struct cbw cbw; /* CBW request */ struct csw csw; /* CSW answer */ int cswdelay; /* postpone CSW sending */ int cswearly; /* early CSW sending on error */ unsigned char scsi_sense[MAX_SCSI_DATA_SENSE]; /* should this be per LUN ? */ __u32 scsi_lba; __u32 scsi_blks; struct completion cpl_dio_sync; /* enter / exit sync */ struct semaphore wait_dio_cmd; volatile struct dioreq dio; unsigned char tmp[256]; /* template */ unsigned char chunkbuf[MAX_CHUNKBUF];};struct d12 d12;/* ------------------------------------------------------------------------- */static __u16 get_u16be(const void *p){ const unsigned char *b; __u16 r; b = (const unsigned char *)p; r = (b[0] << 8) | (b[1] << 0); return r;}#if 0static void put_16be(void *p, __u16 d){ unsigned char *b; b = (unsigned char *)p; b[0] = (d >> 8) & 0xFF; b[1] = (d >> 0) & 0xFF;}#endifstatic __u32 get_u32be(const void *p){ const unsigned char *b; __u32 r; b = (const unsigned char *)p; r = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3] << 0); return r;}static void put_32be(void *p, __u32 d){ unsigned char *b; b = (unsigned char *)p; b[0] = (d >> 24) & 0xFF; b[1] = (d >> 16) & 0xFF; b[2] = (d >> 8) & 0xFF; b[3] = (d >> 0) & 0xFF;}static __u64 get_u64be(const void *p){ const unsigned char *b; __u64 r; b = (const unsigned char *)p; r = get_u32be(b + 0); r <<= 32; r |= get_u32be(b + 4); return r;}static void put_64be(void *p, __u64 d){ unsigned char *b; b = (unsigned char *)p; put_32be(b + 0, d >> 32); put_32be(b + 4, d >> 0);}/* ------------------------------------------------------------------------- *//* * low-level PDIUSBD12 */static inline void d12_outc(struct d12 *p, int cmd){ __delay(); __raw_writeb(cmd, p->reg_cmd);}static inline void d12_outd(struct d12 *p, int dat){ __delay(); __raw_writeb(dat, p->reg_dat);}static inline int d12_ind(struct d12 *p){ __delay(); return __raw_readb(p->reg_dat);}/* --- */static inline void d12_out1(struct d12 *p, int cmd, int d0){ d12_outc(p, cmd); d12_outd(p, d0);}static inline void d12_out2(struct d12 *p, int cmd, int d0, int d1){ d12_outc(p, cmd); d12_outd(p, d0); d12_outd(p, d1);}static inline int d12_in1(struct d12 *p, int cmd){ d12_outc(p, cmd); return d12_ind(p);}static inline int d12_in2(struct d12 *p, int cmd){ int d0, d1; d12_outc(p, cmd); d0 = d12_ind(p); d1 = d12_ind(p); return (d1 << 8) | d0;}/* --- *//* clear buffer */static void d12_buf_clear(struct d12 *p){ d12_outc(p, CMD_CLEAR_BUFFER);}/* validate buffer */static void d12_buf_validate(struct d12 *p){ d12_outc(p, CMD_VALIDATE_BUFFER);}/* get frame number */#if 0static int d12_frame(struct d12 *p){ return d12_in2(p, CMD_READ_FRAMENUM) & 0x7FF;}#endif/* read last transaction status and ack interrupt */static int d12_read_last_trans(struct d12 *p, int epx){ return d12_in1(p, CMD_READ_LTRNS + epx);}/* set data pointer to start of internal buffer */static int d12_select_endpoint(struct d12 *p, int epx){ return d12_in1(p, CMD_SELECT_ENDP + epx);}/* set USB address (0x00 .. 0x7F) and enable */static void d12_set_address_enable(struct d12 *p, int adr, int en){ d12_out1(p, CMD_SET_ADDR_EN, adr | (en ? 0x80 : 0x00));}/* enable generic/isochronous endpoints */static void d12_set_endpoint_enable(struct d12 *p, int en){ d12_out1(p, CMD_SET_ENDP_EN, en ? 0x01 : 0x00);}/* get endpoint status */static int d12_status_get(struct d12 *p, int epx){ return d12_in1(p, CMD_READ_ENDP_STATUS + epx);}/* unconditionally initialize / stall endpoint */static void d12_status_set(struct d12 *p, int epx, int stall){ d12_out1(p, CMD_SET_ENDP_STATUS + epx, stall);}/* --- *//* stall / unstall */static void d12_stall_ctrl(struct d12 *p, int stall){ d12_status_set(p, EPX_CTRL_O, stall); d12_status_set(p, EPX_CTRL_I, stall);}static void d12_stall_free(struct d12 *p, int stall){ d12_status_set(p, EPX_FREE_O, stall); d12_status_set(p, EPX_FREE_I, stall);}static void d12_stall_bulk(struct d12 *p, int stall){ d12_status_set(p, EPX_BULK_O, stall); d12_status_set(p, EPX_BULK_I, stall);}/* connect / disconnect */static void d12_connect(struct d12 *p, int on){ d12_out2(p, CMD_MODE, D_MODE0_DEFAULT | (on ? D_MODE0_SOFTCONNECT : 0), D_MODE1_DEFAULT);}/* after bus-reset */static void d12_reset(struct d12 *p){ d12_set_address_enable(p, 0, 1); d12_set_endpoint_enable(p, 0); d12_stall_ctrl(p, 1); d12_stall_free(p, 1); d12_stall_bulk(p, 1);}/* initialize */static void d12_init(struct d12 *p){ d12_connect(p, 0); d12_out1(p, CMD_DMA, D_DMA_DEFAULT | D_DMA_EPX4IE | D_DMA_EPX5IE); d12_reset(p); d12_connect(p, 1);}/* acknowledge setup on endpoint #0 */static void d12_acknowledge_setup(struct d12 *p){ d12_select_endpoint(p, EPX_CTRL_O); d12_outc(p, CMD_ACKNOWLEDGE_SETUP); d12_select_endpoint(p, EPX_CTRL_I); d12_outc(p, CMD_ACKNOWLEDGE_SETUP);}/* read buffer */static int d12_buf_read(struct d12 *p, int epx, void *buf, int max){ int i, l, len, stat; unsigned char *b; b = (unsigned char *)buf; if (epx == EPX_BULK_O) { stat = d12_status_get(p, epx); i = len = 0; if (stat & D_READ_ENDP_STAT_BUF0) i++; if (stat & D_READ_ENDP_STAT_BUF1) i++; while (i-- && (max - len)) { d12_select_endpoint(p, epx); d12_in1(p, CMD_READ_BUFFER); l = d12_ind(p); if (len + l > max) l = max - len; len += l; while (l--) *b++ = d12_ind(p); d12_buf_clear(p); } } else { d12_select_endpoint(p, epx); d12_in1(p, CMD_READ_BUFFER); l = d12_ind(p); if (l > max) l = max; len = l;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -