2964 lines · 79.7 KB · C
RawDownload
// 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 master

Lines 1–500 of 2964.