URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [scsi/] [tripace.c] - Rev 1626
Go to most recent revision | Compare with Previous | Blame | View Log
/* tc2550.c -- Tripace TC-2550x based PCI SCSI Adapter SCSI driver * Created:D.Ravi jun 8 1998 at chennai lab of Tripace * Copyright 1998 Tripace Europe BV * * Driver version 1.00.000 (904) * 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, 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. ************************************************************************** DESCRIPTION: REFERENCES USED: 1.0 Design Kit for the Tripace TC-2550x based PCI SCSI Adapter 2.0 Tripace IOLAYER document. 3.0 LINUX driver sources in /usr/linux/drivers/scsi directory. ALPHA TESTERS: 1.0 so far no external testers,only developer testing has been done. 2.0 Testing with IBMHDD,Quantum HDD,zip drive and sony CDROM has been done. NOTES ON USER DEFINABLE OPTIONS: 1.0 The following are the command line options that are possible for the TRIPACE TC-2550 PCI SCSI controller without BIOS. tripace=fast clock,discon,sync,tag The values for these four options can be either 0 or 1. If fast clock is set to 1 ,then the chip uses a 60mhz clock.If ultra scsi devices are used this should be set and the controller should have a 60mhz crystal. if disconnect is 1 ,then disconnect/reconnect is allowed for all scsi devices connected to the controller.if it is 0 ,it is off. sync = 1 means that synchronous negotiation will be done with scsi devices.currently,though the flag is set ,the function is not implemented. Tag = 1 means that tagged queue commands can be sent to the scsi devices. This is not implemnted as yet in the driver. The default values are 0,1,1,1 **************************************************************************/ #ifdef MODULE #include <linux/module.h> #endif #include <linux/kernel.h> #include <linux/malloc.h> #include <linux/mm.h> #include <linux/head.h> #include <linux/sched.h> #include <linux/types.h> #include <asm/io.h> #include <linux/blk.h> #include "scsi.h" #include "hosts.h" #include "tripace.h" #include <asm/system.h> #include <asm/dma.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/ioport.h> #include <linux/proc_fs.h> #include <linux/bios32.h> #include <linux/pci.h> #include <linux/delay.h> #include "tripace_mcode.h" #define VERSION "$Revision: 1.1 $" /* #define DEBUG */ #define MAXTARGET 8 #define MAXSRBS 16 #define RISC_ENTRY1 0 #define DEV_PARAM_REG_BASE CFG_BASE+0x20 #define SCSI_DMA_POINTER CFG_BASE+0x40 #define SCSI_DMA_COUNTER CFG_BASE+0x44 #define HOST_DMA_POINTER CFG_BASE+0x48 #define HOST_DMA_COUNTER CFG_BASE+0x4c #define MBX_IN_BASE CFG_BASE+0x50 #define MBX_OUT_BASE CFG_BASE+0x54 #define DEV_HDR_BASE CFG_BASE+0x58 #define TASK_Q_BASE CFG_BASE+0x5c #define CURRENT_DEV_PTR CFG_BASE+0x60 #define CURRENT_TASK_PTR CFG_BASE+0x64 #define SG_LIST_PTR CFG_BASE+0x68 #define ACC CFG_BASE+0x6c #define SCRATCHB CFG_BASE+0x70 #define CURRENT_INST CFG_BASE+0x74 #define FLAG CFG_BASE+0x78 #define SCRATCHC CFG_BASE+0x7a #define MBX_IN_INDEX CFG_BASE+0x7b #define PC CFG_BASE+0x7c #define MBX_OUT_INDEX CFG_BASE+0x7f #define PROGRAM_DATA_REG CFG_BASE+0x80 #define CONTROL_REG CFG_BASE+0x84 #define STATUS_INT_REG CFG_BASE+0x86 #define SCSI_DATA CFG_BASE+0x88 #define SCSI_CONTROL CFG_BASE+0x8A #define SCSI_ID_REG CFG_BASE+0x8b #define INFO_BASE 0x780 #define SIG_BASE 0x7D4 #define Q_FILE_SIZE 8192 #define DEVHDR_SIZE 512 #define MBOX_SIZE 48 #define BIOS_SIGNATURE 0x55AAC731 #define PCI_1 1 #define PCI_2 2 #define MAX 28 #define PCI_BUS 1 #define PCI_INDEX_PORT 0xCF8 #define PCI_INDEX 0xF0 #define PCI_CONFIG 0xC000 /* this needs to include slot number */ #define FAIL -1 /* #define GOOD 0 */ #define ERR 1 #define DONE 2 #define INT_HALT 0xfe000000 /* interrupt code */ #define SRB_DONE 0 #define SRB_ACTIVE 2 #define SRB_READY 1 #define SRB_CLEAN 4 #define SRB_ASSIGNED 8 #define SRB_STOP 2 #define STOP_RISC 2 #define FIRST_CMD 1 #define SPECIAL_CMD 1 #define MAX_OFFSET 30 /* #define MAX_PERIOD 12 */ #define MAX_PERIOD 12 #define W_MAX_OFFSET 14 /* msg */ #define CMD_ABORT 0x6 #define DEVICE_RESET 0xC #define CHECK_SYNC 0xA #define CHECK_WIDE 0xD #define SYNC_NORESPONSE 0x8 #define SYNC_REJECTED 0x9 #define SEL_TIME_OUT 0x11 #define ERR_OVERRUN 0x12 #define ERR_BUSFREE 0x13 #define ERR_PHASE 0x14 #define CHECK_COND 0x04 #define ERR_PARITY 0x05 #define ERR_MESSAGE 0x07 #define WIDE_NORESPONSE 0x0b #define WIDE_REJECTED 0x0c #define DO_WIDE_NEGO 0x02 #define DO_SYNC_NEGO 0x04 #define FIRST_COMMAND 0x01 #define ENABLE_WIDE_BUS 0x100 #define ENABLE_TAG_MSG 0x200 /*---------------------------------------------------------* ** parameter in register definitions * ** * **---------------------------------------------------------*/ /* status register (read only) */ #define INTR_PENDING 0x01 #define INTR_DIS 0x02 #define SCAM_INTR_DIS 0x04 #define POWER_SAVE_ON 0x08 #define SCSI_RESET_OUT 0x10 #define SCAM_INTR 0x20 #define RISC_HALT 0x40 #define SELECTION_TIMEOUT 0x80 #define SCSI_PARITY_ERR 0x100 #define SCSI_RESET_LAT 0x200 #define STS_SCSI_RESET 0x400 #define EEPROM_IN 0x800 #define SCAM_ARB_WIN 0x1000 #define EN_DMA 0x2000 #define EN_ARB_SEL 0x4000 /* ** interrupt control register (write only) */ #define RESET_INTR_LATCH 0x01 #define DIS_INTR 0x02 #define DIS_SCAM_INTR 0x04 #define POWER_SAVE 0x08 #define RESET_SCSI_BUS 0x10 /*ravi 10/3/98 changed for term power disable/enable in command line */ /*#define EN_TERM_PWR 0x20 */ #define TERM_PWR_EN 0x20 /* ** control register ( read/write ) */ #define RISC_CHIP_RESET 0x01 #define RISC_SINGLE_SETP 0x02 #define HALT_RISC 0x04 #define EN_SCSI_SCAM 0x08 #define DIS_SCSI_PARITY 0x10 #define EN_TARGET_MODE 0x20 #define FAST_CLOCK 0x40 #define EN_MEMORY_WRITE 0x80 #define DIS_PCI_BURST 0x100 #define DIS_PCI_CACHE_LINE 0x200 #define DIS_MUL_CACHE_LINE 0x400 #define EEPROM_CLOCK 0x800 #define EEPROM_DATA_OUT 0x1000 #define EEPROM_CHIP_SEL 0x2000 #define FAST_SELECTION 0x4000 #define EN_SCAM_ARB 0x8000 #define TASK_DONE 0x80 /*************************************************************************** ** filename: newtypes.h ** usage : type definition ****************************************************************************/ /*************************************************************************** ** filename: newtypes.h ** usage : type definition ****************************************************************************/ #define MAX_Adapter 4 /* maximu adapters allow */ /* ** command related structure (7 bytes) */ typedef struct _task_cmd { u8 CmdInProcess; u8 * REQ_Header; void (*complete) (void); } TaskCmd, *PTaskCmd; /* ** total 128 bytes */ typedef struct _HIM { u8 status; /* device status, 0xFF not installed */ u8 Target_ID; /* target ID */ u8 type; /* target type mapped to device name */ u16 attrib; /* lo 4bit: 1-Wide, 2-Sync, 4-Tag, */ /* 8-removable, */ /* hi 4bit: 10h do wide, 20h do sync, */ /* 40h do tag, 0x80h - disc */ /* bit 15: under BIOS, bit 8: > 1GB */ u8 op_param; /* bit 7-5:period, bit4-0:offset */ u8 drv_num; /* current ID --> 8x for BIOS used only */ u8 sect_per_track; /* sector per track */ u8 head_per_cyl; /* sector per track */ u8 byte_per_sect; /* byte per sector (BIOS is 512) */ u8 link_count; /* SRB link count: */ u8 * ASPICMDLink; /* SRB link starting pointer */ TaskCmd task[16]; /* array structure for each task */ } DEVStruct; /* ** total (4K+ 36) bytes for each adapter */ typedef struct _Adapter { u16 IoPort; /* I/O Port address, 0 notsupport */ u8 AdapterID; /* Adapter scsi ID, default = 7 */ u8 first_disk_num; /* first disk number under BIOS (82->2) */ u8 last_disk_num; /* last disk number under BIOS */ u8 time_factor; /* used for device scam */ u8 IntrNum; /* -1 NotSupport, otherwise IRQ */ u16 hw_attr; /* see eeprom def */ u16 sw_attr; /* see eeprom def */ DEVStruct dev[16]; /* target device structure */ u16 mbx_out_ptr; /* mail box out pointer */ u8 HAParam[16]; /* host adapter parameters */ u8 bios_install; /* adapter has BIOS installed */ u16 scam_type; /* scam use */ u8 scam_assigned; /* scam use */ u8 * Signature; /* BIOS signature & new manager address */ u32 mbx_in_base; /* mbx in base logical address */ u32 mbx_out_base; /* mbx out base logical address */ u32 devhdr_base; /* devhdr base logical address */ u32 taskq_base; /* taskq base logical address */ u32 ptaskq_base; /* taskq base physical address */ u32 pdev_base; /* taskq base physical address */ u32 pmbi_base; /* taskq base physical address */ u32 pmbo_base; /* taskq base physical address */ } Adapter, *PAdapter; /* ** adapter information structure reserved for BIOS usage */ typedef struct _dev_parm { u8 header; /* header */ u8 sect_per_track; /* sector per tarck */ } target_parm, *ptarget_parm; /* ** This structure is for BIOS used and read by driver */ typedef struct _Ada_data { u8 drv_start; /* first drive */ u8 drv_end; /* last drive */ u16 timfact; /* timing factor */ u32 old_int13; /* old int13 addr */ u8 drive_id[8]; /* index= (8x-drv_start) */ u8 drive_num[16]; /* device number */ target_parm dev[16]; /* device parameters */ u8 allow_discon[16]; /* disconnect record */ u16 scam_type; /* scam use */ u8 scam_assigned; /* scam use */ u8 adapter_id; /* scam use */ u32 signature; /* BIOS signature */ } HA_data, *PHA_data; typedef struct _ScatGath { u32 sg_address; /* must be physical address */ u32 sg_length; /* sg length */ } ScatGath, *PScatGath; /* length must be 32 bytes */ typedef struct _risc_srb { u8 Tag_info; /* Tag information */ u8 SRB_flag; /* SRB flag */ u8 DEV_Status; /* SRB status */ u8 ScsiStatus; /* scsi command status */ u32 CDB; /* SCSI Command Block */ u32 CDBLength; /* SCSI Command Length */ u32 SenseDataPtr; /* auto sense pointer */ u32 Sense_Cmd_Ptr; /* auto sense pointer */ u32 SG_ListPtr; /* SG list */ u8 SGNum; /* S/G number */ u8 Identify; /* Identify message */ u8 Sense_LUN; /* lun */ u8 Sense_len; /* sense len */ u32 Cmd_sg_addr; /* cmr sg ptr point to cmd */ } RISC_SRB, *PRISC_SRB; typedef struct _SRB { /* for SCSI */ RISC_SRB *risc_srb; /* structure of SRB in RISC */ PAdapter AdapterPtr; /* a pointer to adapter structure */ u8 LUN; /* lun number */ u8 Tag_type; /* tag type */ u8 Request_type; /* request type */ } SRB, *PSRB; typedef struct _sync_tbl { int period; /* parameter setting */ int f_factor; /* fast clock factor */ int s_factor; /* slow clock factor */ } sync_tbl; /* ** total 12 bytes of sg header */ typedef struct _dma_hrd { u32 size; /* region size */ u32 offset; /* offste */ u16 segment; /* segment */ u16 revsed; /* reserved */ u16 num_avail; /* number available */ u16 num_used; /* number used */ } DMA_HDR; /* ** total 60 bytes (for each task in windows) */ typedef struct _dma_desc { DMA_HDR dma_hdr; ScatGath sg_list[6]; } dma_desc, *Pdma_desc; /* Flags */ #define SRB_TOHOST 8 #define SRB_TOTARGET 0x10 #define SRB_NEEDSDT 0x20 #define SRB_SENSE 0x40 /* only for OSD */ #define SIZE_OF_SG 0x3C /* size of sg table (60 bytes) */ /* 2. Target Error == SCSI Status */ /* ============ function definiton ============ ** 1. INPUT: PSRB ** Start a scsi command. * */ static void StartScsiCmd(PRISC_SRB); #define CMD_INPROGRESS 0x01 #define CMD_DISCONCTED 0x02 #define CMD_DATAXFERED 0x04 #define TAR_TRUESG 0x01 #define NEEDSDTN 0x10 #define MSG_ABORT 0x06 #define MSG_RESET 0x0C #define MSG_ALLOWDISC 0x40 #define MSG_IDENTIFY 0x80 #define MSG_EXTENDED 0x01 #define MSG_NOMSG 0x08 #define MSG_CMDCOMP 0x00 #define MSG_DISC 0x04 #define MSG_IGNWR 0x23 #define MSG_RESTPTR 0x03 #define MSG_SAVEPTR 0x02 #define MSG_REJECTED 0x07 #define MSG_INITRECVY 0x0f #define MSG_LNKCMD 0x0a #define MSG_LNKCMDTAG 0x0b #define MSG_SIMPLEQUE 0x20 #define MSG_MDFDATPTR 0x00 #define MSG_SDTREQ 0x01 #define MSG_WDTREQ 0x03 #define SECOND 18 //ticks per second #define DEV_EMPTY 0xFF typedef struct _DEVHDR { u32 Updatedmap; /* updated bitmap */ u32 Startedmap; /* start bitmap */ u32 Currentmap; /* current bitmap */ u8 Task_Index; /* current task index */ u8 Request_type; /* request type */ u8 SpCmddone; /* special commad done */ u8 Srb_loc; /* location */ u8 TagCmdCnt; /* Pending task count */ u8 ScsiID; /* scsi ID */ u8 DeviceNum; /* device number */ u8 WideMsg; /* wide bus message */ u8 SyncPeriod; /* sync period */ u8 SyncOffset; /* sync offset */ u8 RtnWideMsg; /* return wide bus msg */ u8 RtnSyncPeriod; /* Rtn sync period */ u8 RtnSyncOffset; /* rtn sync offset */ u8 ChkSenseTask; /* check sense task */ u8 SenseCmd[6]; /* sense command */ } DevHdr, *PDevHdr; typedef struct _E2Prom { u16 hw_parm; u16 sw_parm; u16 dev_parm[16]; } E2prom, *PE2prom; /* parameter setting for parameter */ #define HW_ADAPTER_ID 0x0f #define HW_PARITY_DISABLE 0x10 #define HW_TERMPWR_ENABLE 0x20 #define HW_FAST_CLOCK 0x40 #define HW_DIAG_ENABLE 0x80 #define HW_BURST_DISABLE 0x100 #define HW_CACHE_LINE_DISABLE 0x200 #define HW_MULTI_CACHE_DISABLE 0x400 #define HW_SCAM_ENABLE 0x800 #define HW_CACHE_LINE_SIZE_4 0x1000 #define HW_CACHE_LINE_SIZE_8 0x2000 #define HW_CACHE_LINE_SIZE_16 0x3000 #define HW_FAST_SELECTION 0x4000 #define HW_BIOS_DISABLE 0x8000 /* software setting */ #define SW_REMOVABLE_SUPPORT 0x1 #define SW_OVER_1GB 0x2 #define SW_8_DRIVE_SUPPORT 0x4 #define SW_MAX_ID 0x8 #define SW_DEVICE_CHANGED 0x10 /* parameter setting */ #define PM_UNDER_BIOS 0x8000 #define PM_BIOSSCAN_DISABLE 0x2000 #define PM_NEED_STARTCMD 0x1000 #define PM_DISCON_ENABLE 0x800 #define PM_SYNC_ENABLE 0x400 #define PM_TAG_ENABLE 0x200 #define PM_WIDEBUS_ENABLE 0x100 #define PM_TRANSFER_RATE 0xE0 #define PM_TRANSFER_OFFSET 0x1F /* ScsiFunc */ #define SUPPORT_MORETHAN16M 0x40 #define SUPPORT_RESELECT 0x20 #define SUPPORT_SYNC 0x10 #define SUPPORT_LINKED 0x08 #define SUPPORT_CMDQUEUE 0x02 #define SUPPORT_SFTRE 0x01 #define SUPPORT_WIDEHOST 0x04 #define SUPPORT_PARCHK 0x80 static unsigned int port_base = 0; static unsigned int CFG_BASE = 0; static unsigned int interrupt_level = 0; /* period table static sync_tbl period_tbl[8]= { { 0, 12, 12,}, { 1, 16, 18,}, { 2, 20, 25,}, { 3, 25, 31,}, { 4, 29, 37,}, { 5, 33, 43,}, { 6, 50, 50,}, { 7, 58, 75,}, }; */ /* pointer to scsi host struc for each HA */ /* needs change for multiple HA support */ static struct Scsi_Host *tripace_host; /* Tc2550 mailbox data structures */ static unsigned char tcmbdata[Q_FILE_SIZE + DEVHDR_SIZE + MBOX_SIZE + 64]; /* static alloc of sg table for all tasks */ /* dynamic memory is giving problems */ static unsigned char table[8 * MAXSGENT * MAXSRBS * MAXTARGET + 4]; /* global variables */ /* logical addresses of mailbox in/out,dev header base,task que base */ static unsigned char *startsgptr; static unsigned long pstartsgptr; static u32 mbx_in_base, mbx_out_base, devhdr_base, taskq_base; static u8 TargetID; /* hostadapter structure-as of now we have a single adapter */ Adapter HostAdapter[1]; unsigned char ReqType, targetID, HANumber, Index, HostID; /* variable for term power in case of /T option */ static unsigned short int EN_TERM_PWR = TERM_PWR_EN; static unsigned short fast_clk = 0; static unsigned short par_off = 0; static unsigned short discon = 1; static unsigned short syncflag = 1; static unsigned short tagflag = 0; static int makecode(unsigned, unsigned); static void Init_struc(int); static int download_RISC_code(void); static void init_chip_reg(void); static PRISC_SRB search(PAdapter); u32 insert_bit(u32, short int); static void Get_Base(Adapter *); void tc2550_intr(int, void *, struct pt_regs *); static void internal_done(Scsi_Cmnd *); int tc2550_command(Scsi_Cmnd *); static int tc2550_pci_bios_detect(int *irq, int *iobase) { int error; unsigned char pci_bus, pci_dev_fn; /* PCI bus & device function */ unsigned char pci_irq; /* PCI interrupt line */ unsigned int pci_base; /* PCI I/O base address */ unsigned short pci_vendor, pci_device; /* PCI vendor & device IDs */ /* We will have to change this if more than 1 PCI bus is present and the tripace scsi host is not on the first bus (i.e., a PCI to PCI bridge, which is not supported by bios32 right now anyway). */ pci_bus = 0; for (pci_dev_fn = 0x0; pci_dev_fn < 0xff; pci_dev_fn++) { pcibios_read_config_word(pci_bus, pci_dev_fn, PCI_VENDOR_ID, &pci_vendor); if (pci_vendor == 0x1190) { pcibios_read_config_word(pci_bus, pci_dev_fn, PCI_DEVICE_ID, &pci_device); if (pci_device == 0xc731) { /* Break out once we have the correct device. If othertrip PCI devices are added to this driver we will need to add an or of the other PCI_DEVICE_ID here. */ printk(KERN_INFO "Tripace TC-2550x based PCI SCSI Adapter detected\n"); break; } else { /* If we can't finl an tripace scsi card we give up. */ return 0; } } } /* vendor id not found */ if (pci_device != 0xc731) { printk(KERN_INFO "Tripace TC-2550x - No Host Adapter Detected \n"); return (0); } /* We now have the appropriate device function for the tripace board so we just read the PCI config info from the registers. */ if ((error = pcibios_read_config_dword(pci_bus, pci_dev_fn, PCI_BASE_ADDRESS_0, &pci_base)) || (error = pcibios_read_config_byte(pci_bus, pci_dev_fn, PCI_INTERRUPT_LINE, &pci_irq))) { printk(KERN_ERR "Tripace TC-2550x not initializing" " due to error reading configuration space\n"); return 0; } else { printk(KERN_INFO "TC-2550x PCI: IRQ = %u, I/O base = %X\n", pci_irq, pci_base); /* Now we have the I/O base address and interrupt from the PCI configuration registers. */ *irq = pci_irq; *iobase = (pci_base & 0xfff8); CFG_BASE = *iobase; printk(KERN_INFO "TC-2550x Driver version 1.00.000 (904)\n"); printk(KERN_INFO "TC-2550x: IRQ = %d, I/O base = 0x%X\n", *irq, *iobase); return 1; } return 0; } static void init_chip_reg(void) { int i, val, base; unsigned long tick; outw(HALT_RISC, CONTROL_REG); base = CFG_BASE + 0x20; for (i = 0; i < 16; i++) outw(0, base + i * 2); outw(EN_TERM_PWR, STATUS_INT_REG); outw(RESET_SCSI_BUS, STATUS_INT_REG); udelay(50); /*wait for 50 micro secs */ outw(EN_TERM_PWR, STATUS_INT_REG); val = (HALT_RISC | RISC_CHIP_RESET); outw(val, CONTROL_REG); outw(HALT_RISC, CONTROL_REG); val = inw(STATUS_INT_REG); tick = 0; while ((val & 0x40) == 0 && tick < 0x3fff) { val = inw(STATUS_INT_REG); tick += 1; }; if (tick == 0x3fff) printk(KERN_DEBUG "val= %x \n\r", val); outw(EN_TERM_PWR, STATUS_INT_REG); outw(0, SCSI_CONTROL); /* delay for 50 micro secs */ udelay(50); } static int download_RISC_code(void) { unsigned short i, j, fast = 0; unsigned short hi, low, base; long tmp; unsigned long start_time; i = inw(CONTROL_REG); if (i & 0x40) fast = 1; outw(HALT_RISC, CONTROL_REG); /* Ravi modified for sanity check dec 17 1998 */ start_time = jiffies; do { if ((jiffies - start_time) > 5 * HZ) { printk(KERN_ERR "tc2550: Download failure.\n"); return 1; } i = inw(STATUS_INT_REG); } while ((i & 0x40) == 0); outw(HALT_RISC + EN_MEMORY_WRITE, CONTROL_REG); outw(EN_TERM_PWR + DIS_INTR, STATUS_INT_REG); /* download load RISC code */ outw(0, PC); for (i = 0; i < ucode_size; i++) outl(ucode_instruction[i], CURRENT_INST); /* // checksum checking (word) */ base = 0; for (i = 0; i < ucode_size; i++) { outw(i * 4, PC); tmp = inl(PROGRAM_DATA_REG); hi = tmp >> 16; low = (tmp & 0xffff); base = base + (hi + low); } outw(HALT_RISC + RISC_CHIP_RESET, CONTROL_REG); outw(HALT_RISC, CONTROL_REG); if (fast_clk) outw((HALT_RISC | FAST_CLOCK), CONTROL_REG); if (par_off) { i = inw(CONTROL_REG); outw((i | DIS_SCSI_PARITY), CONTROL_REG); } outw(EN_TERM_PWR, STATUS_INT_REG); /*ravi */ if ((unsigned short) (ucode_checksum + base) != 0) { printk(KERN_ERR "tc2550: Checksum Error During Code Download\n"); return (1); }; /* load vector table */ j = 0; base = CFG_BASE; for (i = 0, j = 0; i < 15; i++, j = j + 2) outw(ucode_vector[i], (base + j)); outw(0, SCSI_CONTROL); return (0); } int tc2550_detect(Scsi_Host_Template * tpnt) { int flag = 0; int retcode; struct Scsi_Host *shpnt; unsigned long flags; unsigned int mod4; flag = tc2550_pci_bios_detect(&interrupt_level, &port_base); if (!flag) return (0); init_chip_reg(); /* chip Tc-2550 initialize */ flag = download_RISC_code(); if (flag == 0) { printk(KERN_INFO "tc2550: Successful F/W download on TC-2550x\n"); } /* now do a scsi register and get scsi host ptr */ shpnt = scsi_register(tpnt, 0); save_flags(flags); cli(); retcode = request_irq(interrupt_level, tc2550_intr, SA_INTERRUPT, "tripace", NULL); if (retcode) { printk(KERN_ERR "tc2550: Unable to allocate IRQ for Tripace TC-2550x based SCSI Host Adapter.\n"); goto unregister; } /* For multiple HA we need to change all this */ tripace_host = shpnt; shpnt->io_port = CFG_BASE; shpnt->n_io_port = 0xfc; /* Number of bytes of I/O space used */ shpnt->dma_channel = 0; shpnt->irq = interrupt_level; restore_flags(flags); /* log i/o ports with the kernel */ request_region(port_base, 0xfc, "tripace"); /* when we support multiple HA ,we need to modify */ Init_struc(0); /* init mailboxes for one adapter */ /* sg table init */ /* get physical address */ startsgptr = (unsigned char *) table; pstartsgptr = virt_to_phys((unsigned char *) table); mod4 = pstartsgptr % 4; if (mod4) { pstartsgptr += (4 - mod4); startsgptr += (4 - mod4); } return (0); unregister: scsi_unregister(shpnt); return (0); } /**************************************************************************** ** Init chip registers and allocate required memory space ****************************************************************************/ static void Init_struc(int id) { u32 pmbx_in_base, pmbx_out_base, pdevhdr_base, ptaskq_base; unsigned long paddr; char *laddr; unsigned short modulo; /* setup ioport address and irq */ HostAdapter[id].IoPort = (u16) CFG_BASE; HostAdapter[id].IntrNum = (u8) interrupt_level; laddr = tcmbdata; paddr = virt_to_phys(tcmbdata); /* adjust phys address to 32 byte boundary */ modulo = paddr % 32; if (modulo) { paddr = paddr + 32 - modulo; laddr = laddr + 32 - modulo; } /* logical address */ mbx_in_base = (u32) laddr; pmbx_in_base = paddr; HostAdapter[id].mbx_in_base = mbx_in_base; laddr += 32; paddr += 32; mbx_out_base = (u32) laddr; pmbx_out_base = paddr; HostAdapter[id].mbx_out_base = mbx_out_base; memset((char *) mbx_in_base, 0, 48); laddr += 32; paddr += 32; devhdr_base = (u32) laddr; pdevhdr_base = paddr; HostAdapter[id].devhdr_base = devhdr_base; memset((char *) devhdr_base, 0, 512); laddr += 512; paddr += 512; taskq_base = (u32) laddr; ptaskq_base = paddr; HostAdapter[id].taskq_base = taskq_base; memset((char *) taskq_base, 0, Q_FILE_SIZE); HostAdapter[id].ptaskq_base = (u32) ptaskq_base; HostAdapter[id].pdev_base = (u32) pdevhdr_base; HostAdapter[id].pmbi_base = (u32) pmbx_in_base; HostAdapter[id].pmbo_base = (u32) pmbx_out_base; outl(pmbx_in_base, MBX_IN_BASE); outl(pmbx_out_base, MBX_OUT_BASE); outl(pdevhdr_base, DEV_HDR_BASE); outl(ptaskq_base, TASK_Q_BASE); outb(0, MBX_IN_INDEX); outb(0, MBX_OUT_INDEX); /* clear mailbox out pointer */ HostAdapter[id].mbx_out_ptr = 0; } /*************************************************************************** ** adjust bitmap position ** Input : old bitmap, new bit location ** Output: return with new bitmap layout ****************************************************************************/ extern __inline__ u32 insert_bit(u32 bits, short int loc) { u32 lo = 0; lo = 1; lo <<= loc; return ((lo | bits)); } /*************************************************************************** ** Get the base address and structures of current host adapter ** Input : adapter structure pointer ** Output : none ****************************************************************************/ static void Get_Base(Adapter * padapter) { CFG_BASE = padapter->IoPort; interrupt_level = (u16) padapter->IntrNum; mbx_in_base = padapter->mbx_in_base; mbx_out_base = padapter->mbx_out_base; devhdr_base = padapter->devhdr_base; taskq_base = padapter->taskq_base; } /*************************************************************************** ** name : search() ** Desc : search an available SRB from taskQ ** Input : adapter structure pointer ** Output : 1. a risc structure space pointer or 0 for non available ** 2. index of task location ****************************************************************************/ static PRISC_SRB search(PAdapter pa) { PRISC_SRB rsrb; short int i; unsigned long flags; Get_Base(pa); rsrb = (PRISC_SRB) (taskq_base + TargetID * 16 * sizeof(RISC_SRB)); /* check attrib in case drive doesn't support SYNC xfer */ if ((pa->dev[TargetID].attrib & DO_SYNC_NEGO) == 0) ReqType = 0; save_flags(flags); cli(); Index = 0; for (i = 0; i < 16; i++, rsrb++) if (rsrb->SRB_flag == SRB_DONE) break; if (i == 16) { restore_flags(flags); return (0); } Index = i; restore_flags(flags); memset((char *) rsrb, 0, 32); rsrb->SRB_flag = SRB_ASSIGNED; /* mark for use */ if ((pa->dev[TargetID].attrib & 0x44) == 0x44) rsrb->Tag_info = 0x20; return (rsrb); } /*************************************************************************** ** Name : StartSCSICmd() ** func : 1. all commands passed through here are regular ** 2. fill in device structure bitmap && start RISC ** Input : risc srb structure, Index, ReqType ** Output : none ****************************************************************************/ static void StartScsiCmd(PRISC_SRB rsrb) { DevHdr *dev; u16 status; PAdapter pa; unsigned short val; u8 t, find_id, find_last, ch = 0; char *mptr; /* Request sense CDB */ char RequestSense[6] = {0x03, 0x00, 0x00, 0x00, 0x0e, 0}; pa = (PAdapter) & HostAdapter[HANumber]; pa->dev[TargetID].task[Index].CmdInProcess = 1; /* command in process */ dev = (DevHdr *) (devhdr_base + TargetID * sizeof(DevHdr)); dev->Srb_loc = Index; dev->Request_type = ReqType; /* set request type */ dev->Updatedmap = insert_bit(dev->Updatedmap, Index); for (val = 0; val < 6; val++) dev->SenseCmd[val] = RequestSense[val]; rsrb->Sense_Cmd_Ptr = virt_to_phys(&dev->SenseCmd[0]); if (ReqType >= 2) { if (ReqType & 0x2) dev->WideMsg = 0x1; else { /* printf("firing sync nego"); //ravi 10/3/98 -ultra support in parse if(fast_clk) dev->SyncPeriod = period_tbl[(ultra[TargetID])].f_factor; else dev->SyncPeriod = period_tbl[(ultra[TargetID])].s_factor; */ if (pa->dev[TargetID].attrib & 0x1) dev->SyncOffset = W_MAX_OFFSET; else dev->SyncOffset = MAX_OFFSET; } } mptr = (char *) mbx_in_base; find_id = 0xff; find_last = 0xff; for (t = 0; t < 15; t++) { ch = *mptr++; if ((ch & 0xf) == TargetID) { find_id = t; break; } } mptr = (char *) mbx_in_base; for (t = 0; t < 15; t++) { ch = *mptr++; if (ch & 0x80) { find_last = t; break; } } val = inw(STATUS_INT_REG); mptr = (char *) mbx_in_base; if (find_id != 0xff && find_last != 0xff) { if (find_id < find_last) *(char *) (mptr + find_id) |= 0x10; else { *(char *) (mptr + find_last) &= 0x7f; *(char *) (mptr + find_id) |= 0x90; } } else if (find_last != 0xff && find_id == 0xff) { *(char *) (mptr + find_last) &= 0x7f; find_last = find_last + 1; *(char *) (mptr + find_last) = (0x90 | TargetID); } else if (find_last == 0xff) *(char *) mptr = (0x90 | TargetID); ReqType = 0; /* // restart the RISC if it is halted before */ rsrb->SRB_flag = SRB_READY; if (val & RISC_HALT) { outw(ucode_start, PC); /* set pc counter */ status = inw(CONTROL_REG); /* clear halt status */ outw((status & ~HALT_RISC), CONTROL_REG); } #ifdef DEBUG printk(KERN_DEBUG " start scsi issued \n"); #endif } static void internal_done(Scsi_Cmnd * SCpnt) { SCpnt->SCp.Status++; } int tc2550_command(Scsi_Cmnd * SCpnt) { tc2550_queue(SCpnt, internal_done); SCpnt->SCp.Status = 0; while (!SCpnt->SCp.Status) barrier(); return SCpnt->result; } int tc2550_queue(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) { PAdapter pa; PRISC_SRB rsrb; int val; struct scatterlist *sgpnt; ScatGath *riscsgptr; int i; unsigned int nentries; pa = (PAdapter) & HostAdapter[HANumber]; Get_Base(pa); val = 0xaa; TargetID = SCpnt->target; HostID = 7; /* the following code is for corel compatibility */ if (SCpnt->lun != 0 || (TargetID == HostID)) { SCpnt->result = DID_BAD_TARGET << 16; done(SCpnt); return (0); }; /* Error if more than 16 tasks /target if no space is available */ if ((rsrb = search(pa)) == 0) { SCpnt->result = DID_ERROR << 16; done(SCpnt); return (0); }; /*Ravi -modified for hostid 16/12/98 */ outb((u8) ((TargetID << 4) | HostID), SCSI_ID_REG); rsrb->CDBLength = SCpnt->cmd_len; /* get the physical address of CDB */ rsrb->CDB = virt_to_phys((unsigned char *) SCpnt->cmnd); if (discon) rsrb->Identify = 0xC0; else rsrb->Identify = 0x80; /* scatter gather processing */ nentries = SCpnt->use_sg; if (nentries == 0) nentries = 1; rsrb->SGNum = nentries; #ifdef DEBUG printk(KERN_DEBUG "sgentries = %d\n", nentries); #endif /*allocate mem for scatter gather table at 32bit boundary */ SCpnt->host_scribble = startsgptr + 8 * MAXSGENT * MAXSRBS * TargetID; /*(unsigned char *)scsi_malloc(4096); */ sgpnt = (struct scatterlist *) SCpnt->request_buffer; riscsgptr = (PScatGath) (SCpnt->host_scribble); if (riscsgptr == NULL) panic("tripace: unable to allocate DMA memory\n"); /* fill physical address of scatter-gather list */ rsrb->SG_ListPtr = virt_to_phys(SCpnt->host_scribble); rsrb->Cmd_sg_addr = virt_to_phys(rsrb + 4); if (SCpnt->use_sg) { for (i = 0; i < SCpnt->use_sg; i++) { if (sgpnt[i].length == 0 || SCpnt->use_sg > 255) { unsigned char *ptr; printk(KERN_ERR "tc2550: Bad segment list supplied to Tripace.c (%d, %d)\n", SCpnt->use_sg, i); for (i = 0; i < SCpnt->use_sg; i++) { printk(KERN_ERR "%d: %x %x %d\n", i, (unsigned int) sgpnt[i].address, (unsigned int) sgpnt[i].alt_address, sgpnt[i].length); }; printk(KERN_ERR "RISCGPTR %x: ", (unsigned int) riscsgptr); ptr = (unsigned char *) &riscsgptr[i]; for (i = 0; i < 18; i++) printk("%02x ", ptr[i]); panic("Tripace tc-2550x driver!"); }; riscsgptr[i].sg_address = (u32) sgpnt[i].address; riscsgptr[i].sg_length = sgpnt[i].length; }; } else { riscsgptr[0].sg_address = (u32) SCpnt->request_buffer; riscsgptr[0].sg_length = SCpnt->request_bufflen; }; /* fill sense data pointer and len */ rsrb->Sense_len = sizeof(SCpnt->sense_buffer); rsrb->SenseDataPtr = virt_to_phys(SCpnt->sense_buffer); /* store scsi command pointer for use in intr routine */ pa->dev[TargetID].task[Index].REQ_Header = (u8 *) SCpnt; SCpnt->scsi_done = done; /* pa->dev[TargetID].task[Index].complete = CompleteIORequest; */ ReqType = 0; StartScsiCmd(rsrb); return 0; } int tc2550_reset(Scsi_Cmnd * SCpnt) { return 0; } #include "sd.h" int tc2550_biosparam(Scsi_Disk * disk, int dev, int *info_array) { return 0; } int tc2550_abort(Scsi_Cmnd * SCpnt) { return 0; } const char *tc2550_info(struct Scsi_Host *ignore) { return 0; } void tc2550_intr(int irq, void *dev_id, struct pt_regs *regs) { void (*my_done) (Scsi_Cmnd *) = NULL; int val, id, map = 0, tmap, mbx_out_ptr; u8 loc = 0; PRISC_SRB rsrb; DevHdr *dev; char *ptr0; PAdapter padapter; /* int i ; unsigned long flags ; */ unsigned int memsize; Scsi_Cmnd *SCtmp; unsigned devstat = 0; unsigned scsistat = 0; long start_time; #ifdef DEBUG printk("interrupt registered \n"); #endif /* save_flags(flags); cli(); */ /* multiple HA? not supported now! */ HANumber = 0; padapter = (PAdapter) & HostAdapter[HANumber]; Get_Base(padapter); /* disable interrupts */ val = inw(STATUS_INT_REG); udelay(10); val |= 0x20; /* clear interrupt pending */ outw(val, STATUS_INT_REG); udelay(10); val |= 0x22; /* disable interrupt */ outw(val, STATUS_INT_REG); /* // if RISC is in halt state then find out why ? */ tmap = inw(STATUS_INT_REG); /* The following code needs to be added when we intro sync /wide nego */ /* if(tmap & RISC_HALT) { tmap= risc_halt_check(); if(tmap) return(0xff); }; */ mbx_out_ptr = padapter->mbx_out_ptr; val = *(u16 *) (mbx_out_base + mbx_out_ptr); while (val & 0x80) { loc = (char) (val & 0x7f); id = val & 0xff00; id >>= 8; TargetID = id; dev = (DevHdr *) (devhdr_base + TargetID * sizeof(DevHdr)); /*(u16 *)(mbx_out_base+ mbx_out_ptr)= (val& 0xff7f); */ rsrb = (PRISC_SRB) (taskq_base + (id * 16 + loc) * sizeof(RISC_SRB)); Index = loc; devstat = rsrb->DEV_Status; scsistat = rsrb->ScsiStatus; /* (*padapter->dev[TargetID].task[loc].complete)(rsrb); */ *(u16 *) (mbx_out_base + mbx_out_ptr) = (val & 0xff7f); padapter->dev[TargetID].task[loc].CmdInProcess = 0; mbx_out_ptr += 2; if (mbx_out_ptr == 32) mbx_out_ptr = 0; padapter->mbx_out_ptr = mbx_out_ptr; /* * Clear init bimap if no more tasks are waiting */ map = 1; dev->Updatedmap = (dev->Updatedmap ^ (map << loc)); if ((dev->Updatedmap & 0x0000ffff) == 0) { ptr0 = (char *) mbx_in_base; for (map = 0; map < 16; map++) if ((*ptr0 & 0xf) == (char) id) { *ptr0 = (*ptr0 & 0xef); break; } else ptr0++; } val = *(u16 *) (mbx_out_base + mbx_out_ptr); rsrb->SRB_flag = SRB_DONE; /* mark done */ } /* stop RISC if mailbox is empty */ ptr0 = (char *) mbx_in_base; for (map = 0; map < 16; map++) if (*ptr0 & 0x10) break; if (map == 16) { tmap = inw(CONTROL_REG); outw((tmap | HALT_RISC), CONTROL_REG); /*Ravi modified to introduce sanity check&time out dec 16 1998 */ start_time = jiffies; do { if ((start_time - jiffies) > 5 * HZ) { printk(KERN_ERR "tc2550: TC-2550x Controller Failure\n"); return; } tmap = inw(STATUS_INT_REG); } while((tmap & RISC_HALT) == 0); } SCtmp = (Scsi_Cmnd *) padapter->dev[TargetID].task[loc].REQ_Header; if (!SCtmp || !SCtmp->scsi_done) { printk(KERN_ERR "tc2550: Tripace_Intr_Handle: Unexpected Interrupt\n"); return; } memsize = 255 * sizeof(struct _ScatGath) + 4; my_done = SCtmp->scsi_done; /*if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble,4096); */ padapter->dev[TargetID].task[loc].REQ_Header = NULL; SCtmp->result = makecode(devstat, scsistat); /*enable chip interrupt signal */ val = inw(STATUS_INT_REG); udelay(25); /* delay for 25 micros */ val &= 0xfd; /* enable interrupt-bit1=0 in status-int reg */ outw(val, STATUS_INT_REG); my_done(SCtmp); /* inform mid layer that scsi command is over */ /* restore_flags(flags); */ } /* called from init/main.c */ void tripace_setup(char *str, int *ints) { switch (ints[0]) { case 0: printk(KERN_INFO "tc2550: No Arguments In Command Line:Assuming Defaults\n"); break; case 1: fast_clk = ints[1]; break; case 2: fast_clk = ints[1]; discon = ints[2]; break; case 3: fast_clk = ints[1]; discon = ints[2]; syncflag = ints[3]; break; case 4: fast_clk = ints[1]; discon = ints[2]; syncflag = ints[3]; tagflag = ints[4]; } #ifdef DEBUG printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n", fast_clk, discon, syncflag, tagflag); printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n", fast_clk, discon, syncflag, tagflag); printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n", fast_clk, discon, syncflag, tagflag); printk("fast_clk = %d,discon = %d,syncflag =%d,tagflag=%d\n", fast_clk, discon, syncflag, tagflag); #endif } static int makecode(unsigned hosterr, unsigned scsierr) { switch (hosterr) { case 0x0: hosterr = 0; break; case SEL_TIME_OUT: /* Selection time out-The initiator selection or target reselection was not complete within the SCSI Time out period */ hosterr = DID_TIME_OUT; break; case ERR_PARITY: /* parity error */ hosterr = DID_PARITY; break; case ERR_OVERRUN: /* Data overrun/underrun-The target attempted to transfer more data than was allocated by the Data Length field or the sum of the Scatter / Gather Data Length fields. */ case ERR_BUSFREE: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */ case ERR_PHASE: /* Target bus phase sequence failure-An invalid bus phase or bus phase sequence was requested by the target. */ hosterr = DID_ERROR; /* Couldn't find any better */ break; default: hosterr = DID_ERROR; printk(KERN_ERR "tc2550: Makecode: Unknown Hoststatus %x\n", hosterr); break; } return scsierr | (hosterr << 16); }
Go to most recent revision | Compare with Previous | Blame | View Log