linux/drivers/scsi/initio.c

// 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 <[email protected]>
 * Copyright (c) 2004 Christoph Hellwig <[email protected]>
 * 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 <[email protected]>
 * 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

#define i91u_MAXQUEUE
#define i91u_REVID

#ifdef DEBUG_i91u
static unsigned int i91u_debug = DEBUG_DEFAULT;
#endif

static int initio_tag_enable =;

#ifdef DEBUG_i91u
static int setup_debug = 0;
#endif

static void i91uSCBPost(u8 * pHcb, u8 * pScb);

#define DEBUG_INTERRUPT
#define DEBUG_QUEUE
#define DEBUG_STATE
#define INT_DISC

/*--- 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] =;			/*      - CheckSum -            */


static u8 initio_rate_tbl[8] =/* fast 20      */
{};

static void initio_do_pause(unsigned amount)
{}

/*-- 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)
{}


/**
 *	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_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_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)
{}

/**
 *	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)
{}

/**
 *	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)
{}

/**
 *	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)
{}

/**
 *	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)
{}

/**
 *	initio_stop_bm		-	stop bus master
 *	@host: InitIO we are stopping
 *
 *	Stop any pending DMA operation, aborting the DMA if necessary
 */

static void initio_stop_bm(struct initio_host * host)
{}

/**
 *	initio_reset_scsi		-	Reset SCSI host controller
 *	@host: InitIO host to reset
 *	@seconds: Recovery time
 *
 *	Perform a full reset of the SCSI subsystem.
 */

static int initio_reset_scsi(struct initio_host * host, int seconds)
{}

/**
 *	initio_init		-	set up an InitIO host adapter
 *	@host: InitIO host adapter
 *	@bios_addr: BIOS address
 *
 *	Set up the host adapter and devices according to the configuration
 *	retrieved from the E2PROM.
 *
 *	Locking: Calls E2PROM layer code which is not re-enterable so must
 *	run single threaded for now.
 */

static void initio_init(struct initio_host * host, u8 *bios_addr)
{}

/**
 *	initio_alloc_scb		-	Allocate an SCB
 *	@host: InitIO host we are allocating for
 *
 *	Walk the SCB list for the controller and allocate a free SCB if
 *	one exists.
 */
static struct scsi_ctrl_blk *initio_alloc_scb(struct initio_host *host)
{}

/**
 *	initio_release_scb		-	Release an SCB
 *	@host: InitIO host that owns the SCB
 *	@cmnd: SCB command block being returned
 *
 *	Return an allocated SCB to the host free list
 */

static void initio_release_scb(struct initio_host * host, struct scsi_ctrl_blk * cmnd)
{}

/***************************************************************************/
static void initio_append_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{}

/***************************************************************************/
static void initio_push_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{}

static struct scsi_ctrl_blk *initio_find_first_pend_scb(struct initio_host * host)
{}

static void initio_unlink_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
{}

static void initio_append_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{}

/***************************************************************************/
static struct scsi_ctrl_blk *initio_pop_busy_scb(struct initio_host * host)
{}

/***************************************************************************/
static void initio_unlink_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
{}

struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun)
{}

static void initio_append_done_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp)
{}

struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host)
{}

static int initio_abort_srb(struct initio_host * host, struct scsi_cmnd *srbp)
{}

/***************************************************************************/
static int initio_bad_seq(struct initio_host * host)
{}


/************************************************************************/
static void initio_exec_scb(struct initio_host * host, struct scsi_ctrl_blk * scb)
{}

/***************************************************************************/
static int initio_isr(struct initio_host * host)
{}

static int tulip_main(struct initio_host * host)
{}

static void tulip_scsi(struct initio_host * host)
{}

/**
 *	initio_next_state		-	Next SCSI state
 *	@host: InitIO host we are processing
 *
 *	Progress the active command block along the state machine
 *	until we hit a state which we must wait for activity to occur.
 *
 *	Returns zero or a negative code.
 */

static int initio_next_state(struct initio_host * host)
{}


/**
 *	initio_state_1		-	SCSI state machine
 *	@host: InitIO host we are controlling
 *
 *	Perform SCSI state processing for Select/Attention/Stop
 */

static int initio_state_1(struct initio_host * host)
{}


/**
 *	initio_state_2		-	SCSI state machine
 *	@host: InitIO host we are controlling
 *
 * state after selection with attention
 * state after selection with attention3
 */

static int initio_state_2(struct initio_host * host)
{}

/**
 *	initio_state_3		-	SCSI state machine
 *	@host: InitIO host we are controlling
 *
 * state before CDB xfer is done
 */

static int initio_state_3(struct initio_host * host)
{}

/**
 *	initio_state_4		-	SCSI state machine
 *	@host: InitIO host we are controlling
 *
 *	SCSI state machine. State 4
 */

static int initio_state_4(struct initio_host * host)
{}


/**
 *	initio_state_5		-	SCSI state machine
 *	@host: InitIO host we are controlling
 *
 *	State after dma xfer done or phase change before xfer done
 */

static int initio_state_5(struct initio_host * host)
{}

/**
 *	initio_state_6		-	SCSI state machine
 *	@host: InitIO host we are controlling
 *
 *	State after Data phase
 */

static int initio_state_6(struct initio_host * host)
{}

/**
 *	initio_state_7		-	SCSI state machine
 *	@host: InitIO host we are controlling
 *
 */

static int initio_state_7(struct initio_host * host)
{}

/**
 *	initio_xfer_data_in	-	Commence data input
 *	@host: InitIO host in use
 *
 *	Commence a block of data transfer. The transfer itself will
 *	be managed by the controller and we will get a completion (or
 *	failure) interrupt.
 */
static int initio_xfer_data_in(struct initio_host * host)
{}

/**
 *	initio_xfer_data_out	-	Commence data output
 *	@host: InitIO host in use
 *
 *	Commence a block of data transfer. The transfer itself will
 *	be managed by the controller and we will get a completion (or
 *	failure) interrupt.
 */

static int initio_xfer_data_out(struct initio_host * host)
{}

int initio_xpad_in(struct initio_host * host)
{}

int initio_xpad_out(struct initio_host * host)
{}

int initio_status_msg(struct initio_host * host)
{}


/* scsi bus free */
int int_initio_busfree(struct initio_host * host)
{}


/**
 *	int_initio_scsi_rst	-	SCSI reset occurred
 *	@host: Host seeing the reset
 *
 *	A SCSI bus reset has occurred. Clean up any pending transfer
 *	the hardware is doing by DMA and then abort all active and
 *	disconnected commands. The mid layer should sort the rest out
 *	for us
 */

static int int_initio_scsi_rst(struct initio_host * host)
{}

/**
 *	int_initio_resel	-	Reselection occurred
 *	@host: InitIO host adapter
 *
 *	A SCSI reselection event has been signalled and the interrupt
 *	is now being processed. Work out which command block needs attention
 *	and continue processing that command.
 */

int int_initio_resel(struct initio_host * host)
{}

/**
 *	int_initio_bad_seq		-	out of phase
 *	@host: InitIO host flagging event
 *
 *	We have ended up out of phase somehow. Reset the host controller
 *	and throw all our toys out of the pram. Let the midlayer clean up
 */

static int int_initio_bad_seq(struct initio_host * host)
{}


/**
 *	initio_msgout_abort_targ		-	abort a tag
 *	@host: InitIO host
 *
 *	Abort when the target/lun does not match or when our SCB is not
 *	busy. Used by untagged commands.
 */

static int initio_msgout_abort_targ(struct initio_host * host)
{}

/**
 *	initio_msgout_abort_tag		-	abort a tag
 *	@host: InitIO host
 *
 *	Abort when the target/lun does not match or when our SCB is not
 *	busy. Used for tagged commands.
 */

static int initio_msgout_abort_tag(struct initio_host * host)
{}

/**
 *	initio_msgin		-	Message in
 *	@host: InitIO Host
 *
 *	Process incoming message
 */
static int initio_msgin(struct initio_host * host)
{}

static int initio_msgout_reject(struct initio_host * host)
{}

static int initio_msgout_ide(struct initio_host * host)
{}

static int initio_msgin_extend(struct initio_host * host)
{}

static int initio_msgin_sync(struct initio_host * host)
{}

static int wdtr_done(struct initio_host * host)
{}

static int initio_sync_done(struct initio_host * host)
{}


static int initio_post_scsi_rst(struct initio_host * host)
{}

static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb)
{}


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)
{}

/**
 *	initio_bus_device_reset	-	 SCSI Bus Device Reset
 *	@host: InitIO host to reset
 *
 *	Perform a device reset and abort all pending SCBs for the
 *	victim device
 */
int initio_bus_device_reset(struct initio_host * host)
{}

static int initio_msgin_accept(struct initio_host * host)
{}

static int wait_tulip(struct initio_host * host)
{}

static int initio_wait_disc(struct initio_host * host)
{}

static int initio_wait_done_disc(struct initio_host * host)
{}

/**
 *	i91u_intr		-	IRQ handler
 *	@irqno: IRQ number
 *	@dev_id: IRQ identifier
 *
 *	Take the relevant locks and then invoke the actual isr processing
 *	code under the lock.
 */

static irqreturn_t i91u_intr(int irqno, void *dev_id)
{}


/**
 *	initio_build_scb		-	Build the mappings and SCB
 *	@host: InitIO host taking the command
 *	@cblk: Firmware command block
 *	@cmnd: SCSI midlayer command block
 *
 *	Translate the abstract SCSI command into a firmware command block
 *	suitable for feeding to the InitIO host controller. This also requires
 *	we build the scatter gather lists and ensure they are mapped properly.
 */

static void initio_build_scb(struct initio_host * host, struct scsi_ctrl_blk * cblk, struct scsi_cmnd * cmnd)
{}

/**
 *	i91u_queuecommand_lck	-	Queue a new command if possible
 *	@cmd: SCSI command block from the mid layer
 *
 *	Attempts to queue a new command with the host adapter. Will return
 *	zero if successful or indicate a host busy condition if not (which
 *	will cause the mid layer to call us again later with the command)
 */
static int i91u_queuecommand_lck(struct scsi_cmnd *cmd)
{}

static DEF_SCSI_QCMD(i91u_queuecommand)

/**
 *	i91u_bus_reset		-	reset the SCSI bus
 *	@cmnd: Command block we want to trigger the reset for
 *
 *	Initiate a SCSI bus reset sequence
 */

static int i91u_bus_reset(struct scsi_cmnd * cmnd)
{}

/**
 *	i91u_biosparam			-	return the "logical geometry
 *	@sdev: SCSI device
 *	@dev: Matching block device
 *	@capacity: Sector size of drive
 *	@info_array: Return space for BIOS geometry
 *
 *	Map the device geometry in a manner compatible with the host
 *	controller BIOS behaviour.
 *
 *	FIXME: limited to 2^32 sector devices.
 */

static int i91u_biosparam(struct scsi_device *sdev, struct block_device *dev,
		sector_t capacity, int *info_array)
{}

/**
 *	i91u_unmap_scb		-	Unmap a command
 *	@pci_dev: PCI device the command is for
 *	@cmnd: The command itself
 *
 *	Unmap any PCI mapping/IOMMU resources allocated when the command
 *	was mapped originally as part of initio_build_scb
 */

static void i91u_unmap_scb(struct pci_dev *pci_dev, struct scsi_cmnd *cmnd)
{}

/*
 *	i91uSCBPost		-	SCSI callback
 *
 *	This is callback routine be called when tulip finish one
 *	SCSI command.
 */

static void i91uSCBPost(u8 * host_mem, u8 * cblk_mem)
{}

static const struct scsi_host_template initio_template =;

static int initio_probe_one(struct pci_dev *pdev,
	const struct pci_device_id *id)
{}

/**
 *	initio_remove_one	-	control shutdown
 *	@pdev:	PCI device being released
 *
 *	Release the resources assigned to this adapter after it has
 *	finished being used.
 */

static void initio_remove_one(struct pci_dev *pdev)
{}

MODULE_LICENSE();

static struct pci_device_id initio_pci_tbl[] =;
MODULE_DEVICE_TABLE(pci, initio_pci_tbl);

static struct pci_driver initio_pci_driver =;
module_pci_driver();

MODULE_DESCRIPTION();
MODULE_AUTHOR();
MODULE_LICENSE();