// SPDX-License-Identifier: GPL-2.0-or-later/************************************************************************** * Initio 9100 device driver for Linux. * * Copyright (c) 1994-1998 Initio Corporation * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl> * Copyright (c) 2004 Christoph Hellwig <hch@lst.de> * Copyright (c) 2007 Red Hat * ************************************************************************* * * DESCRIPTION: * * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host * adapters * * 08/06/97 hc - v1.01h * - Support inic-940 and inic-935 * 09/26/97 hc - v1.01i * - Make correction from J.W. Schultz suggestion * 10/13/97 hc - Support reset function * 10/21/97 hc - v1.01j * - Support 32 LUN (SCSI 3) * 01/14/98 hc - v1.01k * - Fix memory allocation problem * 03/04/98 hc - v1.01l * - Fix tape rewind which will hang the system problem * - Set can_queue to initio_num_scb * 06/25/98 hc - v1.01m * - Get it work for kernel version >= 2.1.75 * - Dynamic assign SCSI bus reset holding time in initio_init() * 07/02/98 hc - v1.01n * - Support 0002134A * 08/07/98 hc - v1.01o * - Change the initio_abort_srb routine to use scsi_done. <01> * 09/07/98 hl - v1.02 * - Change the INI9100U define and proc_dir_entry to * reflect the newer Kernel 2.1.118, but the v1.o1o * should work with Kernel 2.1.118. * 09/20/98 wh - v1.02a * - Support Abort command. * - Handle reset routine. * 09/21/98 hl - v1.03 * - remove comments. * 12/09/98 bv - v1.03a * - Removed unused code * 12/13/98 bv - v1.03b * - Remove cli() locking for kernels >= 2.1.95. This uses * spinlocks to serialize access to the pSRB_head and * pSRB_tail members of the HCS structure. * 09/01/99 bv - v1.03d * - Fixed a deadlock problem in SMP. * 21/01/99 bv - v1.03e * - Add support for the Domex 3192U PCI SCSI * This is a slightly modified patch by * Brian Macy <bmacy@sunshinecomputing.com> * 22/02/99 bv - v1.03f * - Didn't detect the INIC-950 in 2.0.x correctly. * Now fixed. * 05/07/99 bv - v1.03g * - Changed the assumption that HZ = 100 * 10/17/03 mc - v1.04 * - added new DMA API support * 06/01/04 jmd - v1.04a * - Re-add reset_bus support **************************************************************************/ #include <linux/module.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/spinlock.h>#include <linux/stat.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/string.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/dma-mapping.h>#include <asm/io.h> #include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_tcq.h> #include "initio.h" #define SENSE_SIZE 14 #define i91u_MAXQUEUE 2#define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a" #ifdef DEBUG_i91ustatic unsigned int i91u_debug = DEBUG_DEFAULT;#endif static int initio_tag_enable = 1; #ifdef DEBUG_i91ustatic int setup_debug = 0;#endif static void i91uSCBPost(u8 * pHcb, u8 * pScb); #define DEBUG_INTERRUPT 0#define DEBUG_QUEUE 0#define DEBUG_STATE 0#define INT_DISC 0 /*--- forward references ---*/static struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun);static struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host); static int tulip_main(struct initio_host * host); static int initio_next_state(struct initio_host * host);static int initio_state_1(struct initio_host * host);static int initio_state_2(struct initio_host * host);static int initio_state_3(struct initio_host * host);static int initio_state_4(struct initio_host * host);static int initio_state_5(struct initio_host * host);static int initio_state_6(struct initio_host * host);static int initio_state_7(struct initio_host * host);static int initio_xfer_data_in(struct initio_host * host);static int initio_xfer_data_out(struct initio_host * host);static int initio_xpad_in(struct initio_host * host);static int initio_xpad_out(struct initio_host * host);static int initio_status_msg(struct initio_host * host); static int initio_msgin(struct initio_host * host);static int initio_msgin_sync(struct initio_host * host);static int initio_msgin_accept(struct initio_host * host);static int initio_msgout_reject(struct initio_host * host);static int initio_msgin_extend(struct initio_host * host); static int initio_msgout_ide(struct initio_host * host);static int initio_msgout_abort_targ(struct initio_host * host);static int initio_msgout_abort_tag(struct initio_host * host); static int initio_bus_device_reset(struct initio_host * host);static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb);static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb);static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb);static int int_initio_busfree(struct initio_host * host);static int int_initio_scsi_rst(struct initio_host * host);static int int_initio_bad_seq(struct initio_host * host);static int int_initio_resel(struct initio_host * host);static int initio_sync_done(struct initio_host * host);static int wdtr_done(struct initio_host * host);static int wait_tulip(struct initio_host * host);static int initio_wait_done_disc(struct initio_host * host);static int initio_wait_disc(struct initio_host * host);static void tulip_scsi(struct initio_host * host);static int initio_post_scsi_rst(struct initio_host * host); static void initio_se2_ew_en(unsigned long base);static void initio_se2_ew_ds(unsigned long base);static int initio_se2_rd_all(unsigned long base);static void initio_se2_update_all(unsigned long base); /* setup default pattern */static void initio_read_eeprom(unsigned long base); /* ---- INTERNAL VARIABLES ---- */ static NVRAM i91unvram;static NVRAM *i91unvramp; static u8 i91udftNvRam[64] ={ /*----------- header -----------*/ 0x25, 0xc9, /* Signature */ 0x40, /* Size */ 0x01, /* Revision */ /* -- Host Adapter Structure -- */ 0x95, /* ModelByte0 */ 0x00, /* ModelByte1 */ 0x00, /* ModelInfo */ 0x01, /* NumOfCh */ NBC1_DEFAULT, /* BIOSConfig1 */ 0, /* BIOSConfig2 */ 0, /* HAConfig1 */ 0, /* HAConfig2 */ /* SCSI channel 0 and target Structure */ 7, /* SCSIid */ NCC1_DEFAULT, /* SCSIconfig1 */ 0, /* SCSIconfig2 */ 0x10, /* NumSCSItarget */ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, /* SCSI channel 1 and target Structure */ 7, /* SCSIid */ NCC1_DEFAULT, /* SCSIconfig1 */ 0, /* SCSIconfig2 */ 0x10, /* NumSCSItarget */ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* - CheckSum - */ static u8 initio_rate_tbl[8] = /* fast 20 */{ /* nanosecond divide by 4 */ 12, /* 50ns, 20M */ 18, /* 75ns, 13.3M */ 25, /* 100ns, 10M */ 31, /* 125ns, 8M */ 37, /* 150ns, 6.6M */ 43, /* 175ns, 5.7M */ 50, /* 200ns, 5M */ 62 /* 250ns, 4M */}; static void initio_do_pause(unsigned amount){ /* Pause for amount jiffies */ unsigned long the_time = jiffies + amount; while (time_before_eq(jiffies, the_time)) cpu_relax();} /*-- forward reference --*/ /****************************************************************** Input: instruction for Serial E2PROM EX: se2_rd(0 call se2_instr() to send address and read command StartBit OP_Code Address Data --------- -------- ------------------ ------- 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0 +----------------------------------------------------- | CS -----+ +--+ +--+ +--+ +--+ +--+ ^ | ^ | ^ | ^ | ^ | | | | | | | | | | | CLK -------+ +--+ +--+ +--+ +--+ +-- (leading edge trigger) +--1-----1--+ | SB OP | OP A5 A4 DI ----+ +--0------------------ (address and cmd sent to nvram) -------------------------------------------+ | DO +--- (data sent from nvram) ******************************************************************/ /** * initio_se2_instr - bitbang an instruction * @base: Base of InitIO controller * @instr: Instruction for serial E2PROM * * Bitbang an instruction out to the serial E2Prom */ static void initio_se2_instr(unsigned long base, u8 instr){ int i; u8 b; outb(SE2CS | SE2DO, base + TUL_NVRAM); /* cs+start bit */ udelay(30); outb(SE2CS | SE2CLK | SE2DO, base + TUL_NVRAM); /* +CLK */ udelay(30); for (i = 0; i < 8; i++) { if (instr & 0x80) b = SE2CS | SE2DO; /* -CLK+dataBit */ else b = SE2CS; /* -CLK */ outb(b, base + TUL_NVRAM); udelay(30); outb(b | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); instr <<= 1; } outb(SE2CS, base + TUL_NVRAM); /* -CLK */ udelay(30);} /** * initio_se2_ew_en - Enable erase/write * @base: Base address of InitIO controller * * Enable erase/write state of serial EEPROM */void initio_se2_ew_en(unsigned long base){ initio_se2_instr(base, 0x30); /* EWEN */ outb(0, base + TUL_NVRAM); /* -CS */ udelay(30);} /** * initio_se2_ew_ds - Disable erase/write * @base: Base address of InitIO controller * * Disable erase/write state of serial EEPROM */void initio_se2_ew_ds(unsigned long base){ initio_se2_instr(base, 0); /* EWDS */ outb(0, base + TUL_NVRAM); /* -CS */ udelay(30);} /** * initio_se2_rd - read E2PROM word * @base: Base of InitIO controller * @addr: Address of word in E2PROM * * Read a word from the NV E2PROM device */static u16 initio_se2_rd(unsigned long base, u8 addr){ u8 instr, rb; u16 val = 0; int i; instr = (u8) (addr | 0x80); initio_se2_instr(base, instr); /* READ INSTR */ for (i = 15; i >= 0; i--) { outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); outb(SE2CS, base + TUL_NVRAM); /* -CLK */ /* sample data after the following edge of clock */ rb = inb(base + TUL_NVRAM); rb &= SE2DI; val += (rb << i); udelay(30); /* 6/20/95 */ } outb(0, base + TUL_NVRAM); /* no chip select */ udelay(30); return val;} /** * initio_se2_wr - read E2PROM word * @base: Base of InitIO controller * @addr: Address of word in E2PROM * @val: Value to write * * Write a word to the NV E2PROM device. Used when recovering from * a problem with the NV. */static void initio_se2_wr(unsigned long base, u8 addr, u16 val){ u8 instr; int i; instr = (u8) (addr | 0x40); initio_se2_instr(base, instr); /* WRITE INSTR */ for (i = 15; i >= 0; i--) { if (val & 0x8000) outb(SE2CS | SE2DO, base + TUL_NVRAM); /* -CLK+dataBit 1 */ else outb(SE2CS, base + TUL_NVRAM); /* -CLK+dataBit 0 */ udelay(30); outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); val <<= 1; } outb(SE2CS, base + TUL_NVRAM); /* -CLK */ udelay(30); outb(0, base + TUL_NVRAM); /* -CS */ udelay(30); outb(SE2CS, base + TUL_NVRAM); /* +CS */ udelay(30); for (;;) { outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); outb(SE2CS, base + TUL_NVRAM); /* -CLK */ udelay(30); if (inb(base + TUL_NVRAM) & SE2DI) break; /* write complete */ } outb(0, base + TUL_NVRAM); /* -CS */} /** * initio_se2_rd_all - read hostadapter NV configuration * @base: Base address of InitIO controller * * Reads the E2PROM data into main memory. Ensures that the checksum * and header marker are valid. Returns 1 on success -1 on error. */ static int initio_se2_rd_all(unsigned long base){ int i; u16 chksum = 0; u16 *np; i91unvramp = &i91unvram; np = (u16 *) i91unvramp; for (i = 0; i < 32; i++) *np++ = initio_se2_rd(base, i); /* Is signature "ini" ok ? */ if (i91unvramp->NVM_Signature != INI_SIGNATURE) return -1; /* Is ckecksum ok ? */ np = (u16 *) i91unvramp; for (i = 0; i < 31; i++) chksum += *np++; if (i91unvramp->NVM_CheckSum != chksum) return -1; return 1;} /** * initio_se2_update_all - Update E2PROM * @base: Base of InitIO controller * * Update the E2PROM by wrting any changes into the E2PROM * chip, rewriting the checksum. */static void initio_se2_update_all(unsigned long base){ /* setup default pattern */ int i; u16 chksum = 0; u16 *np, *np1; i91unvramp = &i91unvram; /* Calculate checksum first */ np = (u16 *) i91udftNvRam; for (i = 0; i < 31; i++) chksum += *np++; *np = chksum; initio_se2_ew_en(base); /* Enable write */ np = (u16 *) i91udftNvRam; np1 = (u16 *) i91unvramp; for (i = 0; i < 32; i++, np++, np1++) { if (*np != *np1) initio_se2_wr(base, i, *np); } initio_se2_ew_ds(base); /* Disable write */} /** * initio_read_eeprom - Retrieve configuration * @base: Base of InitIO Host Adapter * * Retrieve the host adapter configuration data from E2Prom. If the * data is invalid then the defaults are used and are also restored * into the E2PROM. This forms the access point for the SCSI driver * into the E2PROM layer, the other functions for the E2PROM are all * internal use. * * Must be called single threaded, uses a shared global area. */ static void initio_read_eeprom(unsigned long base){ u8 gctrl; i91unvramp = &i91unvram; /* Enable EEProm programming */ gctrl = inb(base + TUL_GCTRL); outb(gctrl | TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL); if (initio_se2_rd_all(base) != 1) { initio_se2_update_all(base); /* setup default pattern */ initio_se2_rd_all(base); /* load again */ } /* Disable EEProm programming */ gctrl = inb(base + TUL_GCTRL); outb(gctrl & ~TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL);} /** * initio_stop_bm - stop bus masterLines 1–500 of 2964. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /************************************************************************** * Initio 9100 device driver for Linux. * * Copyright (c) 1994-1998 Initio Corporation * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl> * Copyright (c) 2004 Christoph Hellwig <hch@lst.de> * Copyright (c) 2007 Red Hat * ************************************************************************* * * DESCRIPTION: * * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host * adapters * * 08/06/97 hc - v1.01h * - Support inic-940 and inic-935 * 09/26/97 hc - v1.01i * - Make correction from J.W. Schultz suggestion * 10/13/97 hc - Support reset function * 10/21/97 hc - v1.01j * - Support 32 LUN (SCSI 3) * 01/14/98 hc - v1.01k * - Fix memory allocation problem * 03/04/98 hc - v1.01l * - Fix tape rewind which will hang the system problem * - Set can_queue to initio_num_scb * 06/25/98 hc - v1.01m * - Get it work for kernel version >= 2.1.75 * - Dynamic assign SCSI bus reset holding time in initio_init() * 07/02/98 hc - v1.01n * - Support 0002134A * 08/07/98 hc - v1.01o * - Change the initio_abort_srb routine to use scsi_done. <01> * 09/07/98 hl - v1.02 * - Change the INI9100U define and proc_dir_entry to * reflect the newer Kernel 2.1.118, but the v1.o1o * should work with Kernel 2.1.118. * 09/20/98 wh - v1.02a * - Support Abort command. * - Handle reset routine. * 09/21/98 hl - v1.03 * - remove comments. * 12/09/98 bv - v1.03a * - Removed unused code * 12/13/98 bv - v1.03b * - Remove cli() locking for kernels >= 2.1.95. This uses * spinlocks to serialize access to the pSRB_head and * pSRB_tail members of the HCS structure. * 09/01/99 bv - v1.03d * - Fixed a deadlock problem in SMP. * 21/01/99 bv - v1.03e * - Add support for the Domex 3192U PCI SCSI * This is a slightly modified patch by * Brian Macy <bmacy@sunshinecomputing.com> * 22/02/99 bv - v1.03f * - Didn't detect the INIC-950 in 2.0.x correctly. * Now fixed. * 05/07/99 bv - v1.03g * - Changed the assumption that HZ = 100 * 10/17/03 mc - v1.04 * - added new DMA API support * 06/01/04 jmd - v1.04a * - Re-add reset_bus support **************************************************************************/ #include <linux/module.h> #include <linux/errno.h> #include <linux/delay.h> #include <linux/pci.h> #include <linux/init.h> #include <linux/blkdev.h> #include <linux/spinlock.h> #include <linux/stat.h> #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/string.h> #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/dma-mapping.h> #include <asm/io.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> #include "initio.h" #define SENSE_SIZE 14 #define i91u_MAXQUEUE 2 #define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a" #ifdef DEBUG_i91u static unsigned int i91u_debug = DEBUG_DEFAULT; #endif static int initio_tag_enable = 1; #ifdef DEBUG_i91u static int setup_debug = 0; #endif static void i91uSCBPost(u8 * pHcb, u8 * pScb); #define DEBUG_INTERRUPT 0 #define DEBUG_QUEUE 0 #define DEBUG_STATE 0 #define INT_DISC 0 /*--- forward references ---*/ static struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun); static struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host); static int tulip_main(struct initio_host * host); static int initio_next_state(struct initio_host * host); static int initio_state_1(struct initio_host * host); static int initio_state_2(struct initio_host * host); static int initio_state_3(struct initio_host * host); static int initio_state_4(struct initio_host * host); static int initio_state_5(struct initio_host * host); static int initio_state_6(struct initio_host * host); static int initio_state_7(struct initio_host * host); static int initio_xfer_data_in(struct initio_host * host); static int initio_xfer_data_out(struct initio_host * host); static int initio_xpad_in(struct initio_host * host); static int initio_xpad_out(struct initio_host * host); static int initio_status_msg(struct initio_host * host); static int initio_msgin(struct initio_host * host); static int initio_msgin_sync(struct initio_host * host); static int initio_msgin_accept(struct initio_host * host); static int initio_msgout_reject(struct initio_host * host); static int initio_msgin_extend(struct initio_host * host); static int initio_msgout_ide(struct initio_host * host); static int initio_msgout_abort_targ(struct initio_host * host); static int initio_msgout_abort_tag(struct initio_host * host); static int initio_bus_device_reset(struct initio_host * host); static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb); static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb); static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb); static int int_initio_busfree(struct initio_host * host); static int int_initio_scsi_rst(struct initio_host * host); static int int_initio_bad_seq(struct initio_host * host); static int int_initio_resel(struct initio_host * host); static int initio_sync_done(struct initio_host * host); static int wdtr_done(struct initio_host * host); static int wait_tulip(struct initio_host * host); static int initio_wait_done_disc(struct initio_host * host); static int initio_wait_disc(struct initio_host * host); static void tulip_scsi(struct initio_host * host); static int initio_post_scsi_rst(struct initio_host * host); static void initio_se2_ew_en(unsigned long base); static void initio_se2_ew_ds(unsigned long base); static int initio_se2_rd_all(unsigned long base); static void initio_se2_update_all(unsigned long base); /* setup default pattern */ static void initio_read_eeprom(unsigned long base); /* ---- INTERNAL VARIABLES ---- */ static NVRAM i91unvram; static NVRAM *i91unvramp; static u8 i91udftNvRam[64] = { /*----------- header -----------*/ 0x25, 0xc9, /* Signature */ 0x40, /* Size */ 0x01, /* Revision */ /* -- Host Adapter Structure -- */ 0x95, /* ModelByte0 */ 0x00, /* ModelByte1 */ 0x00, /* ModelInfo */ 0x01, /* NumOfCh */ NBC1_DEFAULT, /* BIOSConfig1 */ 0, /* BIOSConfig2 */ 0, /* HAConfig1 */ 0, /* HAConfig2 */ /* SCSI channel 0 and target Structure */ 7, /* SCSIid */ NCC1_DEFAULT, /* SCSIconfig1 */ 0, /* SCSIconfig2 */ 0x10, /* NumSCSItarget */ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, /* SCSI channel 1 and target Structure */ 7, /* SCSIid */ NCC1_DEFAULT, /* SCSIconfig1 */ 0, /* SCSIconfig2 */ 0x10, /* NumSCSItarget */ NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* - CheckSum - */ static u8 initio_rate_tbl[8] = /* fast 20 */ { /* nanosecond divide by 4 */ 12, /* 50ns, 20M */ 18, /* 75ns, 13.3M */ 25, /* 100ns, 10M */ 31, /* 125ns, 8M */ 37, /* 150ns, 6.6M */ 43, /* 175ns, 5.7M */ 50, /* 200ns, 5M */ 62 /* 250ns, 4M */ }; static void initio_do_pause(unsigned amount) { /* Pause for amount jiffies */ unsigned long the_time = jiffies + amount; while (time_before_eq(jiffies, the_time)) cpu_relax(); } /*-- forward reference --*/ /****************************************************************** Input: instruction for Serial E2PROM EX: se2_rd(0 call se2_instr() to send address and read command StartBit OP_Code Address Data --------- -------- ------------------ ------- 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0 +----------------------------------------------------- | CS -----+ +--+ +--+ +--+ +--+ +--+ ^ | ^ | ^ | ^ | ^ | | | | | | | | | | | CLK -------+ +--+ +--+ +--+ +--+ +-- (leading edge trigger) +--1-----1--+ | SB OP | OP A5 A4 DI ----+ +--0------------------ (address and cmd sent to nvram) -------------------------------------------+ | DO +--- (data sent from nvram) ******************************************************************/ /** * initio_se2_instr - bitbang an instruction * @base: Base of InitIO controller * @instr: Instruction for serial E2PROM * * Bitbang an instruction out to the serial E2Prom */ static void initio_se2_instr(unsigned long base, u8 instr) { int i; u8 b; outb(SE2CS | SE2DO, base + TUL_NVRAM); /* cs+start bit */ udelay(30); outb(SE2CS | SE2CLK | SE2DO, base + TUL_NVRAM); /* +CLK */ udelay(30); for (i = 0; i < 8; i++) { if (instr & 0x80) b = SE2CS | SE2DO; /* -CLK+dataBit */ else b = SE2CS; /* -CLK */ outb(b, base + TUL_NVRAM); udelay(30); outb(b | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); instr <<= 1; } outb(SE2CS, base + TUL_NVRAM); /* -CLK */ udelay(30); } /** * initio_se2_ew_en - Enable erase/write * @base: Base address of InitIO controller * * Enable erase/write state of serial EEPROM */ void initio_se2_ew_en(unsigned long base) { initio_se2_instr(base, 0x30); /* EWEN */ outb(0, base + TUL_NVRAM); /* -CS */ udelay(30); } /** * initio_se2_ew_ds - Disable erase/write * @base: Base address of InitIO controller * * Disable erase/write state of serial EEPROM */ void initio_se2_ew_ds(unsigned long base) { initio_se2_instr(base, 0); /* EWDS */ outb(0, base + TUL_NVRAM); /* -CS */ udelay(30); } /** * initio_se2_rd - read E2PROM word * @base: Base of InitIO controller * @addr: Address of word in E2PROM * * Read a word from the NV E2PROM device */ static u16 initio_se2_rd(unsigned long base, u8 addr) { u8 instr, rb; u16 val = 0; int i; instr = (u8) (addr | 0x80); initio_se2_instr(base, instr); /* READ INSTR */ for (i = 15; i >= 0; i--) { outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); outb(SE2CS, base + TUL_NVRAM); /* -CLK */ /* sample data after the following edge of clock */ rb = inb(base + TUL_NVRAM); rb &= SE2DI; val += (rb << i); udelay(30); /* 6/20/95 */ } outb(0, base + TUL_NVRAM); /* no chip select */ udelay(30); return val; } /** * initio_se2_wr - read E2PROM word * @base: Base of InitIO controller * @addr: Address of word in E2PROM * @val: Value to write * * Write a word to the NV E2PROM device. Used when recovering from * a problem with the NV. */ static void initio_se2_wr(unsigned long base, u8 addr, u16 val) { u8 instr; int i; instr = (u8) (addr | 0x40); initio_se2_instr(base, instr); /* WRITE INSTR */ for (i = 15; i >= 0; i--) { if (val & 0x8000) outb(SE2CS | SE2DO, base + TUL_NVRAM); /* -CLK+dataBit 1 */ else outb(SE2CS, base + TUL_NVRAM); /* -CLK+dataBit 0 */ udelay(30); outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); val <<= 1; } outb(SE2CS, base + TUL_NVRAM); /* -CLK */ udelay(30); outb(0, base + TUL_NVRAM); /* -CS */ udelay(30); outb(SE2CS, base + TUL_NVRAM); /* +CS */ udelay(30); for (;;) { outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ udelay(30); outb(SE2CS, base + TUL_NVRAM); /* -CLK */ udelay(30); if (inb(base + TUL_NVRAM) & SE2DI) break; /* write complete */ } outb(0, base + TUL_NVRAM); /* -CS */ } /** * initio_se2_rd_all - read hostadapter NV configuration * @base: Base address of InitIO controller * * Reads the E2PROM data into main memory. Ensures that the checksum * and header marker are valid. Returns 1 on success -1 on error. */ static int initio_se2_rd_all(unsigned long base) { int i; u16 chksum = 0; u16 *np; i91unvramp = &i91unvram; np = (u16 *) i91unvramp; for (i = 0; i < 32; i++) *np++ = initio_se2_rd(base, i); /* Is signature "ini" ok ? */ if (i91unvramp->NVM_Signature != INI_SIGNATURE) return -1; /* Is ckecksum ok ? */ np = (u16 *) i91unvramp; for (i = 0; i < 31; i++) chksum += *np++; if (i91unvramp->NVM_CheckSum != chksum) return -1; return 1; } /** * initio_se2_update_all - Update E2PROM * @base: Base of InitIO controller * * Update the E2PROM by wrting any changes into the E2PROM * chip, rewriting the checksum. */ static void initio_se2_update_all(unsigned long base) { /* setup default pattern */ int i; u16 chksum = 0; u16 *np, *np1; i91unvramp = &i91unvram; /* Calculate checksum first */ np = (u16 *) i91udftNvRam; for (i = 0; i < 31; i++) chksum += *np++; *np = chksum; initio_se2_ew_en(base); /* Enable write */ np = (u16 *) i91udftNvRam; np1 = (u16 *) i91unvramp; for (i = 0; i < 32; i++, np++, np1++) { if (*np != *np1) initio_se2_wr(base, i, *np); } initio_se2_ew_ds(base); /* Disable write */ } /** * initio_read_eeprom - Retrieve configuration * @base: Base of InitIO Host Adapter * * Retrieve the host adapter configuration data from E2Prom. If the * data is invalid then the defaults are used and are also restored * into the E2PROM. This forms the access point for the SCSI driver * into the E2PROM layer, the other functions for the E2PROM are all * internal use. * * Must be called single threaded, uses a shared global area. */ static void initio_read_eeprom(unsigned long base) { u8 gctrl; i91unvramp = &i91unvram; /* Enable EEProm programming */ gctrl = inb(base + TUL_GCTRL); outb(gctrl | TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL); if (initio_se2_rd_all(base) != 1) { initio_se2_update_all(base); /* setup default pattern */ initio_se2_rd_all(base); /* load again */ } /* Disable EEProm programming */ gctrl = inb(base + TUL_GCTRL); outb(gctrl & ~TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL); } /** * initio_stop_bm - stop bus master