11da177e4SLinus Torvalds /************************************************************************** 21da177e4SLinus Torvalds * Initio 9100 device driver for Linux. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 1994-1998 Initio Corporation 51da177e4SLinus Torvalds * Copyright (c) 1998 Bas Vermeulen <bvermeul@blackstar.xs4all.nl> 672d39feaSAlan Cox * Copyright (c) 2004 Christoph Hellwig <hch@lst.de> 7fa195afeSAlan Cox * Copyright (c) 2007 Red Hat 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 101da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 111da177e4SLinus Torvalds * the Free Software Foundation; either version 2, or (at your option) 121da177e4SLinus Torvalds * any later version. 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 151da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 161da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 171da177e4SLinus Torvalds * GNU General Public License for more details. 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 201da177e4SLinus Torvalds * along with this program; see the file COPYING. If not, write to 211da177e4SLinus Torvalds * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds ************************************************************************* 251da177e4SLinus Torvalds * 261da177e4SLinus Torvalds * DESCRIPTION: 271da177e4SLinus Torvalds * 281da177e4SLinus Torvalds * This is the Linux low-level SCSI driver for Initio INI-9X00U/UW SCSI host 291da177e4SLinus Torvalds * adapters 301da177e4SLinus Torvalds * 311da177e4SLinus Torvalds * 08/06/97 hc - v1.01h 321da177e4SLinus Torvalds * - Support inic-940 and inic-935 331da177e4SLinus Torvalds * 09/26/97 hc - v1.01i 341da177e4SLinus Torvalds * - Make correction from J.W. Schultz suggestion 351da177e4SLinus Torvalds * 10/13/97 hc - Support reset function 361da177e4SLinus Torvalds * 10/21/97 hc - v1.01j 371da177e4SLinus Torvalds * - Support 32 LUN (SCSI 3) 381da177e4SLinus Torvalds * 01/14/98 hc - v1.01k 391da177e4SLinus Torvalds * - Fix memory allocation problem 401da177e4SLinus Torvalds * 03/04/98 hc - v1.01l 411da177e4SLinus Torvalds * - Fix tape rewind which will hang the system problem 4272d39feaSAlan Cox * - Set can_queue to initio_num_scb 431da177e4SLinus Torvalds * 06/25/98 hc - v1.01m 441da177e4SLinus Torvalds * - Get it work for kernel version >= 2.1.75 4572d39feaSAlan Cox * - Dynamic assign SCSI bus reset holding time in initio_init() 461da177e4SLinus Torvalds * 07/02/98 hc - v1.01n 471da177e4SLinus Torvalds * - Support 0002134A 481da177e4SLinus Torvalds * 08/07/98 hc - v1.01o 4972d39feaSAlan Cox * - Change the initio_abort_srb routine to use scsi_done. <01> 501da177e4SLinus Torvalds * 09/07/98 hl - v1.02 511da177e4SLinus Torvalds * - Change the INI9100U define and proc_dir_entry to 521da177e4SLinus Torvalds * reflect the newer Kernel 2.1.118, but the v1.o1o 531da177e4SLinus Torvalds * should work with Kernel 2.1.118. 541da177e4SLinus Torvalds * 09/20/98 wh - v1.02a 551da177e4SLinus Torvalds * - Support Abort command. 561da177e4SLinus Torvalds * - Handle reset routine. 571da177e4SLinus Torvalds * 09/21/98 hl - v1.03 581da177e4SLinus Torvalds * - remove comments. 591da177e4SLinus Torvalds * 12/09/98 bv - v1.03a 601da177e4SLinus Torvalds * - Removed unused code 611da177e4SLinus Torvalds * 12/13/98 bv - v1.03b 621da177e4SLinus Torvalds * - Remove cli() locking for kernels >= 2.1.95. This uses 631da177e4SLinus Torvalds * spinlocks to serialize access to the pSRB_head and 641da177e4SLinus Torvalds * pSRB_tail members of the HCS structure. 651da177e4SLinus Torvalds * 09/01/99 bv - v1.03d 661da177e4SLinus Torvalds * - Fixed a deadlock problem in SMP. 671da177e4SLinus Torvalds * 21/01/99 bv - v1.03e 681da177e4SLinus Torvalds * - Add support for the Domex 3192U PCI SCSI 691da177e4SLinus Torvalds * This is a slightly modified patch by 701da177e4SLinus Torvalds * Brian Macy <bmacy@sunshinecomputing.com> 711da177e4SLinus Torvalds * 22/02/99 bv - v1.03f 721da177e4SLinus Torvalds * - Didn't detect the INIC-950 in 2.0.x correctly. 731da177e4SLinus Torvalds * Now fixed. 741da177e4SLinus Torvalds * 05/07/99 bv - v1.03g 751da177e4SLinus Torvalds * - Changed the assumption that HZ = 100 761da177e4SLinus Torvalds * 10/17/03 mc - v1.04 771da177e4SLinus Torvalds * - added new DMA API support 781da177e4SLinus Torvalds * 06/01/04 jmd - v1.04a 791da177e4SLinus Torvalds * - Re-add reset_bus support 801da177e4SLinus Torvalds **************************************************************************/ 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds #include <linux/module.h> 831da177e4SLinus Torvalds #include <linux/errno.h> 841da177e4SLinus Torvalds #include <linux/delay.h> 851da177e4SLinus Torvalds #include <linux/pci.h> 861da177e4SLinus Torvalds #include <linux/init.h> 871da177e4SLinus Torvalds #include <linux/blkdev.h> 881da177e4SLinus Torvalds #include <linux/spinlock.h> 891da177e4SLinus Torvalds #include <linux/stat.h> 901da177e4SLinus Torvalds #include <linux/kernel.h> 911da177e4SLinus Torvalds #include <linux/proc_fs.h> 921da177e4SLinus Torvalds #include <linux/string.h> 931da177e4SLinus Torvalds #include <linux/interrupt.h> 941da177e4SLinus Torvalds #include <linux/ioport.h> 951da177e4SLinus Torvalds #include <linux/slab.h> 961da177e4SLinus Torvalds #include <linux/jiffies.h> 97910638aeSMatthias Gehre #include <linux/dma-mapping.h> 981da177e4SLinus Torvalds #include <asm/io.h> 991da177e4SLinus Torvalds 1001da177e4SLinus Torvalds #include <scsi/scsi.h> 1011da177e4SLinus Torvalds #include <scsi/scsi_cmnd.h> 1021da177e4SLinus Torvalds #include <scsi/scsi_device.h> 1031da177e4SLinus Torvalds #include <scsi/scsi_host.h> 1041da177e4SLinus Torvalds #include <scsi/scsi_tcq.h> 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds #include "initio.h" 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds #define SENSE_SIZE 14 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds #define i91u_MAXQUEUE 2 1111da177e4SLinus Torvalds #define i91u_REVID "Initio INI-9X00U/UW SCSI device driver; Revision: 1.04a" 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds #define I950_DEVICE_ID 0x9500 /* Initio's inic-950 product ID */ 1141da177e4SLinus Torvalds #define I940_DEVICE_ID 0x9400 /* Initio's inic-940 product ID */ 1151da177e4SLinus Torvalds #define I935_DEVICE_ID 0x9401 /* Initio's inic-935 product ID */ 1161da177e4SLinus Torvalds #define I920_DEVICE_ID 0x0002 /* Initio's other product ID */ 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds #ifdef DEBUG_i91u 1191da177e4SLinus Torvalds static unsigned int i91u_debug = DEBUG_DEFAULT; 1201da177e4SLinus Torvalds #endif 1211da177e4SLinus Torvalds 12272d39feaSAlan Cox static int initio_tag_enable = 1; 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds #ifdef DEBUG_i91u 1251da177e4SLinus Torvalds static int setup_debug = 0; 1261da177e4SLinus Torvalds #endif 1271da177e4SLinus Torvalds 12872d39feaSAlan Cox static void i91uSCBPost(u8 * pHcb, u8 * pScb); 1291da177e4SLinus Torvalds 13001288732SBen Collins /* PCI Devices supported by this driver */ 13184ad58e4SRandy Dunlap static struct pci_device_id i91u_pci_devices[] = { 13201288732SBen Collins { PCI_VENDOR_ID_INIT, I950_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 13301288732SBen Collins { PCI_VENDOR_ID_INIT, I940_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 13401288732SBen Collins { PCI_VENDOR_ID_INIT, I935_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 13501288732SBen Collins { PCI_VENDOR_ID_INIT, I920_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 13601288732SBen Collins { PCI_VENDOR_ID_DOMEX, I920_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 13701288732SBen Collins { } 1381da177e4SLinus Torvalds }; 13901288732SBen Collins MODULE_DEVICE_TABLE(pci, i91u_pci_devices); 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds #define DEBUG_INTERRUPT 0 1421da177e4SLinus Torvalds #define DEBUG_QUEUE 0 1431da177e4SLinus Torvalds #define DEBUG_STATE 0 1441da177e4SLinus Torvalds #define INT_DISC 0 1451da177e4SLinus Torvalds 14672d39feaSAlan Cox /*--- forward references ---*/ 14772d39feaSAlan Cox static struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun); 14872d39feaSAlan Cox static struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host); 1491da177e4SLinus Torvalds 15072d39feaSAlan Cox static int tulip_main(struct initio_host * host); 1511da177e4SLinus Torvalds 15272d39feaSAlan Cox static int initio_next_state(struct initio_host * host); 15372d39feaSAlan Cox static int initio_state_1(struct initio_host * host); 15472d39feaSAlan Cox static int initio_state_2(struct initio_host * host); 15572d39feaSAlan Cox static int initio_state_3(struct initio_host * host); 15672d39feaSAlan Cox static int initio_state_4(struct initio_host * host); 15772d39feaSAlan Cox static int initio_state_5(struct initio_host * host); 15872d39feaSAlan Cox static int initio_state_6(struct initio_host * host); 15972d39feaSAlan Cox static int initio_state_7(struct initio_host * host); 16072d39feaSAlan Cox static int initio_xfer_data_in(struct initio_host * host); 16172d39feaSAlan Cox static int initio_xfer_data_out(struct initio_host * host); 16272d39feaSAlan Cox static int initio_xpad_in(struct initio_host * host); 16372d39feaSAlan Cox static int initio_xpad_out(struct initio_host * host); 16472d39feaSAlan Cox static int initio_status_msg(struct initio_host * host); 1651da177e4SLinus Torvalds 16672d39feaSAlan Cox static int initio_msgin(struct initio_host * host); 16772d39feaSAlan Cox static int initio_msgin_sync(struct initio_host * host); 16872d39feaSAlan Cox static int initio_msgin_accept(struct initio_host * host); 16972d39feaSAlan Cox static int initio_msgout_reject(struct initio_host * host); 17072d39feaSAlan Cox static int initio_msgin_extend(struct initio_host * host); 1711da177e4SLinus Torvalds 17272d39feaSAlan Cox static int initio_msgout_ide(struct initio_host * host); 17372d39feaSAlan Cox static int initio_msgout_abort_targ(struct initio_host * host); 17472d39feaSAlan Cox static int initio_msgout_abort_tag(struct initio_host * host); 1751da177e4SLinus Torvalds 17672d39feaSAlan Cox static int initio_bus_device_reset(struct initio_host * host); 17772d39feaSAlan Cox static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb); 17872d39feaSAlan Cox static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb); 17972d39feaSAlan Cox static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb); 18072d39feaSAlan Cox static int int_initio_busfree(struct initio_host * host); 18172d39feaSAlan Cox static int int_initio_scsi_rst(struct initio_host * host); 18272d39feaSAlan Cox static int int_initio_bad_seq(struct initio_host * host); 18372d39feaSAlan Cox static int int_initio_resel(struct initio_host * host); 18472d39feaSAlan Cox static int initio_sync_done(struct initio_host * host); 18572d39feaSAlan Cox static int wdtr_done(struct initio_host * host); 18672d39feaSAlan Cox static int wait_tulip(struct initio_host * host); 18772d39feaSAlan Cox static int initio_wait_done_disc(struct initio_host * host); 18872d39feaSAlan Cox static int initio_wait_disc(struct initio_host * host); 18972d39feaSAlan Cox static void tulip_scsi(struct initio_host * host); 19072d39feaSAlan Cox static int initio_post_scsi_rst(struct initio_host * host); 1911da177e4SLinus Torvalds 19272d39feaSAlan Cox static void initio_se2_ew_en(unsigned long base); 19372d39feaSAlan Cox static void initio_se2_ew_ds(unsigned long base); 19472d39feaSAlan Cox static int initio_se2_rd_all(unsigned long base); 19572d39feaSAlan Cox static void initio_se2_update_all(unsigned long base); /* setup default pattern */ 19672d39feaSAlan Cox static void initio_read_eeprom(unsigned long base); 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds /* ---- INTERNAL VARIABLES ---- */ 1991da177e4SLinus Torvalds 2001da177e4SLinus Torvalds static NVRAM i91unvram; 2011da177e4SLinus Torvalds static NVRAM *i91unvramp; 2021da177e4SLinus Torvalds 20372d39feaSAlan Cox static u8 i91udftNvRam[64] = 2041da177e4SLinus Torvalds { 2051da177e4SLinus Torvalds /*----------- header -----------*/ 2061da177e4SLinus Torvalds 0x25, 0xc9, /* Signature */ 2071da177e4SLinus Torvalds 0x40, /* Size */ 2081da177e4SLinus Torvalds 0x01, /* Revision */ 2091da177e4SLinus Torvalds /* -- Host Adapter Structure -- */ 2101da177e4SLinus Torvalds 0x95, /* ModelByte0 */ 2111da177e4SLinus Torvalds 0x00, /* ModelByte1 */ 2121da177e4SLinus Torvalds 0x00, /* ModelInfo */ 2131da177e4SLinus Torvalds 0x01, /* NumOfCh */ 2141da177e4SLinus Torvalds NBC1_DEFAULT, /* BIOSConfig1 */ 2151da177e4SLinus Torvalds 0, /* BIOSConfig2 */ 2161da177e4SLinus Torvalds 0, /* HAConfig1 */ 2171da177e4SLinus Torvalds 0, /* HAConfig2 */ 2181da177e4SLinus Torvalds /* SCSI channel 0 and target Structure */ 2191da177e4SLinus Torvalds 7, /* SCSIid */ 2201da177e4SLinus Torvalds NCC1_DEFAULT, /* SCSIconfig1 */ 2211da177e4SLinus Torvalds 0, /* SCSIconfig2 */ 2221da177e4SLinus Torvalds 0x10, /* NumSCSItarget */ 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2251da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2261da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2271da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2281da177e4SLinus Torvalds 2291da177e4SLinus Torvalds /* SCSI channel 1 and target Structure */ 2301da177e4SLinus Torvalds 7, /* SCSIid */ 2311da177e4SLinus Torvalds NCC1_DEFAULT, /* SCSIconfig1 */ 2321da177e4SLinus Torvalds 0, /* SCSIconfig2 */ 2331da177e4SLinus Torvalds 0x10, /* NumSCSItarget */ 2341da177e4SLinus Torvalds 2351da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2361da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2371da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2381da177e4SLinus Torvalds NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, NTC_DEFAULT, 2391da177e4SLinus Torvalds 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2401da177e4SLinus Torvalds 0, 0}; /* - CheckSum - */ 2411da177e4SLinus Torvalds 2421da177e4SLinus Torvalds 24372d39feaSAlan Cox static u8 initio_rate_tbl[8] = /* fast 20 */ 2441da177e4SLinus Torvalds { 24525985edcSLucas De Marchi /* nanosecond divide by 4 */ 2461da177e4SLinus Torvalds 12, /* 50ns, 20M */ 2471da177e4SLinus Torvalds 18, /* 75ns, 13.3M */ 2481da177e4SLinus Torvalds 25, /* 100ns, 10M */ 2491da177e4SLinus Torvalds 31, /* 125ns, 8M */ 2501da177e4SLinus Torvalds 37, /* 150ns, 6.6M */ 2511da177e4SLinus Torvalds 43, /* 175ns, 5.7M */ 2521da177e4SLinus Torvalds 50, /* 200ns, 5M */ 2531da177e4SLinus Torvalds 62 /* 250ns, 4M */ 2541da177e4SLinus Torvalds }; 2551da177e4SLinus Torvalds 25672d39feaSAlan Cox static void initio_do_pause(unsigned amount) 25772d39feaSAlan Cox { 25872d39feaSAlan Cox /* Pause for amount jiffies */ 2591da177e4SLinus Torvalds unsigned long the_time = jiffies + amount; 2601da177e4SLinus Torvalds 26172d39feaSAlan Cox while (time_before_eq(jiffies, the_time)) 26272d39feaSAlan Cox cpu_relax(); 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds /*-- forward reference --*/ 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds /****************************************************************** 2681da177e4SLinus Torvalds Input: instruction for Serial E2PROM 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds EX: se2_rd(0 call se2_instr() to send address and read command 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds StartBit OP_Code Address Data 2731da177e4SLinus Torvalds --------- -------- ------------------ ------- 2741da177e4SLinus Torvalds 1 1 , 0 A5,A4,A3,A2,A1,A0 D15-D0 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds +----------------------------------------------------- 2771da177e4SLinus Torvalds | 2781da177e4SLinus Torvalds CS -----+ 2791da177e4SLinus Torvalds +--+ +--+ +--+ +--+ +--+ 2801da177e4SLinus Torvalds ^ | ^ | ^ | ^ | ^ | 2811da177e4SLinus Torvalds | | | | | | | | | | 2821da177e4SLinus Torvalds CLK -------+ +--+ +--+ +--+ +--+ +-- 2831da177e4SLinus Torvalds (leading edge trigger) 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds +--1-----1--+ 2861da177e4SLinus Torvalds | SB OP | OP A5 A4 2871da177e4SLinus Torvalds DI ----+ +--0------------------ 2881da177e4SLinus Torvalds (address and cmd sent to nvram) 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds -------------------------------------------+ 2911da177e4SLinus Torvalds | 2921da177e4SLinus Torvalds DO +--- 2931da177e4SLinus Torvalds (data sent from nvram) 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds ******************************************************************/ 29772d39feaSAlan Cox 29872d39feaSAlan Cox /** 29972d39feaSAlan Cox * initio_se2_instr - bitbang an instruction 30072d39feaSAlan Cox * @base: Base of InitIO controller 30172d39feaSAlan Cox * @instr: Instruction for serial E2PROM 30272d39feaSAlan Cox * 30372d39feaSAlan Cox * Bitbang an instruction out to the serial E2Prom 30472d39feaSAlan Cox */ 30572d39feaSAlan Cox 30672d39feaSAlan Cox static void initio_se2_instr(unsigned long base, u8 instr) 3071da177e4SLinus Torvalds { 3081da177e4SLinus Torvalds int i; 30972d39feaSAlan Cox u8 b; 3101da177e4SLinus Torvalds 31172d39feaSAlan Cox outb(SE2CS | SE2DO, base + TUL_NVRAM); /* cs+start bit */ 31272d39feaSAlan Cox udelay(30); 31372d39feaSAlan Cox outb(SE2CS | SE2CLK | SE2DO, base + TUL_NVRAM); /* +CLK */ 31472d39feaSAlan Cox udelay(30); 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 3171da177e4SLinus Torvalds if (instr & 0x80) 3181da177e4SLinus Torvalds b = SE2CS | SE2DO; /* -CLK+dataBit */ 3191da177e4SLinus Torvalds else 3201da177e4SLinus Torvalds b = SE2CS; /* -CLK */ 32172d39feaSAlan Cox outb(b, base + TUL_NVRAM); 32272d39feaSAlan Cox udelay(30); 32372d39feaSAlan Cox outb(b | SE2CLK, base + TUL_NVRAM); /* +CLK */ 32472d39feaSAlan Cox udelay(30); 3251da177e4SLinus Torvalds instr <<= 1; 3261da177e4SLinus Torvalds } 32772d39feaSAlan Cox outb(SE2CS, base + TUL_NVRAM); /* -CLK */ 32872d39feaSAlan Cox udelay(30); 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds 33272d39feaSAlan Cox /** 33372d39feaSAlan Cox * initio_se2_ew_en - Enable erase/write 33472d39feaSAlan Cox * @base: Base address of InitIO controller 33572d39feaSAlan Cox * 33672d39feaSAlan Cox * Enable erase/write state of serial EEPROM 33772d39feaSAlan Cox */ 33872d39feaSAlan Cox void initio_se2_ew_en(unsigned long base) 3391da177e4SLinus Torvalds { 34072d39feaSAlan Cox initio_se2_instr(base, 0x30); /* EWEN */ 34172d39feaSAlan Cox outb(0, base + TUL_NVRAM); /* -CS */ 34272d39feaSAlan Cox udelay(30); 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds 34672d39feaSAlan Cox /** 34772d39feaSAlan Cox * initio_se2_ew_ds - Disable erase/write 34872d39feaSAlan Cox * @base: Base address of InitIO controller 34972d39feaSAlan Cox * 35072d39feaSAlan Cox * Disable erase/write state of serial EEPROM 35172d39feaSAlan Cox */ 35272d39feaSAlan Cox void initio_se2_ew_ds(unsigned long base) 3531da177e4SLinus Torvalds { 35472d39feaSAlan Cox initio_se2_instr(base, 0); /* EWDS */ 35572d39feaSAlan Cox outb(0, base + TUL_NVRAM); /* -CS */ 35672d39feaSAlan Cox udelay(30); 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds 36072d39feaSAlan Cox /** 36172d39feaSAlan Cox * initio_se2_rd - read E2PROM word 36272d39feaSAlan Cox * @base: Base of InitIO controller 36372d39feaSAlan Cox * @addr: Address of word in E2PROM 36472d39feaSAlan Cox * 36572d39feaSAlan Cox * Read a word from the NV E2PROM device 36672d39feaSAlan Cox */ 36772d39feaSAlan Cox static u16 initio_se2_rd(unsigned long base, u8 addr) 3681da177e4SLinus Torvalds { 36972d39feaSAlan Cox u8 instr, rb; 37072d39feaSAlan Cox u16 val = 0; 3711da177e4SLinus Torvalds int i; 3721da177e4SLinus Torvalds 37372d39feaSAlan Cox instr = (u8) (addr | 0x80); 37472d39feaSAlan Cox initio_se2_instr(base, instr); /* READ INSTR */ 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds for (i = 15; i >= 0; i--) { 37772d39feaSAlan Cox outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ 37872d39feaSAlan Cox udelay(30); 37972d39feaSAlan Cox outb(SE2CS, base + TUL_NVRAM); /* -CLK */ 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds /* sample data after the following edge of clock */ 38272d39feaSAlan Cox rb = inb(base + TUL_NVRAM); 38372d39feaSAlan Cox rb &= SE2DI; 38472d39feaSAlan Cox val += (rb << i); 38572d39feaSAlan Cox udelay(30); /* 6/20/95 */ 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds 38872d39feaSAlan Cox outb(0, base + TUL_NVRAM); /* no chip select */ 38972d39feaSAlan Cox udelay(30); 39072d39feaSAlan Cox return val; 3911da177e4SLinus Torvalds } 3921da177e4SLinus Torvalds 39372d39feaSAlan Cox /** 39472d39feaSAlan Cox * initio_se2_wr - read E2PROM word 39572d39feaSAlan Cox * @base: Base of InitIO controller 39672d39feaSAlan Cox * @addr: Address of word in E2PROM 39772d39feaSAlan Cox * @val: Value to write 39872d39feaSAlan Cox * 39972d39feaSAlan Cox * Write a word to the NV E2PROM device. Used when recovering from 40072d39feaSAlan Cox * a problem with the NV. 40172d39feaSAlan Cox */ 40272d39feaSAlan Cox static void initio_se2_wr(unsigned long base, u8 addr, u16 val) 4031da177e4SLinus Torvalds { 40472d39feaSAlan Cox u8 rb; 40572d39feaSAlan Cox u8 instr; 4061da177e4SLinus Torvalds int i; 4071da177e4SLinus Torvalds 40872d39feaSAlan Cox instr = (u8) (addr | 0x40); 40972d39feaSAlan Cox initio_se2_instr(base, instr); /* WRITE INSTR */ 4101da177e4SLinus Torvalds for (i = 15; i >= 0; i--) { 41172d39feaSAlan Cox if (val & 0x8000) 41272d39feaSAlan Cox outb(SE2CS | SE2DO, base + TUL_NVRAM); /* -CLK+dataBit 1 */ 4131da177e4SLinus Torvalds else 41472d39feaSAlan Cox outb(SE2CS, base + TUL_NVRAM); /* -CLK+dataBit 0 */ 41572d39feaSAlan Cox udelay(30); 41672d39feaSAlan Cox outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ 41772d39feaSAlan Cox udelay(30); 41872d39feaSAlan Cox val <<= 1; 4191da177e4SLinus Torvalds } 42072d39feaSAlan Cox outb(SE2CS, base + TUL_NVRAM); /* -CLK */ 42172d39feaSAlan Cox udelay(30); 42272d39feaSAlan Cox outb(0, base + TUL_NVRAM); /* -CS */ 42372d39feaSAlan Cox udelay(30); 4241da177e4SLinus Torvalds 42572d39feaSAlan Cox outb(SE2CS, base + TUL_NVRAM); /* +CS */ 42672d39feaSAlan Cox udelay(30); 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds for (;;) { 42972d39feaSAlan Cox outb(SE2CS | SE2CLK, base + TUL_NVRAM); /* +CLK */ 43072d39feaSAlan Cox udelay(30); 43172d39feaSAlan Cox outb(SE2CS, base + TUL_NVRAM); /* -CLK */ 43272d39feaSAlan Cox udelay(30); 43372d39feaSAlan Cox if ((rb = inb(base + TUL_NVRAM)) & SE2DI) 4341da177e4SLinus Torvalds break; /* write complete */ 4351da177e4SLinus Torvalds } 43672d39feaSAlan Cox outb(0, base + TUL_NVRAM); /* -CS */ 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds 43972d39feaSAlan Cox /** 44072d39feaSAlan Cox * initio_se2_rd_all - read hostadapter NV configuration 44172d39feaSAlan Cox * @base: Base address of InitIO controller 44272d39feaSAlan Cox * 44372d39feaSAlan Cox * Reads the E2PROM data into main memory. Ensures that the checksum 44472d39feaSAlan Cox * and header marker are valid. Returns 1 on success -1 on error. 44572d39feaSAlan Cox */ 4461da177e4SLinus Torvalds 44772d39feaSAlan Cox static int initio_se2_rd_all(unsigned long base) 4481da177e4SLinus Torvalds { 4491da177e4SLinus Torvalds int i; 45072d39feaSAlan Cox u16 chksum = 0; 45172d39feaSAlan Cox u16 *np; 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds i91unvramp = &i91unvram; 45472d39feaSAlan Cox np = (u16 *) i91unvramp; 45572d39feaSAlan Cox for (i = 0; i < 32; i++) 45672d39feaSAlan Cox *np++ = initio_se2_rd(base, i); 4571da177e4SLinus Torvalds 45872d39feaSAlan Cox /* Is signature "ini" ok ? */ 4591da177e4SLinus Torvalds if (i91unvramp->NVM_Signature != INI_SIGNATURE) 4601da177e4SLinus Torvalds return -1; 46172d39feaSAlan Cox /* Is ckecksum ok ? */ 46272d39feaSAlan Cox np = (u16 *) i91unvramp; 4631da177e4SLinus Torvalds for (i = 0; i < 31; i++) 4641da177e4SLinus Torvalds chksum += *np++; 46572d39feaSAlan Cox if (i91unvramp->NVM_CheckSum != chksum) 4661da177e4SLinus Torvalds return -1; 4671da177e4SLinus Torvalds return 1; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds 47072d39feaSAlan Cox /** 47172d39feaSAlan Cox * initio_se2_update_all - Update E2PROM 47272d39feaSAlan Cox * @base: Base of InitIO controller 47372d39feaSAlan Cox * 47472d39feaSAlan Cox * Update the E2PROM by wrting any changes into the E2PROM 47572d39feaSAlan Cox * chip, rewriting the checksum. 47672d39feaSAlan Cox */ 47772d39feaSAlan Cox static void initio_se2_update_all(unsigned long base) 4781da177e4SLinus Torvalds { /* setup default pattern */ 4791da177e4SLinus Torvalds int i; 48072d39feaSAlan Cox u16 chksum = 0; 48172d39feaSAlan Cox u16 *np, *np1; 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds i91unvramp = &i91unvram; 4841da177e4SLinus Torvalds /* Calculate checksum first */ 48572d39feaSAlan Cox np = (u16 *) i91udftNvRam; 4861da177e4SLinus Torvalds for (i = 0; i < 31; i++) 4871da177e4SLinus Torvalds chksum += *np++; 48872d39feaSAlan Cox *np = chksum; 48972d39feaSAlan Cox initio_se2_ew_en(base); /* Enable write */ 4901da177e4SLinus Torvalds 49172d39feaSAlan Cox np = (u16 *) i91udftNvRam; 49272d39feaSAlan Cox np1 = (u16 *) i91unvramp; 4931da177e4SLinus Torvalds for (i = 0; i < 32; i++, np++, np1++) { 49472d39feaSAlan Cox if (*np != *np1) 49572d39feaSAlan Cox initio_se2_wr(base, i, *np); 4961da177e4SLinus Torvalds } 49772d39feaSAlan Cox initio_se2_ew_ds(base); /* Disable write */ 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 50072d39feaSAlan Cox /** 50172d39feaSAlan Cox * initio_read_eeprom - Retrieve configuration 50272d39feaSAlan Cox * @base: Base of InitIO Host Adapter 50372d39feaSAlan Cox * 50472d39feaSAlan Cox * Retrieve the host adapter configuration data from E2Prom. If the 50572d39feaSAlan Cox * data is invalid then the defaults are used and are also restored 50672d39feaSAlan Cox * into the E2PROM. This forms the access point for the SCSI driver 50772d39feaSAlan Cox * into the E2PROM layer, the other functions for the E2PROM are all 50872d39feaSAlan Cox * internal use. 50972d39feaSAlan Cox * 51072d39feaSAlan Cox * Must be called single threaded, uses a shared global area. 51172d39feaSAlan Cox */ 5121da177e4SLinus Torvalds 51372d39feaSAlan Cox static void initio_read_eeprom(unsigned long base) 5141da177e4SLinus Torvalds { 51572d39feaSAlan Cox u8 gctrl; 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds i91unvramp = &i91unvram; 51872d39feaSAlan Cox /* Enable EEProm programming */ 51972d39feaSAlan Cox gctrl = inb(base + TUL_GCTRL); 52072d39feaSAlan Cox outb(gctrl | TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL); 52172d39feaSAlan Cox if (initio_se2_rd_all(base) != 1) { 52272d39feaSAlan Cox initio_se2_update_all(base); /* setup default pattern */ 52372d39feaSAlan Cox initio_se2_rd_all(base); /* load again */ 5241da177e4SLinus Torvalds } 52572d39feaSAlan Cox /* Disable EEProm programming */ 52672d39feaSAlan Cox gctrl = inb(base + TUL_GCTRL); 52772d39feaSAlan Cox outb(gctrl & ~TUL_GCTRL_EEPROM_BIT, base + TUL_GCTRL); 5281da177e4SLinus Torvalds } 5291da177e4SLinus Torvalds 53072d39feaSAlan Cox /** 53172d39feaSAlan Cox * initio_stop_bm - stop bus master 53272d39feaSAlan Cox * @host: InitIO we are stopping 53372d39feaSAlan Cox * 5343ad2f3fbSDaniel Mack * Stop any pending DMA operation, aborting the DMA if necessary 53572d39feaSAlan Cox */ 5361da177e4SLinus Torvalds 53772d39feaSAlan Cox static void initio_stop_bm(struct initio_host * host) 5381da177e4SLinus Torvalds { 5391da177e4SLinus Torvalds 54072d39feaSAlan Cox if (inb(host->addr + TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */ 54172d39feaSAlan Cox outb(TAX_X_ABT | TAX_X_CLR_FIFO, host->addr + TUL_XCmd); 5421da177e4SLinus Torvalds /* wait Abort DMA xfer done */ 54372d39feaSAlan Cox while ((inb(host->addr + TUL_Int) & XABT) == 0) 54472d39feaSAlan Cox cpu_relax(); 5451da177e4SLinus Torvalds } 54672d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 5471da177e4SLinus Torvalds } 5481da177e4SLinus Torvalds 54972d39feaSAlan Cox /** 55072d39feaSAlan Cox * initio_reset_scsi - Reset SCSI host controller 55172d39feaSAlan Cox * @host: InitIO host to reset 55272d39feaSAlan Cox * @seconds: Recovery time 55372d39feaSAlan Cox * 55472d39feaSAlan Cox * Perform a full reset of the SCSI subsystem. 55572d39feaSAlan Cox */ 55672d39feaSAlan Cox 55772d39feaSAlan Cox static int initio_reset_scsi(struct initio_host * host, int seconds) 5581da177e4SLinus Torvalds { 55972d39feaSAlan Cox outb(TSC_RST_BUS, host->addr + TUL_SCtrl0); 5601da177e4SLinus Torvalds 56172d39feaSAlan Cox while (!((host->jsint = inb(host->addr + TUL_SInt)) & TSS_SCSIRST_INT)) 56272d39feaSAlan Cox cpu_relax(); 5631da177e4SLinus Torvalds 5641da177e4SLinus Torvalds /* reset tulip chip */ 56572d39feaSAlan Cox outb(0, host->addr + TUL_SSignal); 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds /* Stall for a while, wait for target's firmware ready,make it 2 sec ! */ 5681da177e4SLinus Torvalds /* SONY 5200 tape drive won't work if only stall for 1 sec */ 56972d39feaSAlan Cox /* FIXME: this is a very long busy wait right now */ 57072d39feaSAlan Cox initio_do_pause(seconds * HZ); 5711da177e4SLinus Torvalds 57272d39feaSAlan Cox inb(host->addr + TUL_SInt); 57372d39feaSAlan Cox return SCSI_RESET_SUCCESS; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds 57672d39feaSAlan Cox /** 57772d39feaSAlan Cox * initio_init - set up an InitIO host adapter 57872d39feaSAlan Cox * @host: InitIO host adapter 57972d39feaSAlan Cox * @num_scbs: Number of SCBS 58072d39feaSAlan Cox * @bios_addr: BIOS address 58172d39feaSAlan Cox * 58272d39feaSAlan Cox * Set up the host adapter and devices according to the configuration 58372d39feaSAlan Cox * retrieved from the E2PROM. 58472d39feaSAlan Cox * 58572d39feaSAlan Cox * Locking: Calls E2PROM layer code which is not re-enterable so must 58672d39feaSAlan Cox * run single threaded for now. 58772d39feaSAlan Cox */ 58872d39feaSAlan Cox 58972d39feaSAlan Cox static void initio_init(struct initio_host * host, u8 *bios_addr) 5901da177e4SLinus Torvalds { 5911da177e4SLinus Torvalds int i; 59272d39feaSAlan Cox u8 *flags; 59372d39feaSAlan Cox u8 *heads; 5941da177e4SLinus Torvalds 59572d39feaSAlan Cox /* Get E2Prom configuration */ 59672d39feaSAlan Cox initio_read_eeprom(host->addr); 5971da177e4SLinus Torvalds if (i91unvramp->NVM_SCSIInfo[0].NVM_NumOfTarg == 8) 59872d39feaSAlan Cox host->max_tar = 8; 5991da177e4SLinus Torvalds else 60072d39feaSAlan Cox host->max_tar = 16; 6011da177e4SLinus Torvalds 60272d39feaSAlan Cox host->config = i91unvramp->NVM_SCSIInfo[0].NVM_ChConfig1; 6031da177e4SLinus Torvalds 60472d39feaSAlan Cox host->scsi_id = i91unvramp->NVM_SCSIInfo[0].NVM_ChSCSIID; 60572d39feaSAlan Cox host->idmask = ~(1 << host->scsi_id); 6061da177e4SLinus Torvalds 60744456d37SOlaf Hering #ifdef CHK_PARITY 6081da177e4SLinus Torvalds /* Enable parity error response */ 60972d39feaSAlan Cox outb(inb(host->addr + TUL_PCMD) | 0x40, host->addr + TUL_PCMD); 6101da177e4SLinus Torvalds #endif 6111da177e4SLinus Torvalds 6121da177e4SLinus Torvalds /* Mask all the interrupt */ 61372d39feaSAlan Cox outb(0x1F, host->addr + TUL_Mask); 6141da177e4SLinus Torvalds 61572d39feaSAlan Cox initio_stop_bm(host); 6161da177e4SLinus Torvalds /* --- Initialize the tulip --- */ 61772d39feaSAlan Cox outb(TSC_RST_CHIP, host->addr + TUL_SCtrl0); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds /* program HBA's SCSI ID */ 62072d39feaSAlan Cox outb(host->scsi_id << 4, host->addr + TUL_SScsiId); 6211da177e4SLinus Torvalds 6221da177e4SLinus Torvalds /* Enable Initiator Mode ,phase latch,alternate sync period mode, 6231da177e4SLinus Torvalds disable SCSI reset */ 62472d39feaSAlan Cox if (host->config & HCC_EN_PAR) 62572d39feaSAlan Cox host->sconf1 = (TSC_INITDEFAULT | TSC_EN_SCSI_PAR); 6261da177e4SLinus Torvalds else 62772d39feaSAlan Cox host->sconf1 = (TSC_INITDEFAULT); 62872d39feaSAlan Cox outb(host->sconf1, host->addr + TUL_SConfig); 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds /* Enable HW reselect */ 63172d39feaSAlan Cox outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); 6321da177e4SLinus Torvalds 63372d39feaSAlan Cox outb(0, host->addr + TUL_SPeriod); 6341da177e4SLinus Torvalds 6351da177e4SLinus Torvalds /* selection time out = 250 ms */ 63672d39feaSAlan Cox outb(153, host->addr + TUL_STimeOut); 6371da177e4SLinus Torvalds 63872d39feaSAlan Cox /* Enable SCSI terminator */ 63972d39feaSAlan Cox outb((host->config & (HCC_ACT_TERM1 | HCC_ACT_TERM2)), 64072d39feaSAlan Cox host->addr + TUL_XCtrl); 64172d39feaSAlan Cox outb(((host->config & HCC_AUTO_TERM) >> 4) | 64272d39feaSAlan Cox (inb(host->addr + TUL_GCTRL1) & 0xFE), 64372d39feaSAlan Cox host->addr + TUL_GCTRL1); 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds for (i = 0, 64672d39feaSAlan Cox flags = & (i91unvramp->NVM_SCSIInfo[0].NVM_Targ0Config), 64772d39feaSAlan Cox heads = bios_addr + 0x180; 64872d39feaSAlan Cox i < host->max_tar; 64972d39feaSAlan Cox i++, flags++) { 65072d39feaSAlan Cox host->targets[i].flags = *flags & ~(TCF_SYNC_DONE | TCF_WDTR_DONE); 65172d39feaSAlan Cox if (host->targets[i].flags & TCF_EN_255) 65272d39feaSAlan Cox host->targets[i].drv_flags = TCF_DRV_255_63; 6531da177e4SLinus Torvalds else 65472d39feaSAlan Cox host->targets[i].drv_flags = 0; 65572d39feaSAlan Cox host->targets[i].js_period = 0; 65672d39feaSAlan Cox host->targets[i].sconfig0 = host->sconf1; 65772d39feaSAlan Cox host->targets[i].heads = *heads++; 65872d39feaSAlan Cox if (host->targets[i].heads == 255) 65972d39feaSAlan Cox host->targets[i].drv_flags = TCF_DRV_255_63; 6601da177e4SLinus Torvalds else 66172d39feaSAlan Cox host->targets[i].drv_flags = 0; 66272d39feaSAlan Cox host->targets[i].sectors = *heads++; 66372d39feaSAlan Cox host->targets[i].flags &= ~TCF_BUSY; 66472d39feaSAlan Cox host->act_tags[i] = 0; 66572d39feaSAlan Cox host->max_tags[i] = 0xFF; 6661da177e4SLinus Torvalds } /* for */ 6671da177e4SLinus Torvalds printk("i91u: PCI Base=0x%04X, IRQ=%d, BIOS=0x%04X0, SCSI ID=%d\n", 668e9e42fafSAlan Cox host->addr, host->pci_dev->irq, 66972d39feaSAlan Cox host->bios_addr, host->scsi_id); 67072d39feaSAlan Cox /* Reset SCSI Bus */ 67172d39feaSAlan Cox if (host->config & HCC_SCSI_RESET) { 67272d39feaSAlan Cox printk(KERN_INFO "i91u: Reset SCSI Bus ... \n"); 67372d39feaSAlan Cox initio_reset_scsi(host, 10); 6741da177e4SLinus Torvalds } 67572d39feaSAlan Cox outb(0x17, host->addr + TUL_SCFG1); 67672d39feaSAlan Cox outb(0xE9, host->addr + TUL_SIntEnable); 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds 67972d39feaSAlan Cox /** 68072d39feaSAlan Cox * initio_alloc_scb - Allocate an SCB 68172d39feaSAlan Cox * @host: InitIO host we are allocating for 68272d39feaSAlan Cox * 68372d39feaSAlan Cox * Walk the SCB list for the controller and allocate a free SCB if 68472d39feaSAlan Cox * one exists. 68572d39feaSAlan Cox */ 68672d39feaSAlan Cox static struct scsi_ctrl_blk *initio_alloc_scb(struct initio_host *host) 6871da177e4SLinus Torvalds { 68872d39feaSAlan Cox struct scsi_ctrl_blk *scb; 68972d39feaSAlan Cox unsigned long flags; 69072d39feaSAlan Cox 69172d39feaSAlan Cox spin_lock_irqsave(&host->avail_lock, flags); 69272d39feaSAlan Cox if ((scb = host->first_avail) != NULL) { 6931da177e4SLinus Torvalds #if DEBUG_QUEUE 69472d39feaSAlan Cox printk("find scb at %p\n", scb); 6951da177e4SLinus Torvalds #endif 69672d39feaSAlan Cox if ((host->first_avail = scb->next) == NULL) 69772d39feaSAlan Cox host->last_avail = NULL; 69872d39feaSAlan Cox scb->next = NULL; 69972d39feaSAlan Cox scb->status = SCB_RENT; 7001da177e4SLinus Torvalds } 70172d39feaSAlan Cox spin_unlock_irqrestore(&host->avail_lock, flags); 70272d39feaSAlan Cox return scb; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 70572d39feaSAlan Cox /** 70672d39feaSAlan Cox * initio_release_scb - Release an SCB 70772d39feaSAlan Cox * @host: InitIO host that owns the SCB 70872d39feaSAlan Cox * @cmnd: SCB command block being returned 70972d39feaSAlan Cox * 71072d39feaSAlan Cox * Return an allocated SCB to the host free list 71172d39feaSAlan Cox */ 71272d39feaSAlan Cox 71372d39feaSAlan Cox static void initio_release_scb(struct initio_host * host, struct scsi_ctrl_blk * cmnd) 7141da177e4SLinus Torvalds { 71572d39feaSAlan Cox unsigned long flags; 7161da177e4SLinus Torvalds 7171da177e4SLinus Torvalds #if DEBUG_QUEUE 71872d39feaSAlan Cox printk("Release SCB %p; ", cmnd); 7191da177e4SLinus Torvalds #endif 72072d39feaSAlan Cox spin_lock_irqsave(&(host->avail_lock), flags); 72172d39feaSAlan Cox cmnd->srb = NULL; 72272d39feaSAlan Cox cmnd->status = 0; 72372d39feaSAlan Cox cmnd->next = NULL; 72472d39feaSAlan Cox if (host->last_avail != NULL) { 72572d39feaSAlan Cox host->last_avail->next = cmnd; 72672d39feaSAlan Cox host->last_avail = cmnd; 7271da177e4SLinus Torvalds } else { 72872d39feaSAlan Cox host->first_avail = cmnd; 72972d39feaSAlan Cox host->last_avail = cmnd; 7301da177e4SLinus Torvalds } 73172d39feaSAlan Cox spin_unlock_irqrestore(&(host->avail_lock), flags); 7321da177e4SLinus Torvalds } 7331da177e4SLinus Torvalds 7341da177e4SLinus Torvalds /***************************************************************************/ 73572d39feaSAlan Cox static void initio_append_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp) 7361da177e4SLinus Torvalds { 7371da177e4SLinus Torvalds 7381da177e4SLinus Torvalds #if DEBUG_QUEUE 73972d39feaSAlan Cox printk("Append pend SCB %p; ", scbp); 7401da177e4SLinus Torvalds #endif 74172d39feaSAlan Cox scbp->status = SCB_PEND; 74272d39feaSAlan Cox scbp->next = NULL; 74372d39feaSAlan Cox if (host->last_pending != NULL) { 74472d39feaSAlan Cox host->last_pending->next = scbp; 74572d39feaSAlan Cox host->last_pending = scbp; 7461da177e4SLinus Torvalds } else { 74772d39feaSAlan Cox host->first_pending = scbp; 74872d39feaSAlan Cox host->last_pending = scbp; 7491da177e4SLinus Torvalds } 7501da177e4SLinus Torvalds } 7511da177e4SLinus Torvalds 7521da177e4SLinus Torvalds /***************************************************************************/ 75372d39feaSAlan Cox static void initio_push_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp) 7541da177e4SLinus Torvalds { 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds #if DEBUG_QUEUE 75772d39feaSAlan Cox printk("Push pend SCB %p; ", scbp); 7581da177e4SLinus Torvalds #endif 75972d39feaSAlan Cox scbp->status = SCB_PEND; 76072d39feaSAlan Cox if ((scbp->next = host->first_pending) != NULL) { 76172d39feaSAlan Cox host->first_pending = scbp; 7621da177e4SLinus Torvalds } else { 76372d39feaSAlan Cox host->first_pending = scbp; 76472d39feaSAlan Cox host->last_pending = scbp; 7651da177e4SLinus Torvalds } 7661da177e4SLinus Torvalds } 7671da177e4SLinus Torvalds 76872d39feaSAlan Cox static struct scsi_ctrl_blk *initio_find_first_pend_scb(struct initio_host * host) 7691da177e4SLinus Torvalds { 77072d39feaSAlan Cox struct scsi_ctrl_blk *first; 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds 77372d39feaSAlan Cox first = host->first_pending; 77472d39feaSAlan Cox while (first != NULL) { 77572d39feaSAlan Cox if (first->opcode != ExecSCSI) 77672d39feaSAlan Cox return first; 77772d39feaSAlan Cox if (first->tagmsg == 0) { 77872d39feaSAlan Cox if ((host->act_tags[first->target] == 0) && 77972d39feaSAlan Cox !(host->targets[first->target].flags & TCF_BUSY)) 78072d39feaSAlan Cox return first; 7811da177e4SLinus Torvalds } else { 78272d39feaSAlan Cox if ((host->act_tags[first->target] >= 78372d39feaSAlan Cox host->max_tags[first->target]) | 78472d39feaSAlan Cox (host->targets[first->target].flags & TCF_BUSY)) { 78572d39feaSAlan Cox first = first->next; 7861da177e4SLinus Torvalds continue; 7871da177e4SLinus Torvalds } 78872d39feaSAlan Cox return first; 7891da177e4SLinus Torvalds } 79072d39feaSAlan Cox first = first->next; 79172d39feaSAlan Cox } 79272d39feaSAlan Cox return first; 7931da177e4SLinus Torvalds } 7941da177e4SLinus Torvalds 79572d39feaSAlan Cox static void initio_unlink_pend_scb(struct initio_host * host, struct scsi_ctrl_blk * scb) 7961da177e4SLinus Torvalds { 79772d39feaSAlan Cox struct scsi_ctrl_blk *tmp, *prev; 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds #if DEBUG_QUEUE 80072d39feaSAlan Cox printk("unlink pend SCB %p; ", scb); 8011da177e4SLinus Torvalds #endif 8021da177e4SLinus Torvalds 80372d39feaSAlan Cox prev = tmp = host->first_pending; 80472d39feaSAlan Cox while (tmp != NULL) { 80572d39feaSAlan Cox if (scb == tmp) { /* Unlink this SCB */ 80672d39feaSAlan Cox if (tmp == host->first_pending) { 80772d39feaSAlan Cox if ((host->first_pending = tmp->next) == NULL) 80872d39feaSAlan Cox host->last_pending = NULL; 8091da177e4SLinus Torvalds } else { 81072d39feaSAlan Cox prev->next = tmp->next; 81172d39feaSAlan Cox if (tmp == host->last_pending) 81272d39feaSAlan Cox host->last_pending = prev; 8131da177e4SLinus Torvalds } 81472d39feaSAlan Cox tmp->next = NULL; 8151da177e4SLinus Torvalds break; 8161da177e4SLinus Torvalds } 81772d39feaSAlan Cox prev = tmp; 81872d39feaSAlan Cox tmp = tmp->next; 8191da177e4SLinus Torvalds } 8201da177e4SLinus Torvalds } 82172d39feaSAlan Cox 82272d39feaSAlan Cox static void initio_append_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp) 8231da177e4SLinus Torvalds { 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds #if DEBUG_QUEUE 826e2d435eaSStuart Swales printk("append busy SCB %p; ", scbp); 8271da177e4SLinus Torvalds #endif 82872d39feaSAlan Cox if (scbp->tagmsg) 82972d39feaSAlan Cox host->act_tags[scbp->target]++; 8301da177e4SLinus Torvalds else 83172d39feaSAlan Cox host->targets[scbp->target].flags |= TCF_BUSY; 83272d39feaSAlan Cox scbp->status = SCB_BUSY; 83372d39feaSAlan Cox scbp->next = NULL; 83472d39feaSAlan Cox if (host->last_busy != NULL) { 83572d39feaSAlan Cox host->last_busy->next = scbp; 83672d39feaSAlan Cox host->last_busy = scbp; 8371da177e4SLinus Torvalds } else { 83872d39feaSAlan Cox host->first_busy = scbp; 83972d39feaSAlan Cox host->last_busy = scbp; 8401da177e4SLinus Torvalds } 8411da177e4SLinus Torvalds } 8421da177e4SLinus Torvalds 8431da177e4SLinus Torvalds /***************************************************************************/ 84472d39feaSAlan Cox static struct scsi_ctrl_blk *initio_pop_busy_scb(struct initio_host * host) 8451da177e4SLinus Torvalds { 84672d39feaSAlan Cox struct scsi_ctrl_blk *tmp; 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds 84972d39feaSAlan Cox if ((tmp = host->first_busy) != NULL) { 85072d39feaSAlan Cox if ((host->first_busy = tmp->next) == NULL) 85172d39feaSAlan Cox host->last_busy = NULL; 85272d39feaSAlan Cox tmp->next = NULL; 85372d39feaSAlan Cox if (tmp->tagmsg) 85472d39feaSAlan Cox host->act_tags[tmp->target]--; 8551da177e4SLinus Torvalds else 85672d39feaSAlan Cox host->targets[tmp->target].flags &= ~TCF_BUSY; 8571da177e4SLinus Torvalds } 8581da177e4SLinus Torvalds #if DEBUG_QUEUE 85972d39feaSAlan Cox printk("Pop busy SCB %p; ", tmp); 8601da177e4SLinus Torvalds #endif 86172d39feaSAlan Cox return tmp; 8621da177e4SLinus Torvalds } 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds /***************************************************************************/ 86572d39feaSAlan Cox static void initio_unlink_busy_scb(struct initio_host * host, struct scsi_ctrl_blk * scb) 8661da177e4SLinus Torvalds { 86772d39feaSAlan Cox struct scsi_ctrl_blk *tmp, *prev; 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds #if DEBUG_QUEUE 87072d39feaSAlan Cox printk("unlink busy SCB %p; ", scb); 8711da177e4SLinus Torvalds #endif 8721da177e4SLinus Torvalds 87372d39feaSAlan Cox prev = tmp = host->first_busy; 87472d39feaSAlan Cox while (tmp != NULL) { 87572d39feaSAlan Cox if (scb == tmp) { /* Unlink this SCB */ 87672d39feaSAlan Cox if (tmp == host->first_busy) { 87772d39feaSAlan Cox if ((host->first_busy = tmp->next) == NULL) 87872d39feaSAlan Cox host->last_busy = NULL; 8791da177e4SLinus Torvalds } else { 88072d39feaSAlan Cox prev->next = tmp->next; 88172d39feaSAlan Cox if (tmp == host->last_busy) 88272d39feaSAlan Cox host->last_busy = prev; 8831da177e4SLinus Torvalds } 88472d39feaSAlan Cox tmp->next = NULL; 88572d39feaSAlan Cox if (tmp->tagmsg) 88672d39feaSAlan Cox host->act_tags[tmp->target]--; 8871da177e4SLinus Torvalds else 88872d39feaSAlan Cox host->targets[tmp->target].flags &= ~TCF_BUSY; 8891da177e4SLinus Torvalds break; 8901da177e4SLinus Torvalds } 89172d39feaSAlan Cox prev = tmp; 89272d39feaSAlan Cox tmp = tmp->next; 8931da177e4SLinus Torvalds } 8941da177e4SLinus Torvalds return; 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds 89772d39feaSAlan Cox struct scsi_ctrl_blk *initio_find_busy_scb(struct initio_host * host, u16 tarlun) 8981da177e4SLinus Torvalds { 89972d39feaSAlan Cox struct scsi_ctrl_blk *tmp, *prev; 90072d39feaSAlan Cox u16 scbp_tarlun; 9011da177e4SLinus Torvalds 9021da177e4SLinus Torvalds 90372d39feaSAlan Cox prev = tmp = host->first_busy; 90472d39feaSAlan Cox while (tmp != NULL) { 90572d39feaSAlan Cox scbp_tarlun = (tmp->lun << 8) | (tmp->target); 9061da177e4SLinus Torvalds if (scbp_tarlun == tarlun) { /* Unlink this SCB */ 9071da177e4SLinus Torvalds break; 9081da177e4SLinus Torvalds } 90972d39feaSAlan Cox prev = tmp; 91072d39feaSAlan Cox tmp = tmp->next; 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds #if DEBUG_QUEUE 91372d39feaSAlan Cox printk("find busy SCB %p; ", tmp); 9141da177e4SLinus Torvalds #endif 91572d39feaSAlan Cox return tmp; 9161da177e4SLinus Torvalds } 9171da177e4SLinus Torvalds 91872d39feaSAlan Cox static void initio_append_done_scb(struct initio_host * host, struct scsi_ctrl_blk * scbp) 9191da177e4SLinus Torvalds { 9201da177e4SLinus Torvalds #if DEBUG_QUEUE 92172d39feaSAlan Cox printk("append done SCB %p; ", scbp); 9221da177e4SLinus Torvalds #endif 9231da177e4SLinus Torvalds 92472d39feaSAlan Cox scbp->status = SCB_DONE; 92572d39feaSAlan Cox scbp->next = NULL; 92672d39feaSAlan Cox if (host->last_done != NULL) { 92772d39feaSAlan Cox host->last_done->next = scbp; 92872d39feaSAlan Cox host->last_done = scbp; 9291da177e4SLinus Torvalds } else { 93072d39feaSAlan Cox host->first_done = scbp; 93172d39feaSAlan Cox host->last_done = scbp; 9321da177e4SLinus Torvalds } 9331da177e4SLinus Torvalds } 9341da177e4SLinus Torvalds 93572d39feaSAlan Cox struct scsi_ctrl_blk *initio_find_done_scb(struct initio_host * host) 9361da177e4SLinus Torvalds { 93772d39feaSAlan Cox struct scsi_ctrl_blk *tmp; 9381da177e4SLinus Torvalds 93972d39feaSAlan Cox if ((tmp = host->first_done) != NULL) { 94072d39feaSAlan Cox if ((host->first_done = tmp->next) == NULL) 94172d39feaSAlan Cox host->last_done = NULL; 94272d39feaSAlan Cox tmp->next = NULL; 9431da177e4SLinus Torvalds } 9441da177e4SLinus Torvalds #if DEBUG_QUEUE 94572d39feaSAlan Cox printk("find done SCB %p; ",tmp); 9461da177e4SLinus Torvalds #endif 94772d39feaSAlan Cox return tmp; 9481da177e4SLinus Torvalds } 9491da177e4SLinus Torvalds 95072d39feaSAlan Cox static int initio_abort_srb(struct initio_host * host, struct scsi_cmnd *srbp) 9511da177e4SLinus Torvalds { 95272d39feaSAlan Cox unsigned long flags; 95372d39feaSAlan Cox struct scsi_ctrl_blk *tmp, *prev; 9541da177e4SLinus Torvalds 95572d39feaSAlan Cox spin_lock_irqsave(&host->semaph_lock, flags); 9561da177e4SLinus Torvalds 95772d39feaSAlan Cox if ((host->semaph == 0) && (host->active == NULL)) { 9581da177e4SLinus Torvalds /* disable Jasmin SCSI Int */ 95972d39feaSAlan Cox outb(0x1F, host->addr + TUL_Mask); 96072d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 96172d39feaSAlan Cox /* FIXME: synchronize_irq needed ? */ 96272d39feaSAlan Cox tulip_main(host); 96372d39feaSAlan Cox spin_lock_irqsave(&host->semaph_lock, flags); 96472d39feaSAlan Cox host->semaph = 1; 96572d39feaSAlan Cox outb(0x0F, host->addr + TUL_Mask); 96672d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 9671da177e4SLinus Torvalds return SCSI_ABORT_SNOOZE; 9681da177e4SLinus Torvalds } 96972d39feaSAlan Cox prev = tmp = host->first_pending; /* Check Pend queue */ 97072d39feaSAlan Cox while (tmp != NULL) { 9711da177e4SLinus Torvalds /* 07/27/98 */ 97272d39feaSAlan Cox if (tmp->srb == srbp) { 97372d39feaSAlan Cox if (tmp == host->active) { 97472d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 9751da177e4SLinus Torvalds return SCSI_ABORT_BUSY; 97672d39feaSAlan Cox } else if (tmp == host->first_pending) { 97772d39feaSAlan Cox if ((host->first_pending = tmp->next) == NULL) 97872d39feaSAlan Cox host->last_pending = NULL; 9791da177e4SLinus Torvalds } else { 98072d39feaSAlan Cox prev->next = tmp->next; 98172d39feaSAlan Cox if (tmp == host->last_pending) 98272d39feaSAlan Cox host->last_pending = prev; 9831da177e4SLinus Torvalds } 98472d39feaSAlan Cox tmp->hastat = HOST_ABORTED; 98572d39feaSAlan Cox tmp->flags |= SCF_DONE; 98672d39feaSAlan Cox if (tmp->flags & SCF_POST) 98772d39feaSAlan Cox (*tmp->post) ((u8 *) host, (u8 *) tmp); 98872d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 9891da177e4SLinus Torvalds return SCSI_ABORT_SUCCESS; 9901da177e4SLinus Torvalds } 99172d39feaSAlan Cox prev = tmp; 99272d39feaSAlan Cox tmp = tmp->next; 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 99572d39feaSAlan Cox prev = tmp = host->first_busy; /* Check Busy queue */ 99672d39feaSAlan Cox while (tmp != NULL) { 99772d39feaSAlan Cox if (tmp->srb == srbp) { 99872d39feaSAlan Cox if (tmp == host->active) { 99972d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 10001da177e4SLinus Torvalds return SCSI_ABORT_BUSY; 100172d39feaSAlan Cox } else if (tmp->tagmsg == 0) { 100272d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 10031da177e4SLinus Torvalds return SCSI_ABORT_BUSY; 10041da177e4SLinus Torvalds } else { 100572d39feaSAlan Cox host->act_tags[tmp->target]--; 100672d39feaSAlan Cox if (tmp == host->first_busy) { 100772d39feaSAlan Cox if ((host->first_busy = tmp->next) == NULL) 100872d39feaSAlan Cox host->last_busy = NULL; 10091da177e4SLinus Torvalds } else { 101072d39feaSAlan Cox prev->next = tmp->next; 101172d39feaSAlan Cox if (tmp == host->last_busy) 101272d39feaSAlan Cox host->last_busy = prev; 10131da177e4SLinus Torvalds } 101472d39feaSAlan Cox tmp->next = NULL; 10151da177e4SLinus Torvalds 10161da177e4SLinus Torvalds 101772d39feaSAlan Cox tmp->hastat = HOST_ABORTED; 101872d39feaSAlan Cox tmp->flags |= SCF_DONE; 101972d39feaSAlan Cox if (tmp->flags & SCF_POST) 102072d39feaSAlan Cox (*tmp->post) ((u8 *) host, (u8 *) tmp); 102172d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 10221da177e4SLinus Torvalds return SCSI_ABORT_SUCCESS; 10231da177e4SLinus Torvalds } 10241da177e4SLinus Torvalds } 102572d39feaSAlan Cox prev = tmp; 102672d39feaSAlan Cox tmp = tmp->next; 10271da177e4SLinus Torvalds } 102872d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 102972d39feaSAlan Cox return SCSI_ABORT_NOT_RUNNING; 10301da177e4SLinus Torvalds } 10311da177e4SLinus Torvalds 10321da177e4SLinus Torvalds /***************************************************************************/ 103372d39feaSAlan Cox static int initio_bad_seq(struct initio_host * host) 10341da177e4SLinus Torvalds { 103572d39feaSAlan Cox struct scsi_ctrl_blk *scb; 10361da177e4SLinus Torvalds 103772d39feaSAlan Cox printk("initio_bad_seg c=%d\n", host->index); 10381da177e4SLinus Torvalds 103972d39feaSAlan Cox if ((scb = host->active) != NULL) { 104072d39feaSAlan Cox initio_unlink_busy_scb(host, scb); 104172d39feaSAlan Cox scb->hastat = HOST_BAD_PHAS; 104272d39feaSAlan Cox scb->tastat = 0; 104372d39feaSAlan Cox initio_append_done_scb(host, scb); 10441da177e4SLinus Torvalds } 104572d39feaSAlan Cox initio_stop_bm(host); 104672d39feaSAlan Cox initio_reset_scsi(host, 8); /* 7/29/98 */ 104772d39feaSAlan Cox return initio_post_scsi_rst(host); 10481da177e4SLinus Torvalds } 10491da177e4SLinus Torvalds 1050a2ba192cSAdrian Bunk 10511da177e4SLinus Torvalds /************************************************************************/ 105272d39feaSAlan Cox static void initio_exec_scb(struct initio_host * host, struct scsi_ctrl_blk * scb) 10531da177e4SLinus Torvalds { 105472d39feaSAlan Cox unsigned long flags; 10551da177e4SLinus Torvalds 105672d39feaSAlan Cox scb->mode = 0; 10571da177e4SLinus Torvalds 105872d39feaSAlan Cox scb->sgidx = 0; 105972d39feaSAlan Cox scb->sgmax = scb->sglen; 10601da177e4SLinus Torvalds 106172d39feaSAlan Cox spin_lock_irqsave(&host->semaph_lock, flags); 10621da177e4SLinus Torvalds 106372d39feaSAlan Cox initio_append_pend_scb(host, scb); /* Append this SCB to Pending queue */ 10641da177e4SLinus Torvalds 10651da177e4SLinus Torvalds /* VVVVV 07/21/98 */ 106672d39feaSAlan Cox if (host->semaph == 1) { 106772d39feaSAlan Cox /* Disable Jasmin SCSI Int */ 106872d39feaSAlan Cox outb(0x1F, host->addr + TUL_Mask); 106972d39feaSAlan Cox host->semaph = 0; 107072d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 10711da177e4SLinus Torvalds 107272d39feaSAlan Cox tulip_main(host); 10731da177e4SLinus Torvalds 107472d39feaSAlan Cox spin_lock_irqsave(&host->semaph_lock, flags); 107572d39feaSAlan Cox host->semaph = 1; 107672d39feaSAlan Cox outb(0x0F, host->addr + TUL_Mask); 10771da177e4SLinus Torvalds } 107872d39feaSAlan Cox spin_unlock_irqrestore(&host->semaph_lock, flags); 10791da177e4SLinus Torvalds return; 10801da177e4SLinus Torvalds } 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds /***************************************************************************/ 108372d39feaSAlan Cox static int initio_isr(struct initio_host * host) 10841da177e4SLinus Torvalds { 108572d39feaSAlan Cox if (inb(host->addr + TUL_Int) & TSS_INT_PENDING) { 108672d39feaSAlan Cox if (host->semaph == 1) { 108772d39feaSAlan Cox outb(0x1F, host->addr + TUL_Mask); 10881da177e4SLinus Torvalds /* Disable Tulip SCSI Int */ 108972d39feaSAlan Cox host->semaph = 0; 10901da177e4SLinus Torvalds 109172d39feaSAlan Cox tulip_main(host); 10921da177e4SLinus Torvalds 109372d39feaSAlan Cox host->semaph = 1; 109472d39feaSAlan Cox outb(0x0F, host->addr + TUL_Mask); 109572d39feaSAlan Cox return 1; 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds } 109872d39feaSAlan Cox return 0; 10991da177e4SLinus Torvalds } 11001da177e4SLinus Torvalds 110172d39feaSAlan Cox static int tulip_main(struct initio_host * host) 11021da177e4SLinus Torvalds { 110372d39feaSAlan Cox struct scsi_ctrl_blk *scb; 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds for (;;) { 110672d39feaSAlan Cox tulip_scsi(host); /* Call tulip_scsi */ 11071da177e4SLinus Torvalds 110872d39feaSAlan Cox /* Walk the list of completed SCBs */ 110972d39feaSAlan Cox while ((scb = initio_find_done_scb(host)) != NULL) { /* find done entry */ 111072d39feaSAlan Cox if (scb->tastat == INI_QUEUE_FULL) { 111172d39feaSAlan Cox host->max_tags[scb->target] = 111272d39feaSAlan Cox host->act_tags[scb->target] - 1; 111372d39feaSAlan Cox scb->tastat = 0; 111472d39feaSAlan Cox initio_append_pend_scb(host, scb); 11151da177e4SLinus Torvalds continue; 11161da177e4SLinus Torvalds } 111772d39feaSAlan Cox if (!(scb->mode & SCM_RSENS)) { /* not in auto req. sense mode */ 111872d39feaSAlan Cox if (scb->tastat == 2) { 11191da177e4SLinus Torvalds 11201da177e4SLinus Torvalds /* clr sync. nego flag */ 11211da177e4SLinus Torvalds 112272d39feaSAlan Cox if (scb->flags & SCF_SENSE) { 112372d39feaSAlan Cox u8 len; 112472d39feaSAlan Cox len = scb->senselen; 11251da177e4SLinus Torvalds if (len == 0) 11261da177e4SLinus Torvalds len = 1; 112772d39feaSAlan Cox scb->buflen = scb->senselen; 112872d39feaSAlan Cox scb->bufptr = scb->senseptr; 112972d39feaSAlan Cox scb->flags &= ~(SCF_SG | SCF_DIR); /* for xfer_data_in */ 113072d39feaSAlan Cox /* so, we won't report wrong direction in xfer_data_in, 11311da177e4SLinus Torvalds and won't report HOST_DO_DU in state_6 */ 113272d39feaSAlan Cox scb->mode = SCM_RSENS; 113372d39feaSAlan Cox scb->ident &= 0xBF; /* Disable Disconnect */ 113472d39feaSAlan Cox scb->tagmsg = 0; 113572d39feaSAlan Cox scb->tastat = 0; 113672d39feaSAlan Cox scb->cdblen = 6; 113772d39feaSAlan Cox scb->cdb[0] = SCSICMD_RequestSense; 113872d39feaSAlan Cox scb->cdb[1] = 0; 113972d39feaSAlan Cox scb->cdb[2] = 0; 114072d39feaSAlan Cox scb->cdb[3] = 0; 114172d39feaSAlan Cox scb->cdb[4] = len; 114272d39feaSAlan Cox scb->cdb[5] = 0; 114372d39feaSAlan Cox initio_push_pend_scb(host, scb); 11441da177e4SLinus Torvalds break; 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds } else { /* in request sense mode */ 11481da177e4SLinus Torvalds 114972d39feaSAlan Cox if (scb->tastat == 2) { /* check contition status again after sending 11501da177e4SLinus Torvalds requset sense cmd 0x3 */ 115172d39feaSAlan Cox scb->hastat = HOST_BAD_PHAS; 11521da177e4SLinus Torvalds } 115372d39feaSAlan Cox scb->tastat = 2; 11541da177e4SLinus Torvalds } 115572d39feaSAlan Cox scb->flags |= SCF_DONE; 115672d39feaSAlan Cox if (scb->flags & SCF_POST) { 115772d39feaSAlan Cox /* FIXME: only one post method and lose casts */ 115872d39feaSAlan Cox (*scb->post) ((u8 *) host, (u8 *) scb); 11591da177e4SLinus Torvalds } 11601da177e4SLinus Torvalds } /* while */ 11611da177e4SLinus Torvalds /* find_active: */ 116272d39feaSAlan Cox if (inb(host->addr + TUL_SStatus0) & TSS_INT_PENDING) 11631da177e4SLinus Torvalds continue; 116472d39feaSAlan Cox if (host->active) /* return to OS and wait for xfer_done_ISR/Selected_ISR */ 11651da177e4SLinus Torvalds return 1; /* return to OS, enable interrupt */ 11661da177e4SLinus Torvalds /* Check pending SCB */ 116772d39feaSAlan Cox if (initio_find_first_pend_scb(host) == NULL) 11681da177e4SLinus Torvalds return 1; /* return to OS, enable interrupt */ 11691da177e4SLinus Torvalds } /* End of for loop */ 11701da177e4SLinus Torvalds /* statement won't reach here */ 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds 117372d39feaSAlan Cox static void tulip_scsi(struct initio_host * host) 11741da177e4SLinus Torvalds { 117572d39feaSAlan Cox struct scsi_ctrl_blk *scb; 117672d39feaSAlan Cox struct target_control *active_tc; 11771da177e4SLinus Torvalds 11781da177e4SLinus Torvalds /* make sure to service interrupt asap */ 117972d39feaSAlan Cox if ((host->jsstatus0 = inb(host->addr + TUL_SStatus0)) & TSS_INT_PENDING) { 118072d39feaSAlan Cox host->phase = host->jsstatus0 & TSS_PH_MASK; 118172d39feaSAlan Cox host->jsstatus1 = inb(host->addr + TUL_SStatus1); 118272d39feaSAlan Cox host->jsint = inb(host->addr + TUL_SInt); 118372d39feaSAlan Cox if (host->jsint & TSS_SCSIRST_INT) { /* SCSI bus reset detected */ 118472d39feaSAlan Cox int_initio_scsi_rst(host); 11851da177e4SLinus Torvalds return; 11861da177e4SLinus Torvalds } 118772d39feaSAlan Cox if (host->jsint & TSS_RESEL_INT) { /* if selected/reselected interrupt */ 118872d39feaSAlan Cox if (int_initio_resel(host) == 0) 118972d39feaSAlan Cox initio_next_state(host); 11901da177e4SLinus Torvalds return; 11911da177e4SLinus Torvalds } 119272d39feaSAlan Cox if (host->jsint & TSS_SEL_TIMEOUT) { 119372d39feaSAlan Cox int_initio_busfree(host); 11941da177e4SLinus Torvalds return; 11951da177e4SLinus Torvalds } 119672d39feaSAlan Cox if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */ 119772d39feaSAlan Cox int_initio_busfree(host); /* unexpected bus free or sel timeout */ 11981da177e4SLinus Torvalds return; 11991da177e4SLinus Torvalds } 120072d39feaSAlan Cox if (host->jsint & (TSS_FUNC_COMP | TSS_BUS_SERV)) { /* func complete or Bus service */ 120172d39feaSAlan Cox if ((scb = host->active) != NULL) 120272d39feaSAlan Cox initio_next_state(host); 12031da177e4SLinus Torvalds return; 12041da177e4SLinus Torvalds } 12051da177e4SLinus Torvalds } 120672d39feaSAlan Cox if (host->active != NULL) 12071da177e4SLinus Torvalds return; 12081da177e4SLinus Torvalds 120972d39feaSAlan Cox if ((scb = initio_find_first_pend_scb(host)) == NULL) 12101da177e4SLinus Torvalds return; 12111da177e4SLinus Torvalds 12121da177e4SLinus Torvalds /* program HBA's SCSI ID & target SCSI ID */ 121372d39feaSAlan Cox outb((host->scsi_id << 4) | (scb->target & 0x0F), 121472d39feaSAlan Cox host->addr + TUL_SScsiId); 121572d39feaSAlan Cox if (scb->opcode == ExecSCSI) { 121672d39feaSAlan Cox active_tc = &host->targets[scb->target]; 12171da177e4SLinus Torvalds 121872d39feaSAlan Cox if (scb->tagmsg) 121972d39feaSAlan Cox active_tc->drv_flags |= TCF_DRV_EN_TAG; 12201da177e4SLinus Torvalds else 122172d39feaSAlan Cox active_tc->drv_flags &= ~TCF_DRV_EN_TAG; 12221da177e4SLinus Torvalds 122372d39feaSAlan Cox outb(active_tc->js_period, host->addr + TUL_SPeriod); 122472d39feaSAlan Cox if ((active_tc->flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) { /* do wdtr negotiation */ 122572d39feaSAlan Cox initio_select_atn_stop(host, scb); 12261da177e4SLinus Torvalds } else { 122772d39feaSAlan Cox if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { /* do sync negotiation */ 122872d39feaSAlan Cox initio_select_atn_stop(host, scb); 12291da177e4SLinus Torvalds } else { 123072d39feaSAlan Cox if (scb->tagmsg) 123172d39feaSAlan Cox initio_select_atn3(host, scb); 12321da177e4SLinus Torvalds else 123372d39feaSAlan Cox initio_select_atn(host, scb); 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds } 123672d39feaSAlan Cox if (scb->flags & SCF_POLL) { 123772d39feaSAlan Cox while (wait_tulip(host) != -1) { 123872d39feaSAlan Cox if (initio_next_state(host) == -1) 12391da177e4SLinus Torvalds break; 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds } 124272d39feaSAlan Cox } else if (scb->opcode == BusDevRst) { 124372d39feaSAlan Cox initio_select_atn_stop(host, scb); 124472d39feaSAlan Cox scb->next_state = 8; 124572d39feaSAlan Cox if (scb->flags & SCF_POLL) { 124672d39feaSAlan Cox while (wait_tulip(host) != -1) { 124772d39feaSAlan Cox if (initio_next_state(host) == -1) 12481da177e4SLinus Torvalds break; 12491da177e4SLinus Torvalds } 12501da177e4SLinus Torvalds } 125172d39feaSAlan Cox } else if (scb->opcode == AbortCmd) { 125272d39feaSAlan Cox if (initio_abort_srb(host, scb->srb) != 0) { 125372d39feaSAlan Cox initio_unlink_pend_scb(host, scb); 125472d39feaSAlan Cox initio_release_scb(host, scb); 12551da177e4SLinus Torvalds } else { 125672d39feaSAlan Cox scb->opcode = BusDevRst; 125772d39feaSAlan Cox initio_select_atn_stop(host, scb); 125872d39feaSAlan Cox scb->next_state = 8; 12591da177e4SLinus Torvalds } 12601da177e4SLinus Torvalds } else { 126172d39feaSAlan Cox initio_unlink_pend_scb(host, scb); 126272d39feaSAlan Cox scb->hastat = 0x16; /* bad command */ 126372d39feaSAlan Cox initio_append_done_scb(host, scb); 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds return; 12661da177e4SLinus Torvalds } 12671da177e4SLinus Torvalds 126872d39feaSAlan Cox /** 126972d39feaSAlan Cox * initio_next_state - Next SCSI state 127072d39feaSAlan Cox * @host: InitIO host we are processing 127172d39feaSAlan Cox * 127272d39feaSAlan Cox * Progress the active command block along the state machine 127372d39feaSAlan Cox * until we hit a state which we must wait for activity to occur. 127472d39feaSAlan Cox * 127572d39feaSAlan Cox * Returns zero or a negative code. 127672d39feaSAlan Cox */ 12771da177e4SLinus Torvalds 127872d39feaSAlan Cox static int initio_next_state(struct initio_host * host) 12791da177e4SLinus Torvalds { 12801da177e4SLinus Torvalds int next; 12811da177e4SLinus Torvalds 128272d39feaSAlan Cox next = host->active->next_state; 12831da177e4SLinus Torvalds for (;;) { 12841da177e4SLinus Torvalds switch (next) { 12851da177e4SLinus Torvalds case 1: 128672d39feaSAlan Cox next = initio_state_1(host); 12871da177e4SLinus Torvalds break; 12881da177e4SLinus Torvalds case 2: 128972d39feaSAlan Cox next = initio_state_2(host); 12901da177e4SLinus Torvalds break; 12911da177e4SLinus Torvalds case 3: 129272d39feaSAlan Cox next = initio_state_3(host); 12931da177e4SLinus Torvalds break; 12941da177e4SLinus Torvalds case 4: 129572d39feaSAlan Cox next = initio_state_4(host); 12961da177e4SLinus Torvalds break; 12971da177e4SLinus Torvalds case 5: 129872d39feaSAlan Cox next = initio_state_5(host); 12991da177e4SLinus Torvalds break; 13001da177e4SLinus Torvalds case 6: 130172d39feaSAlan Cox next = initio_state_6(host); 13021da177e4SLinus Torvalds break; 13031da177e4SLinus Torvalds case 7: 130472d39feaSAlan Cox next = initio_state_7(host); 13051da177e4SLinus Torvalds break; 13061da177e4SLinus Torvalds case 8: 130772d39feaSAlan Cox return initio_bus_device_reset(host); 13081da177e4SLinus Torvalds default: 130972d39feaSAlan Cox return initio_bad_seq(host); 13101da177e4SLinus Torvalds } 13111da177e4SLinus Torvalds if (next <= 0) 13121da177e4SLinus Torvalds return next; 13131da177e4SLinus Torvalds } 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds 13161da177e4SLinus Torvalds 131772d39feaSAlan Cox /** 131872d39feaSAlan Cox * initio_state_1 - SCSI state machine 131972d39feaSAlan Cox * @host: InitIO host we are controlling 132072d39feaSAlan Cox * 132172d39feaSAlan Cox * Perform SCSI state processing for Select/Attention/Stop 132272d39feaSAlan Cox */ 132372d39feaSAlan Cox 132472d39feaSAlan Cox static int initio_state_1(struct initio_host * host) 13251da177e4SLinus Torvalds { 132672d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 132772d39feaSAlan Cox struct target_control *active_tc = host->active_tc; 13281da177e4SLinus Torvalds #if DEBUG_STATE 13291da177e4SLinus Torvalds printk("-s1-"); 13301da177e4SLinus Torvalds #endif 13311da177e4SLinus Torvalds 133272d39feaSAlan Cox /* Move the SCB from pending to busy */ 133372d39feaSAlan Cox initio_unlink_pend_scb(host, scb); 133472d39feaSAlan Cox initio_append_busy_scb(host, scb); 13351da177e4SLinus Torvalds 133672d39feaSAlan Cox outb(active_tc->sconfig0, host->addr + TUL_SConfig ); 13371da177e4SLinus Torvalds /* ATN on */ 133872d39feaSAlan Cox if (host->phase == MSG_OUT) { 133972d39feaSAlan Cox outb(TSC_EN_BUS_IN | TSC_HW_RESELECT, host->addr + TUL_SCtrl1); 134072d39feaSAlan Cox outb(scb->ident, host->addr + TUL_SFifo); 13411da177e4SLinus Torvalds 134272d39feaSAlan Cox if (scb->tagmsg) { 134372d39feaSAlan Cox outb(scb->tagmsg, host->addr + TUL_SFifo); 134472d39feaSAlan Cox outb(scb->tagid, host->addr + TUL_SFifo); 13451da177e4SLinus Torvalds } 134672d39feaSAlan Cox if ((active_tc->flags & (TCF_WDTR_DONE | TCF_NO_WDTR)) == 0) { 134772d39feaSAlan Cox active_tc->flags |= TCF_WDTR_DONE; 134872d39feaSAlan Cox outb(MSG_EXTEND, host->addr + TUL_SFifo); 134972d39feaSAlan Cox outb(2, host->addr + TUL_SFifo); /* Extended msg length */ 135072d39feaSAlan Cox outb(3, host->addr + TUL_SFifo); /* Sync request */ 135172d39feaSAlan Cox outb(1, host->addr + TUL_SFifo); /* Start from 16 bits */ 135272d39feaSAlan Cox } else if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) { 135372d39feaSAlan Cox active_tc->flags |= TCF_SYNC_DONE; 135472d39feaSAlan Cox outb(MSG_EXTEND, host->addr + TUL_SFifo); 135572d39feaSAlan Cox outb(3, host->addr + TUL_SFifo); /* extended msg length */ 135672d39feaSAlan Cox outb(1, host->addr + TUL_SFifo); /* sync request */ 135772d39feaSAlan Cox outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo); 135872d39feaSAlan Cox outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */ 13591da177e4SLinus Torvalds } 136072d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 136172d39feaSAlan Cox if (wait_tulip(host) == -1) 136272d39feaSAlan Cox return -1; 13631da177e4SLinus Torvalds } 136472d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 136572d39feaSAlan Cox outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)), host->addr + TUL_SSignal); 136672d39feaSAlan Cox /* Into before CDB xfer */ 136772d39feaSAlan Cox return 3; 13681da177e4SLinus Torvalds } 13691da177e4SLinus Torvalds 13701da177e4SLinus Torvalds 137172d39feaSAlan Cox /** 137272d39feaSAlan Cox * initio_state_2 - SCSI state machine 137372d39feaSAlan Cox * @host: InitIO host we are controlling 137472d39feaSAlan Cox * 137572d39feaSAlan Cox * state after selection with attention 137672d39feaSAlan Cox * state after selection with attention3 137772d39feaSAlan Cox */ 137872d39feaSAlan Cox 137972d39feaSAlan Cox static int initio_state_2(struct initio_host * host) 13801da177e4SLinus Torvalds { 138172d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 138272d39feaSAlan Cox struct target_control *active_tc = host->active_tc; 13831da177e4SLinus Torvalds #if DEBUG_STATE 13841da177e4SLinus Torvalds printk("-s2-"); 13851da177e4SLinus Torvalds #endif 13861da177e4SLinus Torvalds 138772d39feaSAlan Cox initio_unlink_pend_scb(host, scb); 138872d39feaSAlan Cox initio_append_busy_scb(host, scb); 13891da177e4SLinus Torvalds 139072d39feaSAlan Cox outb(active_tc->sconfig0, host->addr + TUL_SConfig); 13911da177e4SLinus Torvalds 139272d39feaSAlan Cox if (host->jsstatus1 & TSS_CMD_PH_CMP) 139372d39feaSAlan Cox return 4; 139472d39feaSAlan Cox 139572d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 139672d39feaSAlan Cox outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)), host->addr + TUL_SSignal); 139772d39feaSAlan Cox /* Into before CDB xfer */ 139872d39feaSAlan Cox return 3; 13991da177e4SLinus Torvalds } 14001da177e4SLinus Torvalds 140172d39feaSAlan Cox /** 140272d39feaSAlan Cox * initio_state_3 - SCSI state machine 140372d39feaSAlan Cox * @host: InitIO host we are controlling 140472d39feaSAlan Cox * 140572d39feaSAlan Cox * state before CDB xfer is done 140672d39feaSAlan Cox */ 140772d39feaSAlan Cox 140872d39feaSAlan Cox static int initio_state_3(struct initio_host * host) 14091da177e4SLinus Torvalds { 141072d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 141172d39feaSAlan Cox struct target_control *active_tc = host->active_tc; 14121da177e4SLinus Torvalds int i; 14131da177e4SLinus Torvalds 14141da177e4SLinus Torvalds #if DEBUG_STATE 14151da177e4SLinus Torvalds printk("-s3-"); 14161da177e4SLinus Torvalds #endif 14171da177e4SLinus Torvalds for (;;) { 141872d39feaSAlan Cox switch (host->phase) { 14191da177e4SLinus Torvalds case CMD_OUT: /* Command out phase */ 142072d39feaSAlan Cox for (i = 0; i < (int) scb->cdblen; i++) 142172d39feaSAlan Cox outb(scb->cdb[i], host->addr + TUL_SFifo); 142272d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 142372d39feaSAlan Cox if (wait_tulip(host) == -1) 142472d39feaSAlan Cox return -1; 142572d39feaSAlan Cox if (host->phase == CMD_OUT) 142672d39feaSAlan Cox return initio_bad_seq(host); 142772d39feaSAlan Cox return 4; 14281da177e4SLinus Torvalds 14291da177e4SLinus Torvalds case MSG_IN: /* Message in phase */ 143072d39feaSAlan Cox scb->next_state = 3; 143172d39feaSAlan Cox if (initio_msgin(host) == -1) 143272d39feaSAlan Cox return -1; 14331da177e4SLinus Torvalds break; 14341da177e4SLinus Torvalds 14351da177e4SLinus Torvalds case STATUS_IN: /* Status phase */ 143672d39feaSAlan Cox if (initio_status_msg(host) == -1) 143772d39feaSAlan Cox return -1; 14381da177e4SLinus Torvalds break; 14391da177e4SLinus Torvalds 14401da177e4SLinus Torvalds case MSG_OUT: /* Message out phase */ 144172d39feaSAlan Cox if (active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) { 144272d39feaSAlan Cox outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */ 144372d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 144472d39feaSAlan Cox if (wait_tulip(host) == -1) 144572d39feaSAlan Cox return -1; 14461da177e4SLinus Torvalds } else { 144772d39feaSAlan Cox active_tc->flags |= TCF_SYNC_DONE; 14481da177e4SLinus Torvalds 144972d39feaSAlan Cox outb(MSG_EXTEND, host->addr + TUL_SFifo); 145072d39feaSAlan Cox outb(3, host->addr + TUL_SFifo); /* ext. msg len */ 145172d39feaSAlan Cox outb(1, host->addr + TUL_SFifo); /* sync request */ 145272d39feaSAlan Cox outb(initio_rate_tbl[active_tc->flags & TCF_SCSI_RATE], host->addr + TUL_SFifo); 145372d39feaSAlan Cox outb(MAX_OFFSET, host->addr + TUL_SFifo); /* REQ/ACK offset */ 145472d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 145572d39feaSAlan Cox if (wait_tulip(host) == -1) 145672d39feaSAlan Cox return -1; 145772d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 145872d39feaSAlan Cox outb(inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7), host->addr + TUL_SSignal); 14591da177e4SLinus Torvalds 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds break; 14621da177e4SLinus Torvalds default: 146372d39feaSAlan Cox return initio_bad_seq(host); 14641da177e4SLinus Torvalds } 14651da177e4SLinus Torvalds } 14661da177e4SLinus Torvalds } 14671da177e4SLinus Torvalds 146872d39feaSAlan Cox /** 146972d39feaSAlan Cox * initio_state_4 - SCSI state machine 147072d39feaSAlan Cox * @host: InitIO host we are controlling 147172d39feaSAlan Cox * 147272d39feaSAlan Cox * SCSI state machine. State 4 147372d39feaSAlan Cox */ 14741da177e4SLinus Torvalds 147572d39feaSAlan Cox static int initio_state_4(struct initio_host * host) 14761da177e4SLinus Torvalds { 147772d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 14781da177e4SLinus Torvalds 14791da177e4SLinus Torvalds #if DEBUG_STATE 14801da177e4SLinus Torvalds printk("-s4-"); 14811da177e4SLinus Torvalds #endif 148272d39feaSAlan Cox if ((scb->flags & SCF_DIR) == SCF_NO_XF) { 148372d39feaSAlan Cox return 6; /* Go to state 6 (After data) */ 14841da177e4SLinus Torvalds } 14851da177e4SLinus Torvalds for (;;) { 148672d39feaSAlan Cox if (scb->buflen == 0) 148772d39feaSAlan Cox return 6; 14881da177e4SLinus Torvalds 148972d39feaSAlan Cox switch (host->phase) { 14901da177e4SLinus Torvalds 14911da177e4SLinus Torvalds case STATUS_IN: /* Status phase */ 149272d39feaSAlan Cox if ((scb->flags & SCF_DIR) != 0) /* if direction bit set then report data underrun */ 149372d39feaSAlan Cox scb->hastat = HOST_DO_DU; 149472d39feaSAlan Cox if ((initio_status_msg(host)) == -1) 149572d39feaSAlan Cox return -1; 14961da177e4SLinus Torvalds break; 14971da177e4SLinus Torvalds 14981da177e4SLinus Torvalds case MSG_IN: /* Message in phase */ 149972d39feaSAlan Cox scb->next_state = 0x4; 150072d39feaSAlan Cox if (initio_msgin(host) == -1) 150172d39feaSAlan Cox return -1; 15021da177e4SLinus Torvalds break; 15031da177e4SLinus Torvalds 15041da177e4SLinus Torvalds case MSG_OUT: /* Message out phase */ 150572d39feaSAlan Cox if (host->jsstatus0 & TSS_PAR_ERROR) { 150672d39feaSAlan Cox scb->buflen = 0; 150772d39feaSAlan Cox scb->hastat = HOST_DO_DU; 150872d39feaSAlan Cox if (initio_msgout_ide(host) == -1) 150972d39feaSAlan Cox return -1; 151072d39feaSAlan Cox return 6; 15111da177e4SLinus Torvalds } else { 151272d39feaSAlan Cox outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */ 151372d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 151472d39feaSAlan Cox if (wait_tulip(host) == -1) 151572d39feaSAlan Cox return -1; 15161da177e4SLinus Torvalds } 15171da177e4SLinus Torvalds break; 15181da177e4SLinus Torvalds 15191da177e4SLinus Torvalds case DATA_IN: /* Data in phase */ 152072d39feaSAlan Cox return initio_xfer_data_in(host); 15211da177e4SLinus Torvalds 15221da177e4SLinus Torvalds case DATA_OUT: /* Data out phase */ 152372d39feaSAlan Cox return initio_xfer_data_out(host); 15241da177e4SLinus Torvalds 15251da177e4SLinus Torvalds default: 152672d39feaSAlan Cox return initio_bad_seq(host); 15271da177e4SLinus Torvalds } 15281da177e4SLinus Torvalds } 15291da177e4SLinus Torvalds } 15301da177e4SLinus Torvalds 15311da177e4SLinus Torvalds 153272d39feaSAlan Cox /** 153372d39feaSAlan Cox * initio_state_5 - SCSI state machine 153472d39feaSAlan Cox * @host: InitIO host we are controlling 153572d39feaSAlan Cox * 153672d39feaSAlan Cox * State after dma xfer done or phase change before xfer done 153772d39feaSAlan Cox */ 153872d39feaSAlan Cox 153972d39feaSAlan Cox static int initio_state_5(struct initio_host * host) 15401da177e4SLinus Torvalds { 154172d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 15421da177e4SLinus Torvalds long cnt, xcnt; /* cannot use unsigned !! code: if (xcnt < 0) */ 15431da177e4SLinus Torvalds 15441da177e4SLinus Torvalds #if DEBUG_STATE 15451da177e4SLinus Torvalds printk("-s5-"); 15461da177e4SLinus Torvalds #endif 15471da177e4SLinus Torvalds /*------ get remaining count -------*/ 154872d39feaSAlan Cox cnt = inl(host->addr + TUL_SCnt0) & 0x0FFFFFF; 15491da177e4SLinus Torvalds 155072d39feaSAlan Cox if (inb(host->addr + TUL_XCmd) & 0x20) { 15511da177e4SLinus Torvalds /* ----------------------- DATA_IN ----------------------------- */ 15521da177e4SLinus Torvalds /* check scsi parity error */ 155372d39feaSAlan Cox if (host->jsstatus0 & TSS_PAR_ERROR) 155472d39feaSAlan Cox scb->hastat = HOST_DO_DU; 155572d39feaSAlan Cox if (inb(host->addr + TUL_XStatus) & XPEND) { /* DMA xfer pending, Send STOP */ 15561da177e4SLinus Torvalds /* tell Hardware scsi xfer has been terminated */ 155772d39feaSAlan Cox outb(inb(host->addr + TUL_XCtrl) | 0x80, host->addr + TUL_XCtrl); 15581da177e4SLinus Torvalds /* wait until DMA xfer not pending */ 155972d39feaSAlan Cox while (inb(host->addr + TUL_XStatus) & XPEND) 156072d39feaSAlan Cox cpu_relax(); 15611da177e4SLinus Torvalds } 15621da177e4SLinus Torvalds } else { 15631da177e4SLinus Torvalds /*-------- DATA OUT -----------*/ 156472d39feaSAlan Cox if ((inb(host->addr + TUL_SStatus1) & TSS_XFER_CMP) == 0) { 156572d39feaSAlan Cox if (host->active_tc->js_period & TSC_WIDE_SCSI) 156672d39feaSAlan Cox cnt += (inb(host->addr + TUL_SFifoCnt) & 0x1F) << 1; 15671da177e4SLinus Torvalds else 156872d39feaSAlan Cox cnt += (inb(host->addr + TUL_SFifoCnt) & 0x1F); 15691da177e4SLinus Torvalds } 157072d39feaSAlan Cox if (inb(host->addr + TUL_XStatus) & XPEND) { /* if DMA xfer is pending, abort DMA xfer */ 157172d39feaSAlan Cox outb(TAX_X_ABT, host->addr + TUL_XCmd); 15721da177e4SLinus Torvalds /* wait Abort DMA xfer done */ 157372d39feaSAlan Cox while ((inb(host->addr + TUL_Int) & XABT) == 0) 157472d39feaSAlan Cox cpu_relax(); 15751da177e4SLinus Torvalds } 157672d39feaSAlan Cox if ((cnt == 1) && (host->phase == DATA_OUT)) { 157772d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 157872d39feaSAlan Cox if (wait_tulip(host) == -1) 157972d39feaSAlan Cox return -1; 15801da177e4SLinus Torvalds cnt = 0; 15811da177e4SLinus Torvalds } else { 158272d39feaSAlan Cox if ((inb(host->addr + TUL_SStatus1) & TSS_XFER_CMP) == 0) 158372d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 15841da177e4SLinus Torvalds } 15851da177e4SLinus Torvalds } 15861da177e4SLinus Torvalds if (cnt == 0) { 158772d39feaSAlan Cox scb->buflen = 0; 158872d39feaSAlan Cox return 6; /* After Data */ 15891da177e4SLinus Torvalds } 15901da177e4SLinus Torvalds /* Update active data pointer */ 159172d39feaSAlan Cox xcnt = (long) scb->buflen - cnt; /* xcnt== bytes already xferred */ 159272d39feaSAlan Cox scb->buflen = (u32) cnt; /* cnt == bytes left to be xferred */ 159372d39feaSAlan Cox if (scb->flags & SCF_SG) { 159472d39feaSAlan Cox struct sg_entry *sgp; 159572d39feaSAlan Cox unsigned long i; 15961da177e4SLinus Torvalds 159772d39feaSAlan Cox sgp = &scb->sglist[scb->sgidx]; 159872d39feaSAlan Cox for (i = scb->sgidx; i < scb->sgmax; sgp++, i++) { 159972d39feaSAlan Cox xcnt -= (long) sgp->len; 16001da177e4SLinus Torvalds if (xcnt < 0) { /* this sgp xfer half done */ 160172d39feaSAlan Cox xcnt += (long) sgp->len; /* xcnt == bytes xferred in this sgp */ 160272d39feaSAlan Cox sgp->data += (u32) xcnt; /* new ptr to be xfer */ 160372d39feaSAlan Cox sgp->len -= (u32) xcnt; /* new len to be xfer */ 160472d39feaSAlan Cox scb->bufptr += ((u32) (i - scb->sgidx) << 3); 16051da177e4SLinus Torvalds /* new SG table ptr */ 160672d39feaSAlan Cox scb->sglen = (u8) (scb->sgmax - i); 16071da177e4SLinus Torvalds /* new SG table len */ 160872d39feaSAlan Cox scb->sgidx = (u16) i; 16091da177e4SLinus Torvalds /* for next disc and come in this loop */ 161072d39feaSAlan Cox return 4; /* Go to state 4 */ 16111da177e4SLinus Torvalds } 16121da177e4SLinus Torvalds /* else (xcnt >= 0 , i.e. this sgp already xferred */ 16131da177e4SLinus Torvalds } /* for */ 161472d39feaSAlan Cox return 6; /* Go to state 6 */ 16151da177e4SLinus Torvalds } else { 161672d39feaSAlan Cox scb->bufptr += (u32) xcnt; 16171da177e4SLinus Torvalds } 161872d39feaSAlan Cox return 4; /* Go to state 4 */ 16191da177e4SLinus Torvalds } 16201da177e4SLinus Torvalds 162172d39feaSAlan Cox /** 162272d39feaSAlan Cox * initio_state_6 - SCSI state machine 162372d39feaSAlan Cox * @host: InitIO host we are controlling 162472d39feaSAlan Cox * 162572d39feaSAlan Cox * State after Data phase 162672d39feaSAlan Cox */ 162772d39feaSAlan Cox 162872d39feaSAlan Cox static int initio_state_6(struct initio_host * host) 16291da177e4SLinus Torvalds { 163072d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 16311da177e4SLinus Torvalds 16321da177e4SLinus Torvalds #if DEBUG_STATE 16331da177e4SLinus Torvalds printk("-s6-"); 16341da177e4SLinus Torvalds #endif 16351da177e4SLinus Torvalds for (;;) { 163672d39feaSAlan Cox switch (host->phase) { 16371da177e4SLinus Torvalds case STATUS_IN: /* Status phase */ 163872d39feaSAlan Cox if ((initio_status_msg(host)) == -1) 163972d39feaSAlan Cox return -1; 16401da177e4SLinus Torvalds break; 16411da177e4SLinus Torvalds 16421da177e4SLinus Torvalds case MSG_IN: /* Message in phase */ 164372d39feaSAlan Cox scb->next_state = 6; 164472d39feaSAlan Cox if ((initio_msgin(host)) == -1) 164572d39feaSAlan Cox return -1; 16461da177e4SLinus Torvalds break; 16471da177e4SLinus Torvalds 16481da177e4SLinus Torvalds case MSG_OUT: /* Message out phase */ 164972d39feaSAlan Cox outb(MSG_NOP, host->addr + TUL_SFifo); /* msg nop */ 165072d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 165172d39feaSAlan Cox if (wait_tulip(host) == -1) 165272d39feaSAlan Cox return -1; 16531da177e4SLinus Torvalds break; 16541da177e4SLinus Torvalds 16551da177e4SLinus Torvalds case DATA_IN: /* Data in phase */ 165672d39feaSAlan Cox return initio_xpad_in(host); 16571da177e4SLinus Torvalds 16581da177e4SLinus Torvalds case DATA_OUT: /* Data out phase */ 165972d39feaSAlan Cox return initio_xpad_out(host); 16601da177e4SLinus Torvalds 16611da177e4SLinus Torvalds default: 166272d39feaSAlan Cox return initio_bad_seq(host); 16631da177e4SLinus Torvalds } 16641da177e4SLinus Torvalds } 16651da177e4SLinus Torvalds } 16661da177e4SLinus Torvalds 166772d39feaSAlan Cox /** 166872d39feaSAlan Cox * initio_state_7 - SCSI state machine 166972d39feaSAlan Cox * @host: InitIO host we are controlling 167072d39feaSAlan Cox * 167172d39feaSAlan Cox */ 167272d39feaSAlan Cox 167372d39feaSAlan Cox int initio_state_7(struct initio_host * host) 16741da177e4SLinus Torvalds { 16751da177e4SLinus Torvalds int cnt, i; 16761da177e4SLinus Torvalds 16771da177e4SLinus Torvalds #if DEBUG_STATE 16781da177e4SLinus Torvalds printk("-s7-"); 16791da177e4SLinus Torvalds #endif 16801da177e4SLinus Torvalds /* flush SCSI FIFO */ 168172d39feaSAlan Cox cnt = inb(host->addr + TUL_SFifoCnt) & 0x1F; 16821da177e4SLinus Torvalds if (cnt) { 16831da177e4SLinus Torvalds for (i = 0; i < cnt; i++) 168472d39feaSAlan Cox inb(host->addr + TUL_SFifo); 16851da177e4SLinus Torvalds } 168672d39feaSAlan Cox switch (host->phase) { 16871da177e4SLinus Torvalds case DATA_IN: /* Data in phase */ 16881da177e4SLinus Torvalds case DATA_OUT: /* Data out phase */ 168972d39feaSAlan Cox return initio_bad_seq(host); 16901da177e4SLinus Torvalds default: 169172d39feaSAlan Cox return 6; /* Go to state 6 */ 16921da177e4SLinus Torvalds } 16931da177e4SLinus Torvalds } 16941da177e4SLinus Torvalds 169572d39feaSAlan Cox /** 169672d39feaSAlan Cox * initio_xfer_data_in - Commence data input 169772d39feaSAlan Cox * @host: InitIO host in use 169872d39feaSAlan Cox * 169972d39feaSAlan Cox * Commence a block of data transfer. The transfer itself will 170072d39feaSAlan Cox * be managed by the controller and we will get a completion (or 170172d39feaSAlan Cox * failure) interrupt. 170272d39feaSAlan Cox */ 170372d39feaSAlan Cox static int initio_xfer_data_in(struct initio_host * host) 17041da177e4SLinus Torvalds { 170572d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 17061da177e4SLinus Torvalds 170772d39feaSAlan Cox if ((scb->flags & SCF_DIR) == SCF_DOUT) 170872d39feaSAlan Cox return 6; /* wrong direction */ 17091da177e4SLinus Torvalds 171072d39feaSAlan Cox outl(scb->buflen, host->addr + TUL_SCnt0); 171172d39feaSAlan Cox outb(TSC_XF_DMA_IN, host->addr + TUL_SCmd); /* 7/25/95 */ 17121da177e4SLinus Torvalds 171372d39feaSAlan Cox if (scb->flags & SCF_SG) { /* S/G xfer */ 171472d39feaSAlan Cox outl(((u32) scb->sglen) << 3, host->addr + TUL_XCntH); 171572d39feaSAlan Cox outl(scb->bufptr, host->addr + TUL_XAddH); 171672d39feaSAlan Cox outb(TAX_SG_IN, host->addr + TUL_XCmd); 17171da177e4SLinus Torvalds } else { 171872d39feaSAlan Cox outl(scb->buflen, host->addr + TUL_XCntH); 171972d39feaSAlan Cox outl(scb->bufptr, host->addr + TUL_XAddH); 172072d39feaSAlan Cox outb(TAX_X_IN, host->addr + TUL_XCmd); 17211da177e4SLinus Torvalds } 172272d39feaSAlan Cox scb->next_state = 0x5; 172372d39feaSAlan Cox return 0; /* return to OS, wait xfer done , let jas_isr come in */ 17241da177e4SLinus Torvalds } 17251da177e4SLinus Torvalds 172672d39feaSAlan Cox /** 172772d39feaSAlan Cox * initio_xfer_data_out - Commence data output 172872d39feaSAlan Cox * @host: InitIO host in use 172972d39feaSAlan Cox * 173072d39feaSAlan Cox * Commence a block of data transfer. The transfer itself will 173172d39feaSAlan Cox * be managed by the controller and we will get a completion (or 173272d39feaSAlan Cox * failure) interrupt. 173372d39feaSAlan Cox */ 17341da177e4SLinus Torvalds 173572d39feaSAlan Cox static int initio_xfer_data_out(struct initio_host * host) 17361da177e4SLinus Torvalds { 173772d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 17381da177e4SLinus Torvalds 173972d39feaSAlan Cox if ((scb->flags & SCF_DIR) == SCF_DIN) 174072d39feaSAlan Cox return 6; /* wrong direction */ 17411da177e4SLinus Torvalds 174272d39feaSAlan Cox outl(scb->buflen, host->addr + TUL_SCnt0); 174372d39feaSAlan Cox outb(TSC_XF_DMA_OUT, host->addr + TUL_SCmd); 174472d39feaSAlan Cox 174572d39feaSAlan Cox if (scb->flags & SCF_SG) { /* S/G xfer */ 174672d39feaSAlan Cox outl(((u32) scb->sglen) << 3, host->addr + TUL_XCntH); 174772d39feaSAlan Cox outl(scb->bufptr, host->addr + TUL_XAddH); 174872d39feaSAlan Cox outb(TAX_SG_OUT, host->addr + TUL_XCmd); 17491da177e4SLinus Torvalds } else { 175072d39feaSAlan Cox outl(scb->buflen, host->addr + TUL_XCntH); 175172d39feaSAlan Cox outl(scb->bufptr, host->addr + TUL_XAddH); 175272d39feaSAlan Cox outb(TAX_X_OUT, host->addr + TUL_XCmd); 17531da177e4SLinus Torvalds } 17541da177e4SLinus Torvalds 175572d39feaSAlan Cox scb->next_state = 0x5; 175672d39feaSAlan Cox return 0; /* return to OS, wait xfer done , let jas_isr come in */ 17571da177e4SLinus Torvalds } 17581da177e4SLinus Torvalds 175972d39feaSAlan Cox int initio_xpad_in(struct initio_host * host) 17601da177e4SLinus Torvalds { 176172d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 176272d39feaSAlan Cox struct target_control *active_tc = host->active_tc; 17631da177e4SLinus Torvalds 176472d39feaSAlan Cox if ((scb->flags & SCF_DIR) != SCF_NO_DCHK) 176572d39feaSAlan Cox scb->hastat = HOST_DO_DU; /* over run */ 17661da177e4SLinus Torvalds for (;;) { 176772d39feaSAlan Cox if (active_tc->js_period & TSC_WIDE_SCSI) 176872d39feaSAlan Cox outl(2, host->addr + TUL_SCnt0); 17691da177e4SLinus Torvalds else 177072d39feaSAlan Cox outl(1, host->addr + TUL_SCnt0); 17711da177e4SLinus Torvalds 177272d39feaSAlan Cox outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd); 177372d39feaSAlan Cox if (wait_tulip(host) == -1) 177472d39feaSAlan Cox return -1; 177572d39feaSAlan Cox if (host->phase != DATA_IN) { 177672d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 177772d39feaSAlan Cox return 6; 17781da177e4SLinus Torvalds } 177972d39feaSAlan Cox inb(host->addr + TUL_SFifo); 17801da177e4SLinus Torvalds } 17811da177e4SLinus Torvalds } 17821da177e4SLinus Torvalds 178372d39feaSAlan Cox int initio_xpad_out(struct initio_host * host) 17841da177e4SLinus Torvalds { 178572d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 178672d39feaSAlan Cox struct target_control *active_tc = host->active_tc; 17871da177e4SLinus Torvalds 178872d39feaSAlan Cox if ((scb->flags & SCF_DIR) != SCF_NO_DCHK) 178972d39feaSAlan Cox scb->hastat = HOST_DO_DU; /* over run */ 17901da177e4SLinus Torvalds for (;;) { 179172d39feaSAlan Cox if (active_tc->js_period & TSC_WIDE_SCSI) 179272d39feaSAlan Cox outl(2, host->addr + TUL_SCnt0); 17931da177e4SLinus Torvalds else 179472d39feaSAlan Cox outl(1, host->addr + TUL_SCnt0); 17951da177e4SLinus Torvalds 179672d39feaSAlan Cox outb(0, host->addr + TUL_SFifo); 179772d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 179872d39feaSAlan Cox if ((wait_tulip(host)) == -1) 179972d39feaSAlan Cox return -1; 180072d39feaSAlan Cox if (host->phase != DATA_OUT) { /* Disable wide CPU to allow read 16 bits */ 180172d39feaSAlan Cox outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); 180272d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 180372d39feaSAlan Cox return 6; 18041da177e4SLinus Torvalds } 18051da177e4SLinus Torvalds } 18061da177e4SLinus Torvalds } 18071da177e4SLinus Torvalds 180872d39feaSAlan Cox int initio_status_msg(struct initio_host * host) 18091da177e4SLinus Torvalds { /* status & MSG_IN */ 181072d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 181172d39feaSAlan Cox u8 msg; 18121da177e4SLinus Torvalds 181372d39feaSAlan Cox outb(TSC_CMD_COMP, host->addr + TUL_SCmd); 181472d39feaSAlan Cox if (wait_tulip(host) == -1) 181572d39feaSAlan Cox return -1; 181672d39feaSAlan Cox 18171da177e4SLinus Torvalds /* get status */ 181872d39feaSAlan Cox scb->tastat = inb(host->addr + TUL_SFifo); 18191da177e4SLinus Torvalds 182072d39feaSAlan Cox if (host->phase == MSG_OUT) { 182172d39feaSAlan Cox if (host->jsstatus0 & TSS_PAR_ERROR) 182272d39feaSAlan Cox outb(MSG_PARITY, host->addr + TUL_SFifo); 182372d39feaSAlan Cox else 182472d39feaSAlan Cox outb(MSG_NOP, host->addr + TUL_SFifo); 182572d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 182672d39feaSAlan Cox return wait_tulip(host); 18271da177e4SLinus Torvalds } 182872d39feaSAlan Cox if (host->phase == MSG_IN) { 182972d39feaSAlan Cox msg = inb(host->addr + TUL_SFifo); 183072d39feaSAlan Cox if (host->jsstatus0 & TSS_PAR_ERROR) { /* Parity error */ 183172d39feaSAlan Cox if ((initio_msgin_accept(host)) == -1) 183272d39feaSAlan Cox return -1; 183372d39feaSAlan Cox if (host->phase != MSG_OUT) 183472d39feaSAlan Cox return initio_bad_seq(host); 183572d39feaSAlan Cox outb(MSG_PARITY, host->addr + TUL_SFifo); 183672d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 183772d39feaSAlan Cox return wait_tulip(host); 18381da177e4SLinus Torvalds } 18391da177e4SLinus Torvalds if (msg == 0) { /* Command complete */ 18401da177e4SLinus Torvalds 184172d39feaSAlan Cox if ((scb->tastat & 0x18) == 0x10) /* No link support */ 184272d39feaSAlan Cox return initio_bad_seq(host); 184372d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 184472d39feaSAlan Cox outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd); 184572d39feaSAlan Cox return initio_wait_done_disc(host); 18461da177e4SLinus Torvalds 18471da177e4SLinus Torvalds } 184872d39feaSAlan Cox if (msg == MSG_LINK_COMP || msg == MSG_LINK_FLAG) { 184972d39feaSAlan Cox if ((scb->tastat & 0x18) == 0x10) 185072d39feaSAlan Cox return initio_msgin_accept(host); 18511da177e4SLinus Torvalds } 18521da177e4SLinus Torvalds } 185372d39feaSAlan Cox return initio_bad_seq(host); 18541da177e4SLinus Torvalds } 18551da177e4SLinus Torvalds 18561da177e4SLinus Torvalds 18571da177e4SLinus Torvalds /* scsi bus free */ 185872d39feaSAlan Cox int int_initio_busfree(struct initio_host * host) 18591da177e4SLinus Torvalds { 186072d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 18611da177e4SLinus Torvalds 186272d39feaSAlan Cox if (scb != NULL) { 186372d39feaSAlan Cox if (scb->status & SCB_SELECT) { /* selection timeout */ 186472d39feaSAlan Cox initio_unlink_pend_scb(host, scb); 186572d39feaSAlan Cox scb->hastat = HOST_SEL_TOUT; 186672d39feaSAlan Cox initio_append_done_scb(host, scb); 18671da177e4SLinus Torvalds } else { /* Unexpected bus free */ 186872d39feaSAlan Cox initio_unlink_busy_scb(host, scb); 186972d39feaSAlan Cox scb->hastat = HOST_BUS_FREE; 187072d39feaSAlan Cox initio_append_done_scb(host, scb); 18711da177e4SLinus Torvalds } 187272d39feaSAlan Cox host->active = NULL; 187372d39feaSAlan Cox host->active_tc = NULL; 18741da177e4SLinus Torvalds } 187572d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */ 187672d39feaSAlan Cox outb(TSC_INITDEFAULT, host->addr + TUL_SConfig); 187772d39feaSAlan Cox outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */ 187872d39feaSAlan Cox return -1; 18791da177e4SLinus Torvalds } 18801da177e4SLinus Torvalds 18811da177e4SLinus Torvalds 188272d39feaSAlan Cox /** 188372d39feaSAlan Cox * int_initio_scsi_rst - SCSI reset occurred 188472d39feaSAlan Cox * @host: Host seeing the reset 188572d39feaSAlan Cox * 188672d39feaSAlan Cox * A SCSI bus reset has occurred. Clean up any pending transfer 188772d39feaSAlan Cox * the hardware is doing by DMA and then abort all active and 188872d39feaSAlan Cox * disconnected commands. The mid layer should sort the rest out 188972d39feaSAlan Cox * for us 189072d39feaSAlan Cox */ 189172d39feaSAlan Cox 189272d39feaSAlan Cox static int int_initio_scsi_rst(struct initio_host * host) 18931da177e4SLinus Torvalds { 189472d39feaSAlan Cox struct scsi_ctrl_blk *scb; 18951da177e4SLinus Torvalds int i; 18961da177e4SLinus Torvalds 18971da177e4SLinus Torvalds /* if DMA xfer is pending, abort DMA xfer */ 189872d39feaSAlan Cox if (inb(host->addr + TUL_XStatus) & 0x01) { 189972d39feaSAlan Cox outb(TAX_X_ABT | TAX_X_CLR_FIFO, host->addr + TUL_XCmd); 19001da177e4SLinus Torvalds /* wait Abort DMA xfer done */ 190172d39feaSAlan Cox while ((inb(host->addr + TUL_Int) & 0x04) == 0) 190272d39feaSAlan Cox cpu_relax(); 190372d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 19041da177e4SLinus Torvalds } 19051da177e4SLinus Torvalds /* Abort all active & disconnected scb */ 190672d39feaSAlan Cox while ((scb = initio_pop_busy_scb(host)) != NULL) { 190772d39feaSAlan Cox scb->hastat = HOST_BAD_PHAS; 190872d39feaSAlan Cox initio_append_done_scb(host, scb); 19091da177e4SLinus Torvalds } 191072d39feaSAlan Cox host->active = NULL; 191172d39feaSAlan Cox host->active_tc = NULL; 19121da177e4SLinus Torvalds 19131da177e4SLinus Torvalds /* clr sync nego. done flag */ 191472d39feaSAlan Cox for (i = 0; i < host->max_tar; i++) 191572d39feaSAlan Cox host->targets[i].flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE); 191672d39feaSAlan Cox return -1; 19171da177e4SLinus Torvalds } 19181da177e4SLinus Torvalds 191972d39feaSAlan Cox /** 192025985edcSLucas De Marchi * int_initio_scsi_resel - Reselection occurred 192172d39feaSAlan Cox * @host: InitIO host adapter 192272d39feaSAlan Cox * 192372d39feaSAlan Cox * A SCSI reselection event has been signalled and the interrupt 192472d39feaSAlan Cox * is now being processed. Work out which command block needs attention 192572d39feaSAlan Cox * and continue processing that command. 192672d39feaSAlan Cox */ 19271da177e4SLinus Torvalds 192872d39feaSAlan Cox int int_initio_resel(struct initio_host * host) 19291da177e4SLinus Torvalds { 193072d39feaSAlan Cox struct scsi_ctrl_blk *scb; 193172d39feaSAlan Cox struct target_control *active_tc; 193272d39feaSAlan Cox u8 tag, msg = 0; 193372d39feaSAlan Cox u8 tar, lun; 19341da177e4SLinus Torvalds 193572d39feaSAlan Cox if ((scb = host->active) != NULL) { 193672d39feaSAlan Cox /* FIXME: Why check and not just clear ? */ 193772d39feaSAlan Cox if (scb->status & SCB_SELECT) /* if waiting for selection complete */ 193872d39feaSAlan Cox scb->status &= ~SCB_SELECT; 193972d39feaSAlan Cox host->active = NULL; 19401da177e4SLinus Torvalds } 19411da177e4SLinus Torvalds /* --------- get target id---------------------- */ 194272d39feaSAlan Cox tar = inb(host->addr + TUL_SBusId); 19431da177e4SLinus Torvalds /* ------ get LUN from Identify message----------- */ 194472d39feaSAlan Cox lun = inb(host->addr + TUL_SIdent) & 0x0F; 19451da177e4SLinus Torvalds /* 07/22/98 from 0x1F -> 0x0F */ 194672d39feaSAlan Cox active_tc = &host->targets[tar]; 194772d39feaSAlan Cox host->active_tc = active_tc; 194872d39feaSAlan Cox outb(active_tc->sconfig0, host->addr + TUL_SConfig); 194972d39feaSAlan Cox outb(active_tc->js_period, host->addr + TUL_SPeriod); 19501da177e4SLinus Torvalds 19511da177e4SLinus Torvalds /* ------------- tag queueing ? ------------------- */ 195272d39feaSAlan Cox if (active_tc->drv_flags & TCF_DRV_EN_TAG) { 195372d39feaSAlan Cox if ((initio_msgin_accept(host)) == -1) 195472d39feaSAlan Cox return -1; 195572d39feaSAlan Cox if (host->phase != MSG_IN) 19561da177e4SLinus Torvalds goto no_tag; 195772d39feaSAlan Cox outl(1, host->addr + TUL_SCnt0); 195872d39feaSAlan Cox outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd); 195972d39feaSAlan Cox if (wait_tulip(host) == -1) 196072d39feaSAlan Cox return -1; 196172d39feaSAlan Cox msg = inb(host->addr + TUL_SFifo); /* Read Tag Message */ 19621da177e4SLinus Torvalds 196372d39feaSAlan Cox if (msg < MSG_STAG || msg > MSG_OTAG) /* Is simple Tag */ 19641da177e4SLinus Torvalds goto no_tag; 19651da177e4SLinus Torvalds 196672d39feaSAlan Cox if (initio_msgin_accept(host) == -1) 196772d39feaSAlan Cox return -1; 19681da177e4SLinus Torvalds 196972d39feaSAlan Cox if (host->phase != MSG_IN) 19701da177e4SLinus Torvalds goto no_tag; 19711da177e4SLinus Torvalds 197272d39feaSAlan Cox outl(1, host->addr + TUL_SCnt0); 197372d39feaSAlan Cox outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd); 197472d39feaSAlan Cox if (wait_tulip(host) == -1) 197572d39feaSAlan Cox return -1; 197672d39feaSAlan Cox tag = inb(host->addr + TUL_SFifo); /* Read Tag ID */ 197772d39feaSAlan Cox scb = host->scb + tag; 197872d39feaSAlan Cox if (scb->target != tar || scb->lun != lun) { 197972d39feaSAlan Cox return initio_msgout_abort_tag(host); 19801da177e4SLinus Torvalds } 198172d39feaSAlan Cox if (scb->status != SCB_BUSY) { /* 03/24/95 */ 198272d39feaSAlan Cox return initio_msgout_abort_tag(host); 19831da177e4SLinus Torvalds } 198472d39feaSAlan Cox host->active = scb; 198572d39feaSAlan Cox if ((initio_msgin_accept(host)) == -1) 198672d39feaSAlan Cox return -1; 19871da177e4SLinus Torvalds } else { /* No tag */ 19881da177e4SLinus Torvalds no_tag: 198972d39feaSAlan Cox if ((scb = initio_find_busy_scb(host, tar | (lun << 8))) == NULL) { 199072d39feaSAlan Cox return initio_msgout_abort_targ(host); 19911da177e4SLinus Torvalds } 199272d39feaSAlan Cox host->active = scb; 199372d39feaSAlan Cox if (!(active_tc->drv_flags & TCF_DRV_EN_TAG)) { 199472d39feaSAlan Cox if ((initio_msgin_accept(host)) == -1) 199572d39feaSAlan Cox return -1; 19961da177e4SLinus Torvalds } 19971da177e4SLinus Torvalds } 19981da177e4SLinus Torvalds return 0; 19991da177e4SLinus Torvalds } 20001da177e4SLinus Torvalds 200172d39feaSAlan Cox /** 200272d39feaSAlan Cox * int_initio_bad_seq - out of phase 200372d39feaSAlan Cox * @host: InitIO host flagging event 200472d39feaSAlan Cox * 200572d39feaSAlan Cox * We have ended up out of phase somehow. Reset the host controller 200672d39feaSAlan Cox * and throw all our toys out of the pram. Let the midlayer clean up 200772d39feaSAlan Cox */ 20081da177e4SLinus Torvalds 200972d39feaSAlan Cox static int int_initio_bad_seq(struct initio_host * host) 20101da177e4SLinus Torvalds { /* target wrong phase */ 201172d39feaSAlan Cox struct scsi_ctrl_blk *scb; 20121da177e4SLinus Torvalds int i; 20131da177e4SLinus Torvalds 201472d39feaSAlan Cox initio_reset_scsi(host, 10); 20151da177e4SLinus Torvalds 201672d39feaSAlan Cox while ((scb = initio_pop_busy_scb(host)) != NULL) { 201772d39feaSAlan Cox scb->hastat = HOST_BAD_PHAS; 201872d39feaSAlan Cox initio_append_done_scb(host, scb); 20191da177e4SLinus Torvalds } 202072d39feaSAlan Cox for (i = 0; i < host->max_tar; i++) 202172d39feaSAlan Cox host->targets[i].flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE); 202272d39feaSAlan Cox return -1; 20231da177e4SLinus Torvalds } 20241da177e4SLinus Torvalds 20251da177e4SLinus Torvalds 202672d39feaSAlan Cox /** 202772d39feaSAlan Cox * initio_msgout_abort_targ - abort a tag 202872d39feaSAlan Cox * @host: InitIO host 202972d39feaSAlan Cox * 203072d39feaSAlan Cox * Abort when the target/lun does not match or when our SCB is not 203172d39feaSAlan Cox * busy. Used by untagged commands. 203272d39feaSAlan Cox */ 203372d39feaSAlan Cox 203472d39feaSAlan Cox static int initio_msgout_abort_targ(struct initio_host * host) 20351da177e4SLinus Torvalds { 20361da177e4SLinus Torvalds 203772d39feaSAlan Cox outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal); 203872d39feaSAlan Cox if (initio_msgin_accept(host) == -1) 203972d39feaSAlan Cox return -1; 204072d39feaSAlan Cox if (host->phase != MSG_OUT) 204172d39feaSAlan Cox return initio_bad_seq(host); 20421da177e4SLinus Torvalds 204372d39feaSAlan Cox outb(MSG_ABORT, host->addr + TUL_SFifo); 204472d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 20451da177e4SLinus Torvalds 204672d39feaSAlan Cox return initio_wait_disc(host); 20471da177e4SLinus Torvalds } 20481da177e4SLinus Torvalds 204972d39feaSAlan Cox /** 205072d39feaSAlan Cox * initio_msgout_abort_tag - abort a tag 205172d39feaSAlan Cox * @host: InitIO host 205272d39feaSAlan Cox * 205372d39feaSAlan Cox * Abort when the target/lun does not match or when our SCB is not 205472d39feaSAlan Cox * busy. Used for tagged commands. 205572d39feaSAlan Cox */ 205672d39feaSAlan Cox 205772d39feaSAlan Cox static int initio_msgout_abort_tag(struct initio_host * host) 20581da177e4SLinus Torvalds { 20591da177e4SLinus Torvalds 206072d39feaSAlan Cox outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal); 206172d39feaSAlan Cox if (initio_msgin_accept(host) == -1) 206272d39feaSAlan Cox return -1; 206372d39feaSAlan Cox if (host->phase != MSG_OUT) 206472d39feaSAlan Cox return initio_bad_seq(host); 20651da177e4SLinus Torvalds 206672d39feaSAlan Cox outb(MSG_ABORT_TAG, host->addr + TUL_SFifo); 206772d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 20681da177e4SLinus Torvalds 206972d39feaSAlan Cox return initio_wait_disc(host); 20701da177e4SLinus Torvalds 20711da177e4SLinus Torvalds } 20721da177e4SLinus Torvalds 207372d39feaSAlan Cox /** 207472d39feaSAlan Cox * initio_msgin - Message in 207572d39feaSAlan Cox * @host: InitIO Host 207672d39feaSAlan Cox * 207772d39feaSAlan Cox * Process incoming message 207872d39feaSAlan Cox */ 207972d39feaSAlan Cox static int initio_msgin(struct initio_host * host) 20801da177e4SLinus Torvalds { 208172d39feaSAlan Cox struct target_control *active_tc; 20821da177e4SLinus Torvalds 20831da177e4SLinus Torvalds for (;;) { 208472d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 20851da177e4SLinus Torvalds 208672d39feaSAlan Cox outl(1, host->addr + TUL_SCnt0); 208772d39feaSAlan Cox outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd); 208872d39feaSAlan Cox if (wait_tulip(host) == -1) 208972d39feaSAlan Cox return -1; 20901da177e4SLinus Torvalds 209172d39feaSAlan Cox switch (inb(host->addr + TUL_SFifo)) { 20921da177e4SLinus Torvalds case MSG_DISC: /* Disconnect msg */ 209372d39feaSAlan Cox outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd); 209472d39feaSAlan Cox return initio_wait_disc(host); 20951da177e4SLinus Torvalds case MSG_SDP: 20961da177e4SLinus Torvalds case MSG_RESTORE: 20971da177e4SLinus Torvalds case MSG_NOP: 209872d39feaSAlan Cox initio_msgin_accept(host); 20991da177e4SLinus Torvalds break; 21001da177e4SLinus Torvalds case MSG_REJ: /* Clear ATN first */ 210172d39feaSAlan Cox outb((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)), 210272d39feaSAlan Cox host->addr + TUL_SSignal); 210372d39feaSAlan Cox active_tc = host->active_tc; 210472d39feaSAlan Cox if ((active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) /* do sync nego */ 210572d39feaSAlan Cox outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), 210672d39feaSAlan Cox host->addr + TUL_SSignal); 210772d39feaSAlan Cox initio_msgin_accept(host); 21081da177e4SLinus Torvalds break; 21091da177e4SLinus Torvalds case MSG_EXTEND: /* extended msg */ 211072d39feaSAlan Cox initio_msgin_extend(host); 21111da177e4SLinus Torvalds break; 21121da177e4SLinus Torvalds case MSG_IGNOREWIDE: 211372d39feaSAlan Cox initio_msgin_accept(host); 21141da177e4SLinus Torvalds break; 21151da177e4SLinus Torvalds case MSG_COMP: 211672d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 211772d39feaSAlan Cox outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd); 211872d39feaSAlan Cox return initio_wait_done_disc(host); 21191da177e4SLinus Torvalds default: 212072d39feaSAlan Cox initio_msgout_reject(host); 21211da177e4SLinus Torvalds break; 21221da177e4SLinus Torvalds } 212372d39feaSAlan Cox if (host->phase != MSG_IN) 212472d39feaSAlan Cox return host->phase; 21251da177e4SLinus Torvalds } 21261da177e4SLinus Torvalds /* statement won't reach here */ 21271da177e4SLinus Torvalds } 21281da177e4SLinus Torvalds 212972d39feaSAlan Cox static int initio_msgout_reject(struct initio_host * host) 21301da177e4SLinus Torvalds { 213172d39feaSAlan Cox outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal); 21321da177e4SLinus Torvalds 213372d39feaSAlan Cox if (initio_msgin_accept(host) == -1) 213472d39feaSAlan Cox return -1; 21351da177e4SLinus Torvalds 213672d39feaSAlan Cox if (host->phase == MSG_OUT) { 213772d39feaSAlan Cox outb(MSG_REJ, host->addr + TUL_SFifo); /* Msg reject */ 213872d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 213972d39feaSAlan Cox return wait_tulip(host); 21401da177e4SLinus Torvalds } 214172d39feaSAlan Cox return host->phase; 21421da177e4SLinus Torvalds } 21431da177e4SLinus Torvalds 214472d39feaSAlan Cox static int initio_msgout_ide(struct initio_host * host) 21451da177e4SLinus Torvalds { 214672d39feaSAlan Cox outb(MSG_IDE, host->addr + TUL_SFifo); /* Initiator Detected Error */ 214772d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 214872d39feaSAlan Cox return wait_tulip(host); 21491da177e4SLinus Torvalds } 21501da177e4SLinus Torvalds 215172d39feaSAlan Cox static int initio_msgin_extend(struct initio_host * host) 21521da177e4SLinus Torvalds { 215372d39feaSAlan Cox u8 len, idx; 21541da177e4SLinus Torvalds 215572d39feaSAlan Cox if (initio_msgin_accept(host) != MSG_IN) 215672d39feaSAlan Cox return host->phase; 21571da177e4SLinus Torvalds 21581da177e4SLinus Torvalds /* Get extended msg length */ 215972d39feaSAlan Cox outl(1, host->addr + TUL_SCnt0); 216072d39feaSAlan Cox outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd); 216172d39feaSAlan Cox if (wait_tulip(host) == -1) 216272d39feaSAlan Cox return -1; 21631da177e4SLinus Torvalds 216472d39feaSAlan Cox len = inb(host->addr + TUL_SFifo); 216572d39feaSAlan Cox host->msg[0] = len; 21661da177e4SLinus Torvalds for (idx = 1; len != 0; len--) { 21671da177e4SLinus Torvalds 216872d39feaSAlan Cox if ((initio_msgin_accept(host)) != MSG_IN) 216972d39feaSAlan Cox return host->phase; 217072d39feaSAlan Cox outl(1, host->addr + TUL_SCnt0); 217172d39feaSAlan Cox outb(TSC_XF_FIFO_IN, host->addr + TUL_SCmd); 217272d39feaSAlan Cox if (wait_tulip(host) == -1) 217372d39feaSAlan Cox return -1; 217472d39feaSAlan Cox host->msg[idx++] = inb(host->addr + TUL_SFifo); 21751da177e4SLinus Torvalds } 217672d39feaSAlan Cox if (host->msg[1] == 1) { /* if it's synchronous data transfer request */ 217772d39feaSAlan Cox u8 r; 217872d39feaSAlan Cox if (host->msg[0] != 3) /* if length is not right */ 217972d39feaSAlan Cox return initio_msgout_reject(host); 218072d39feaSAlan Cox if (host->active_tc->flags & TCF_NO_SYNC_NEGO) { /* Set OFFSET=0 to do async, nego back */ 218172d39feaSAlan Cox host->msg[3] = 0; 21821da177e4SLinus Torvalds } else { 218372d39feaSAlan Cox if (initio_msgin_sync(host) == 0 && 218472d39feaSAlan Cox (host->active_tc->flags & TCF_SYNC_DONE)) { 218572d39feaSAlan Cox initio_sync_done(host); 218672d39feaSAlan Cox return initio_msgin_accept(host); 21871da177e4SLinus Torvalds } 21881da177e4SLinus Torvalds } 21891da177e4SLinus Torvalds 219072d39feaSAlan Cox r = inb(host->addr + TUL_SSignal); 219172d39feaSAlan Cox outb((r & (TSC_SET_ACK | 7)) | TSC_SET_ATN, 219272d39feaSAlan Cox host->addr + TUL_SSignal); 219372d39feaSAlan Cox if (initio_msgin_accept(host) != MSG_OUT) 219472d39feaSAlan Cox return host->phase; 21951da177e4SLinus Torvalds /* sync msg out */ 219672d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); 21971da177e4SLinus Torvalds 219872d39feaSAlan Cox initio_sync_done(host); 21991da177e4SLinus Torvalds 220072d39feaSAlan Cox outb(MSG_EXTEND, host->addr + TUL_SFifo); 220172d39feaSAlan Cox outb(3, host->addr + TUL_SFifo); 220272d39feaSAlan Cox outb(1, host->addr + TUL_SFifo); 220372d39feaSAlan Cox outb(host->msg[2], host->addr + TUL_SFifo); 220472d39feaSAlan Cox outb(host->msg[3], host->addr + TUL_SFifo); 220572d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 220672d39feaSAlan Cox return wait_tulip(host); 22071da177e4SLinus Torvalds } 220872d39feaSAlan Cox if (host->msg[0] != 2 || host->msg[1] != 3) 220972d39feaSAlan Cox return initio_msgout_reject(host); 22101da177e4SLinus Torvalds /* if it's WIDE DATA XFER REQ */ 221172d39feaSAlan Cox if (host->active_tc->flags & TCF_NO_WDTR) { 221272d39feaSAlan Cox host->msg[2] = 0; 22131da177e4SLinus Torvalds } else { 221472d39feaSAlan Cox if (host->msg[2] > 2) /* > 32 bits */ 221572d39feaSAlan Cox return initio_msgout_reject(host); 221672d39feaSAlan Cox if (host->msg[2] == 2) { /* == 32 */ 221772d39feaSAlan Cox host->msg[2] = 1; 22181da177e4SLinus Torvalds } else { 221972d39feaSAlan Cox if ((host->active_tc->flags & TCF_NO_WDTR) == 0) { 222072d39feaSAlan Cox wdtr_done(host); 222172d39feaSAlan Cox if ((host->active_tc->flags & (TCF_SYNC_DONE | TCF_NO_SYNC_NEGO)) == 0) 222272d39feaSAlan Cox outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal); 222372d39feaSAlan Cox return initio_msgin_accept(host); 22241da177e4SLinus Torvalds } 22251da177e4SLinus Torvalds } 22261da177e4SLinus Torvalds } 222772d39feaSAlan Cox outb(((inb(host->addr + TUL_SSignal) & (TSC_SET_ACK | 7)) | TSC_SET_ATN), host->addr + TUL_SSignal); 22281da177e4SLinus Torvalds 222972d39feaSAlan Cox if (initio_msgin_accept(host) != MSG_OUT) 223072d39feaSAlan Cox return host->phase; 22311da177e4SLinus Torvalds /* WDTR msg out */ 223272d39feaSAlan Cox outb(MSG_EXTEND, host->addr + TUL_SFifo); 223372d39feaSAlan Cox outb(2, host->addr + TUL_SFifo); 223472d39feaSAlan Cox outb(3, host->addr + TUL_SFifo); 223572d39feaSAlan Cox outb(host->msg[2], host->addr + TUL_SFifo); 223672d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 223772d39feaSAlan Cox return wait_tulip(host); 22381da177e4SLinus Torvalds } 22391da177e4SLinus Torvalds 224072d39feaSAlan Cox static int initio_msgin_sync(struct initio_host * host) 22411da177e4SLinus Torvalds { 22421da177e4SLinus Torvalds char default_period; 22431da177e4SLinus Torvalds 224472d39feaSAlan Cox default_period = initio_rate_tbl[host->active_tc->flags & TCF_SCSI_RATE]; 224572d39feaSAlan Cox if (host->msg[3] > MAX_OFFSET) { 224672d39feaSAlan Cox host->msg[3] = MAX_OFFSET; 224772d39feaSAlan Cox if (host->msg[2] < default_period) { 224872d39feaSAlan Cox host->msg[2] = default_period; 22491da177e4SLinus Torvalds return 1; 22501da177e4SLinus Torvalds } 225172d39feaSAlan Cox if (host->msg[2] >= 59) /* Change to async */ 225272d39feaSAlan Cox host->msg[3] = 0; 22531da177e4SLinus Torvalds return 1; 22541da177e4SLinus Torvalds } 22551da177e4SLinus Torvalds /* offset requests asynchronous transfers ? */ 225672d39feaSAlan Cox if (host->msg[3] == 0) { 22571da177e4SLinus Torvalds return 0; 22581da177e4SLinus Torvalds } 225972d39feaSAlan Cox if (host->msg[2] < default_period) { 226072d39feaSAlan Cox host->msg[2] = default_period; 22611da177e4SLinus Torvalds return 1; 22621da177e4SLinus Torvalds } 226372d39feaSAlan Cox if (host->msg[2] >= 59) { 226472d39feaSAlan Cox host->msg[3] = 0; 22651da177e4SLinus Torvalds return 1; 22661da177e4SLinus Torvalds } 22671da177e4SLinus Torvalds return 0; 22681da177e4SLinus Torvalds } 22691da177e4SLinus Torvalds 227072d39feaSAlan Cox static int wdtr_done(struct initio_host * host) 22711da177e4SLinus Torvalds { 227272d39feaSAlan Cox host->active_tc->flags &= ~TCF_SYNC_DONE; 227372d39feaSAlan Cox host->active_tc->flags |= TCF_WDTR_DONE; 22741da177e4SLinus Torvalds 227572d39feaSAlan Cox host->active_tc->js_period = 0; 227672d39feaSAlan Cox if (host->msg[2]) /* if 16 bit */ 227772d39feaSAlan Cox host->active_tc->js_period |= TSC_WIDE_SCSI; 227872d39feaSAlan Cox host->active_tc->sconfig0 &= ~TSC_ALT_PERIOD; 227972d39feaSAlan Cox outb(host->active_tc->sconfig0, host->addr + TUL_SConfig); 228072d39feaSAlan Cox outb(host->active_tc->js_period, host->addr + TUL_SPeriod); 22811da177e4SLinus Torvalds 22821da177e4SLinus Torvalds return 1; 22831da177e4SLinus Torvalds } 22841da177e4SLinus Torvalds 228572d39feaSAlan Cox static int initio_sync_done(struct initio_host * host) 22861da177e4SLinus Torvalds { 22871da177e4SLinus Torvalds int i; 22881da177e4SLinus Torvalds 228972d39feaSAlan Cox host->active_tc->flags |= TCF_SYNC_DONE; 22901da177e4SLinus Torvalds 229172d39feaSAlan Cox if (host->msg[3]) { 229272d39feaSAlan Cox host->active_tc->js_period |= host->msg[3]; 22931da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 229472d39feaSAlan Cox if (initio_rate_tbl[i] >= host->msg[2]) /* pick the big one */ 22951da177e4SLinus Torvalds break; 22961da177e4SLinus Torvalds } 229772d39feaSAlan Cox host->active_tc->js_period |= (i << 4); 229872d39feaSAlan Cox host->active_tc->sconfig0 |= TSC_ALT_PERIOD; 22991da177e4SLinus Torvalds } 230072d39feaSAlan Cox outb(host->active_tc->sconfig0, host->addr + TUL_SConfig); 230172d39feaSAlan Cox outb(host->active_tc->js_period, host->addr + TUL_SPeriod); 23021da177e4SLinus Torvalds 230372d39feaSAlan Cox return -1; 23041da177e4SLinus Torvalds } 23051da177e4SLinus Torvalds 23061da177e4SLinus Torvalds 230772d39feaSAlan Cox static int initio_post_scsi_rst(struct initio_host * host) 23081da177e4SLinus Torvalds { 230972d39feaSAlan Cox struct scsi_ctrl_blk *scb; 231072d39feaSAlan Cox struct target_control *active_tc; 23111da177e4SLinus Torvalds int i; 23121da177e4SLinus Torvalds 231372d39feaSAlan Cox host->active = NULL; 231472d39feaSAlan Cox host->active_tc = NULL; 231572d39feaSAlan Cox host->flags = 0; 23161da177e4SLinus Torvalds 231772d39feaSAlan Cox while ((scb = initio_pop_busy_scb(host)) != NULL) { 231872d39feaSAlan Cox scb->hastat = HOST_BAD_PHAS; 231972d39feaSAlan Cox initio_append_done_scb(host, scb); 23201da177e4SLinus Torvalds } 23211da177e4SLinus Torvalds /* clear sync done flag */ 232272d39feaSAlan Cox active_tc = &host->targets[0]; 232372d39feaSAlan Cox for (i = 0; i < host->max_tar; active_tc++, i++) { 232472d39feaSAlan Cox active_tc->flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE); 23251da177e4SLinus Torvalds /* Initialize the sync. xfer register values to an asyn xfer */ 232672d39feaSAlan Cox active_tc->js_period = 0; 232772d39feaSAlan Cox active_tc->sconfig0 = host->sconf1; 232872d39feaSAlan Cox host->act_tags[0] = 0; /* 07/22/98 */ 232972d39feaSAlan Cox host->targets[i].flags &= ~TCF_BUSY; /* 07/22/98 */ 23301da177e4SLinus Torvalds } /* for */ 23311da177e4SLinus Torvalds 233272d39feaSAlan Cox return -1; 23331da177e4SLinus Torvalds } 23341da177e4SLinus Torvalds 233572d39feaSAlan Cox static void initio_select_atn_stop(struct initio_host * host, struct scsi_ctrl_blk * scb) 23361da177e4SLinus Torvalds { 233772d39feaSAlan Cox scb->status |= SCB_SELECT; 233872d39feaSAlan Cox scb->next_state = 0x1; 233972d39feaSAlan Cox host->active = scb; 234072d39feaSAlan Cox host->active_tc = &host->targets[scb->target]; 234172d39feaSAlan Cox outb(TSC_SELATNSTOP, host->addr + TUL_SCmd); 23421da177e4SLinus Torvalds } 23431da177e4SLinus Torvalds 23441da177e4SLinus Torvalds 234572d39feaSAlan Cox static void initio_select_atn(struct initio_host * host, struct scsi_ctrl_blk * scb) 23461da177e4SLinus Torvalds { 23471da177e4SLinus Torvalds int i; 23481da177e4SLinus Torvalds 234972d39feaSAlan Cox scb->status |= SCB_SELECT; 235072d39feaSAlan Cox scb->next_state = 0x2; 23511da177e4SLinus Torvalds 235272d39feaSAlan Cox outb(scb->ident, host->addr + TUL_SFifo); 235372d39feaSAlan Cox for (i = 0; i < (int) scb->cdblen; i++) 235472d39feaSAlan Cox outb(scb->cdb[i], host->addr + TUL_SFifo); 235572d39feaSAlan Cox host->active_tc = &host->targets[scb->target]; 235672d39feaSAlan Cox host->active = scb; 235772d39feaSAlan Cox outb(TSC_SEL_ATN, host->addr + TUL_SCmd); 23581da177e4SLinus Torvalds } 23591da177e4SLinus Torvalds 236072d39feaSAlan Cox static void initio_select_atn3(struct initio_host * host, struct scsi_ctrl_blk * scb) 23611da177e4SLinus Torvalds { 23621da177e4SLinus Torvalds int i; 23631da177e4SLinus Torvalds 236472d39feaSAlan Cox scb->status |= SCB_SELECT; 236572d39feaSAlan Cox scb->next_state = 0x2; 23661da177e4SLinus Torvalds 236772d39feaSAlan Cox outb(scb->ident, host->addr + TUL_SFifo); 236872d39feaSAlan Cox outb(scb->tagmsg, host->addr + TUL_SFifo); 236972d39feaSAlan Cox outb(scb->tagid, host->addr + TUL_SFifo); 237072d39feaSAlan Cox for (i = 0; i < scb->cdblen; i++) 237172d39feaSAlan Cox outb(scb->cdb[i], host->addr + TUL_SFifo); 237272d39feaSAlan Cox host->active_tc = &host->targets[scb->target]; 237372d39feaSAlan Cox host->active = scb; 237472d39feaSAlan Cox outb(TSC_SEL_ATN3, host->addr + TUL_SCmd); 23751da177e4SLinus Torvalds } 23761da177e4SLinus Torvalds 237772d39feaSAlan Cox /** 237872d39feaSAlan Cox * initio_bus_device_reset - SCSI Bus Device Reset 237972d39feaSAlan Cox * @host: InitIO host to reset 238072d39feaSAlan Cox * 238172d39feaSAlan Cox * Perform a device reset and abort all pending SCBs for the 238272d39feaSAlan Cox * victim device 238372d39feaSAlan Cox */ 238472d39feaSAlan Cox int initio_bus_device_reset(struct initio_host * host) 23851da177e4SLinus Torvalds { 238672d39feaSAlan Cox struct scsi_ctrl_blk *scb = host->active; 238772d39feaSAlan Cox struct target_control *active_tc = host->active_tc; 238872d39feaSAlan Cox struct scsi_ctrl_blk *tmp, *prev; 238972d39feaSAlan Cox u8 tar; 23901da177e4SLinus Torvalds 239172d39feaSAlan Cox if (host->phase != MSG_OUT) 239272d39feaSAlan Cox return int_initio_bad_seq(host); /* Unexpected phase */ 239372d39feaSAlan Cox 239472d39feaSAlan Cox initio_unlink_pend_scb(host, scb); 239572d39feaSAlan Cox initio_release_scb(host, scb); 23961da177e4SLinus Torvalds 23971da177e4SLinus Torvalds 239872d39feaSAlan Cox tar = scb->target; /* target */ 239972d39feaSAlan Cox active_tc->flags &= ~(TCF_SYNC_DONE | TCF_WDTR_DONE | TCF_BUSY); 24001da177e4SLinus Torvalds /* clr sync. nego & WDTR flags 07/22/98 */ 24011da177e4SLinus Torvalds 24021da177e4SLinus Torvalds /* abort all SCB with same target */ 240372d39feaSAlan Cox prev = tmp = host->first_busy; /* Check Busy queue */ 240472d39feaSAlan Cox while (tmp != NULL) { 240572d39feaSAlan Cox if (tmp->target == tar) { 24061da177e4SLinus Torvalds /* unlink it */ 240772d39feaSAlan Cox if (tmp == host->first_busy) { 240872d39feaSAlan Cox if ((host->first_busy = tmp->next) == NULL) 240972d39feaSAlan Cox host->last_busy = NULL; 24101da177e4SLinus Torvalds } else { 241172d39feaSAlan Cox prev->next = tmp->next; 241272d39feaSAlan Cox if (tmp == host->last_busy) 241372d39feaSAlan Cox host->last_busy = prev; 24141da177e4SLinus Torvalds } 241572d39feaSAlan Cox tmp->hastat = HOST_ABORTED; 241672d39feaSAlan Cox initio_append_done_scb(host, tmp); 24171da177e4SLinus Torvalds } 24181da177e4SLinus Torvalds /* Previous haven't change */ 24191da177e4SLinus Torvalds else { 242072d39feaSAlan Cox prev = tmp; 24211da177e4SLinus Torvalds } 242272d39feaSAlan Cox tmp = tmp->next; 24231da177e4SLinus Torvalds } 242472d39feaSAlan Cox outb(MSG_DEVRST, host->addr + TUL_SFifo); 242572d39feaSAlan Cox outb(TSC_XF_FIFO_OUT, host->addr + TUL_SCmd); 242672d39feaSAlan Cox return initio_wait_disc(host); 24271da177e4SLinus Torvalds 24281da177e4SLinus Torvalds } 24291da177e4SLinus Torvalds 243072d39feaSAlan Cox static int initio_msgin_accept(struct initio_host * host) 24311da177e4SLinus Torvalds { 243272d39feaSAlan Cox outb(TSC_MSG_ACCEPT, host->addr + TUL_SCmd); 243372d39feaSAlan Cox return wait_tulip(host); 24341da177e4SLinus Torvalds } 24351da177e4SLinus Torvalds 243672d39feaSAlan Cox static int wait_tulip(struct initio_host * host) 24371da177e4SLinus Torvalds { 24381da177e4SLinus Torvalds 243972d39feaSAlan Cox while (!((host->jsstatus0 = inb(host->addr + TUL_SStatus0)) 244072d39feaSAlan Cox & TSS_INT_PENDING)) 244172d39feaSAlan Cox cpu_relax(); 24421da177e4SLinus Torvalds 244372d39feaSAlan Cox host->jsint = inb(host->addr + TUL_SInt); 244472d39feaSAlan Cox host->phase = host->jsstatus0 & TSS_PH_MASK; 244572d39feaSAlan Cox host->jsstatus1 = inb(host->addr + TUL_SStatus1); 24461da177e4SLinus Torvalds 244772d39feaSAlan Cox if (host->jsint & TSS_RESEL_INT) /* if SCSI bus reset detected */ 244872d39feaSAlan Cox return int_initio_resel(host); 244972d39feaSAlan Cox if (host->jsint & TSS_SEL_TIMEOUT) /* if selected/reselected timeout interrupt */ 245072d39feaSAlan Cox return int_initio_busfree(host); 245172d39feaSAlan Cox if (host->jsint & TSS_SCSIRST_INT) /* if SCSI bus reset detected */ 245272d39feaSAlan Cox return int_initio_scsi_rst(host); 245372d39feaSAlan Cox 245472d39feaSAlan Cox if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */ 245572d39feaSAlan Cox if (host->flags & HCF_EXPECT_DONE_DISC) { 245672d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */ 245772d39feaSAlan Cox initio_unlink_busy_scb(host, host->active); 245872d39feaSAlan Cox host->active->hastat = 0; 245972d39feaSAlan Cox initio_append_done_scb(host, host->active); 246072d39feaSAlan Cox host->active = NULL; 246172d39feaSAlan Cox host->active_tc = NULL; 246272d39feaSAlan Cox host->flags &= ~HCF_EXPECT_DONE_DISC; 246372d39feaSAlan Cox outb(TSC_INITDEFAULT, host->addr + TUL_SConfig); 246472d39feaSAlan Cox outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */ 246572d39feaSAlan Cox return -1; 24661da177e4SLinus Torvalds } 246772d39feaSAlan Cox if (host->flags & HCF_EXPECT_DISC) { 246872d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */ 246972d39feaSAlan Cox host->active = NULL; 247072d39feaSAlan Cox host->active_tc = NULL; 247172d39feaSAlan Cox host->flags &= ~HCF_EXPECT_DISC; 247272d39feaSAlan Cox outb(TSC_INITDEFAULT, host->addr + TUL_SConfig); 247372d39feaSAlan Cox outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */ 247472d39feaSAlan Cox return -1; 24751da177e4SLinus Torvalds } 247672d39feaSAlan Cox return int_initio_busfree(host); 24771da177e4SLinus Torvalds } 247872d39feaSAlan Cox /* The old code really does the below. Can probably be removed */ 247972d39feaSAlan Cox if (host->jsint & (TSS_FUNC_COMP | TSS_BUS_SERV)) 248072d39feaSAlan Cox return host->phase; 248172d39feaSAlan Cox return host->phase; 24821da177e4SLinus Torvalds } 248372d39feaSAlan Cox 248472d39feaSAlan Cox static int initio_wait_disc(struct initio_host * host) 24851da177e4SLinus Torvalds { 248672d39feaSAlan Cox while (!((host->jsstatus0 = inb(host->addr + TUL_SStatus0)) & TSS_INT_PENDING)) 248772d39feaSAlan Cox cpu_relax(); 24881da177e4SLinus Torvalds 248972d39feaSAlan Cox host->jsint = inb(host->addr + TUL_SInt); 24901da177e4SLinus Torvalds 249172d39feaSAlan Cox if (host->jsint & TSS_SCSIRST_INT) /* if SCSI bus reset detected */ 249272d39feaSAlan Cox return int_initio_scsi_rst(host); 249372d39feaSAlan Cox if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */ 249472d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */ 249572d39feaSAlan Cox outb(TSC_INITDEFAULT, host->addr + TUL_SConfig); 249672d39feaSAlan Cox outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */ 249772d39feaSAlan Cox host->active = NULL; 249872d39feaSAlan Cox return -1; 24991da177e4SLinus Torvalds } 250072d39feaSAlan Cox return initio_bad_seq(host); 25011da177e4SLinus Torvalds } 25021da177e4SLinus Torvalds 250372d39feaSAlan Cox static int initio_wait_done_disc(struct initio_host * host) 25041da177e4SLinus Torvalds { 250572d39feaSAlan Cox while (!((host->jsstatus0 = inb(host->addr + TUL_SStatus0)) 250672d39feaSAlan Cox & TSS_INT_PENDING)) 250772d39feaSAlan Cox cpu_relax(); 25081da177e4SLinus Torvalds 250972d39feaSAlan Cox host->jsint = inb(host->addr + TUL_SInt); 25101da177e4SLinus Torvalds 251172d39feaSAlan Cox if (host->jsint & TSS_SCSIRST_INT) /* if SCSI bus reset detected */ 251272d39feaSAlan Cox return int_initio_scsi_rst(host); 251372d39feaSAlan Cox if (host->jsint & TSS_DISC_INT) { /* BUS disconnection */ 251472d39feaSAlan Cox outb(TSC_FLUSH_FIFO, host->addr + TUL_SCtrl0); /* Flush SCSI FIFO */ 251572d39feaSAlan Cox outb(TSC_INITDEFAULT, host->addr + TUL_SConfig); 251672d39feaSAlan Cox outb(TSC_HW_RESELECT, host->addr + TUL_SCtrl1); /* Enable HW reselect */ 251772d39feaSAlan Cox initio_unlink_busy_scb(host, host->active); 25181da177e4SLinus Torvalds 251972d39feaSAlan Cox initio_append_done_scb(host, host->active); 252072d39feaSAlan Cox host->active = NULL; 252172d39feaSAlan Cox return -1; 25221da177e4SLinus Torvalds } 252372d39feaSAlan Cox return initio_bad_seq(host); 252472d39feaSAlan Cox } 25251da177e4SLinus Torvalds 252672d39feaSAlan Cox /** 252772d39feaSAlan Cox * i91u_intr - IRQ handler 252872d39feaSAlan Cox * @irqno: IRQ number 252972d39feaSAlan Cox * @dev_id: IRQ identifier 253072d39feaSAlan Cox * 253172d39feaSAlan Cox * Take the relevant locks and then invoke the actual isr processing 253272d39feaSAlan Cox * code under the lock. 253372d39feaSAlan Cox */ 25341da177e4SLinus Torvalds 25357d12e780SDavid Howells static irqreturn_t i91u_intr(int irqno, void *dev_id) 25361da177e4SLinus Torvalds { 25371da177e4SLinus Torvalds struct Scsi_Host *dev = dev_id; 25381da177e4SLinus Torvalds unsigned long flags; 253972d39feaSAlan Cox int r; 25401da177e4SLinus Torvalds 25411da177e4SLinus Torvalds spin_lock_irqsave(dev->host_lock, flags); 254272d39feaSAlan Cox r = initio_isr((struct initio_host *)dev->hostdata); 25431da177e4SLinus Torvalds spin_unlock_irqrestore(dev->host_lock, flags); 254472d39feaSAlan Cox if (r) 25451da177e4SLinus Torvalds return IRQ_HANDLED; 254672d39feaSAlan Cox else 254772d39feaSAlan Cox return IRQ_NONE; 25481da177e4SLinus Torvalds } 25491da177e4SLinus Torvalds 25501da177e4SLinus Torvalds 255172d39feaSAlan Cox /** 255272d39feaSAlan Cox * initio_build_scb - Build the mappings and SCB 255372d39feaSAlan Cox * @host: InitIO host taking the command 255472d39feaSAlan Cox * @cblk: Firmware command block 255572d39feaSAlan Cox * @cmnd: SCSI midlayer command block 255672d39feaSAlan Cox * 255772d39feaSAlan Cox * Translate the abstract SCSI command into a firmware command block 255872d39feaSAlan Cox * suitable for feeding to the InitIO host controller. This also requires 255972d39feaSAlan Cox * we build the scatter gather lists and ensure they are mapped properly. 256072d39feaSAlan Cox */ 25611da177e4SLinus Torvalds 256272d39feaSAlan Cox static void initio_build_scb(struct initio_host * host, struct scsi_ctrl_blk * cblk, struct scsi_cmnd * cmnd) 25631da177e4SLinus Torvalds { /* Create corresponding SCB */ 256472d39feaSAlan Cox struct scatterlist *sglist; 256572d39feaSAlan Cox struct sg_entry *sg; /* Pointer to SG list */ 2566a258c85dSFUJITA Tomonori int i, nseg; 256772d39feaSAlan Cox long total_len; 25681da177e4SLinus Torvalds dma_addr_t dma_addr; 25691da177e4SLinus Torvalds 257072d39feaSAlan Cox /* Fill in the command headers */ 257172d39feaSAlan Cox cblk->post = i91uSCBPost; /* i91u's callback routine */ 257272d39feaSAlan Cox cblk->srb = cmnd; 257372d39feaSAlan Cox cblk->opcode = ExecSCSI; 257472d39feaSAlan Cox cblk->flags = SCF_POST; /* After SCSI done, call post routine */ 257572d39feaSAlan Cox cblk->target = cmnd->device->id; 257672d39feaSAlan Cox cblk->lun = cmnd->device->lun; 257772d39feaSAlan Cox cblk->ident = cmnd->device->lun | DISC_ALLOW; 25781da177e4SLinus Torvalds 257972d39feaSAlan Cox cblk->flags |= SCF_SENSE; /* Turn on auto request sense */ 258072d39feaSAlan Cox 258172d39feaSAlan Cox /* Map the sense buffer into bus memory */ 258272d39feaSAlan Cox dma_addr = dma_map_single(&host->pci_dev->dev, cmnd->sense_buffer, 25831da177e4SLinus Torvalds SENSE_SIZE, DMA_FROM_DEVICE); 2584423eef6fSGrant Grundler cblk->senseptr = (u32)dma_addr; 2585423eef6fSGrant Grundler cblk->senselen = SENSE_SIZE; 258672d39feaSAlan Cox cmnd->SCp.ptr = (char *)(unsigned long)dma_addr; 258772d39feaSAlan Cox cblk->cdblen = cmnd->cmd_len; 25881da177e4SLinus Torvalds 258972d39feaSAlan Cox /* Clear the returned status */ 259072d39feaSAlan Cox cblk->hastat = 0; 259172d39feaSAlan Cox cblk->tastat = 0; 259272d39feaSAlan Cox /* Command the command */ 259364a87b24SBoaz Harrosh memcpy(cblk->cdb, cmnd->cmnd, cmnd->cmd_len); 25941da177e4SLinus Torvalds 259572d39feaSAlan Cox /* Set up tags */ 259672d39feaSAlan Cox if (cmnd->device->tagged_supported) { /* Tag Support */ 259772d39feaSAlan Cox cblk->tagmsg = SIMPLE_QUEUE_TAG; /* Do simple tag only */ 25981da177e4SLinus Torvalds } else { 259972d39feaSAlan Cox cblk->tagmsg = 0; /* No tag support */ 26001da177e4SLinus Torvalds } 260172d39feaSAlan Cox 26021da177e4SLinus Torvalds /* todo handle map_sg error */ 2603a258c85dSFUJITA Tomonori nseg = scsi_dma_map(cmnd); 2604a258c85dSFUJITA Tomonori BUG_ON(nseg < 0); 2605a258c85dSFUJITA Tomonori if (nseg) { 260672d39feaSAlan Cox dma_addr = dma_map_single(&host->pci_dev->dev, &cblk->sglist[0], 260772d39feaSAlan Cox sizeof(struct sg_entry) * TOTAL_SG_ENTRY, 26081da177e4SLinus Torvalds DMA_BIDIRECTIONAL); 2609423eef6fSGrant Grundler cblk->bufptr = (u32)dma_addr; 261072d39feaSAlan Cox cmnd->SCp.dma_handle = dma_addr; 26111da177e4SLinus Torvalds 2612e2d435eaSStuart Swales cblk->sglen = nseg; 26131da177e4SLinus Torvalds 261472d39feaSAlan Cox cblk->flags |= SCF_SG; /* Turn on SG list flag */ 2615a258c85dSFUJITA Tomonori total_len = 0; 2616a258c85dSFUJITA Tomonori sg = &cblk->sglist[0]; 2617a258c85dSFUJITA Tomonori scsi_for_each_sg(cmnd, sglist, cblk->sglen, i) { 261872d39feaSAlan Cox sg->data = cpu_to_le32((u32)sg_dma_address(sglist)); 2619423eef6fSGrant Grundler sg->len = cpu_to_le32((u32)sg_dma_len(sglist)); 2620423eef6fSGrant Grundler total_len += sg_dma_len(sglist); 2621a169e637SBoaz Harrosh ++sg; 26221da177e4SLinus Torvalds } 26231da177e4SLinus Torvalds 2624a258c85dSFUJITA Tomonori cblk->buflen = (scsi_bufflen(cmnd) > total_len) ? 2625a258c85dSFUJITA Tomonori total_len : scsi_bufflen(cmnd); 262672d39feaSAlan Cox } else { /* No data transfer required */ 262772d39feaSAlan Cox cblk->buflen = 0; 262872d39feaSAlan Cox cblk->sglen = 0; 26291da177e4SLinus Torvalds } 26301da177e4SLinus Torvalds } 26311da177e4SLinus Torvalds 263272d39feaSAlan Cox /** 263372d39feaSAlan Cox * i91u_queuecommand - Queue a new command if possible 263472d39feaSAlan Cox * @cmd: SCSI command block from the mid layer 263572d39feaSAlan Cox * @done: Completion handler 263672d39feaSAlan Cox * 263772d39feaSAlan Cox * Attempts to queue a new command with the host adapter. Will return 263872d39feaSAlan Cox * zero if successful or indicate a host busy condition if not (which 263972d39feaSAlan Cox * will cause the mid layer to call us again later with the command) 264072d39feaSAlan Cox */ 264172d39feaSAlan Cox 2642f281233dSJeff Garzik static int i91u_queuecommand_lck(struct scsi_cmnd *cmd, 26431da177e4SLinus Torvalds void (*done)(struct scsi_cmnd *)) 26441da177e4SLinus Torvalds { 264572d39feaSAlan Cox struct initio_host *host = (struct initio_host *) cmd->device->host->hostdata; 264672d39feaSAlan Cox struct scsi_ctrl_blk *cmnd; 26471da177e4SLinus Torvalds 26481da177e4SLinus Torvalds cmd->scsi_done = done; 26491da177e4SLinus Torvalds 265072d39feaSAlan Cox cmnd = initio_alloc_scb(host); 265172d39feaSAlan Cox if (!cmnd) 26521da177e4SLinus Torvalds return SCSI_MLQUEUE_HOST_BUSY; 26531da177e4SLinus Torvalds 265472d39feaSAlan Cox initio_build_scb(host, cmnd, cmd); 265572d39feaSAlan Cox initio_exec_scb(host, cmnd); 26561da177e4SLinus Torvalds return 0; 26571da177e4SLinus Torvalds } 26581da177e4SLinus Torvalds 2659f281233dSJeff Garzik static DEF_SCSI_QCMD(i91u_queuecommand) 2660f281233dSJeff Garzik 266172d39feaSAlan Cox /** 266272d39feaSAlan Cox * i91u_bus_reset - reset the SCSI bus 266372d39feaSAlan Cox * @cmnd: Command block we want to trigger the reset for 266472d39feaSAlan Cox * 266572d39feaSAlan Cox * Initiate a SCSI bus reset sequence 26661da177e4SLinus Torvalds */ 266772d39feaSAlan Cox 266872d39feaSAlan Cox static int i91u_bus_reset(struct scsi_cmnd * cmnd) 26691da177e4SLinus Torvalds { 267072d39feaSAlan Cox struct initio_host *host; 26711da177e4SLinus Torvalds 267272d39feaSAlan Cox host = (struct initio_host *) cmnd->device->host->hostdata; 26731da177e4SLinus Torvalds 267472d39feaSAlan Cox spin_lock_irq(cmnd->device->host->host_lock); 267572d39feaSAlan Cox initio_reset_scsi(host, 0); 267672d39feaSAlan Cox spin_unlock_irq(cmnd->device->host->host_lock); 267768b3aa7cSJeff Garzik 26781da177e4SLinus Torvalds return SUCCESS; 26791da177e4SLinus Torvalds } 26801da177e4SLinus Torvalds 268172d39feaSAlan Cox /** 268272d39feaSAlan Cox * i91u_biospararm - return the "logical geometry 268372d39feaSAlan Cox * @sdev: SCSI device 268472d39feaSAlan Cox * @dev; Matching block device 268572d39feaSAlan Cox * @capacity: Sector size of drive 268672d39feaSAlan Cox * @info_array: Return space for BIOS geometry 268772d39feaSAlan Cox * 268872d39feaSAlan Cox * Map the device geometry in a manner compatible with the host 268972d39feaSAlan Cox * controller BIOS behaviour. 269072d39feaSAlan Cox * 269172d39feaSAlan Cox * FIXME: limited to 2^32 sector devices. 26921da177e4SLinus Torvalds */ 269372d39feaSAlan Cox 26941da177e4SLinus Torvalds static int i91u_biosparam(struct scsi_device *sdev, struct block_device *dev, 26951da177e4SLinus Torvalds sector_t capacity, int *info_array) 26961da177e4SLinus Torvalds { 269772d39feaSAlan Cox struct initio_host *host; /* Point to Host adapter control block */ 269872d39feaSAlan Cox struct target_control *tc; 26991da177e4SLinus Torvalds 270072d39feaSAlan Cox host = (struct initio_host *) sdev->host->hostdata; 270172d39feaSAlan Cox tc = &host->targets[sdev->id]; 27021da177e4SLinus Torvalds 270372d39feaSAlan Cox if (tc->heads) { 270472d39feaSAlan Cox info_array[0] = tc->heads; 270572d39feaSAlan Cox info_array[1] = tc->sectors; 270672d39feaSAlan Cox info_array[2] = (unsigned long)capacity / tc->heads / tc->sectors; 27071da177e4SLinus Torvalds } else { 270872d39feaSAlan Cox if (tc->drv_flags & TCF_DRV_255_63) { 27091da177e4SLinus Torvalds info_array[0] = 255; 27101da177e4SLinus Torvalds info_array[1] = 63; 27111da177e4SLinus Torvalds info_array[2] = (unsigned long)capacity / 255 / 63; 27121da177e4SLinus Torvalds } else { 27131da177e4SLinus Torvalds info_array[0] = 64; 27141da177e4SLinus Torvalds info_array[1] = 32; 27151da177e4SLinus Torvalds info_array[2] = (unsigned long)capacity >> 11; 27161da177e4SLinus Torvalds } 27171da177e4SLinus Torvalds } 27181da177e4SLinus Torvalds 27191da177e4SLinus Torvalds #if defined(DEBUG_BIOSPARAM) 27201da177e4SLinus Torvalds if (i91u_debug & debug_biosparam) { 27211da177e4SLinus Torvalds printk("bios geometry: head=%d, sec=%d, cyl=%d\n", 27221da177e4SLinus Torvalds info_array[0], info_array[1], info_array[2]); 27231da177e4SLinus Torvalds printk("WARNING: check, if the bios geometry is correct.\n"); 27241da177e4SLinus Torvalds } 27251da177e4SLinus Torvalds #endif 27261da177e4SLinus Torvalds 27271da177e4SLinus Torvalds return 0; 27281da177e4SLinus Torvalds } 27291da177e4SLinus Torvalds 273072d39feaSAlan Cox /** 273172d39feaSAlan Cox * i91u_unmap_scb - Unmap a command 273272d39feaSAlan Cox * @pci_dev: PCI device the command is for 273372d39feaSAlan Cox * @cmnd: The command itself 273472d39feaSAlan Cox * 273572d39feaSAlan Cox * Unmap any PCI mapping/IOMMU resources allocated when the command 273672d39feaSAlan Cox * was mapped originally as part of initio_build_scb 273772d39feaSAlan Cox */ 273872d39feaSAlan Cox 273972d39feaSAlan Cox static void i91u_unmap_scb(struct pci_dev *pci_dev, struct scsi_cmnd *cmnd) 27401da177e4SLinus Torvalds { 27411da177e4SLinus Torvalds /* auto sense buffer */ 27421da177e4SLinus Torvalds if (cmnd->SCp.ptr) { 27431da177e4SLinus Torvalds dma_unmap_single(&pci_dev->dev, 27441da177e4SLinus Torvalds (dma_addr_t)((unsigned long)cmnd->SCp.ptr), 27451da177e4SLinus Torvalds SENSE_SIZE, DMA_FROM_DEVICE); 27461da177e4SLinus Torvalds cmnd->SCp.ptr = NULL; 27471da177e4SLinus Torvalds } 27481da177e4SLinus Torvalds 27491da177e4SLinus Torvalds /* request buffer */ 2750a258c85dSFUJITA Tomonori if (scsi_sg_count(cmnd)) { 27511da177e4SLinus Torvalds dma_unmap_single(&pci_dev->dev, cmnd->SCp.dma_handle, 275272d39feaSAlan Cox sizeof(struct sg_entry) * TOTAL_SG_ENTRY, 27531da177e4SLinus Torvalds DMA_BIDIRECTIONAL); 27541da177e4SLinus Torvalds 2755a258c85dSFUJITA Tomonori scsi_dma_unmap(cmnd); 27561da177e4SLinus Torvalds } 27571da177e4SLinus Torvalds } 27581da177e4SLinus Torvalds 275972d39feaSAlan Cox /** 276072d39feaSAlan Cox * i91uSCBPost - SCSI callback 276172d39feaSAlan Cox * @host: Pointer to host adapter control block. 276272d39feaSAlan Cox * @cmnd: Pointer to SCSI control block. 276372d39feaSAlan Cox * 276472d39feaSAlan Cox * This is callback routine be called when tulip finish one 276572d39feaSAlan Cox * SCSI command. 276672d39feaSAlan Cox */ 276772d39feaSAlan Cox 276872d39feaSAlan Cox static void i91uSCBPost(u8 * host_mem, u8 * cblk_mem) 27691da177e4SLinus Torvalds { 277072d39feaSAlan Cox struct scsi_cmnd *cmnd; /* Pointer to SCSI request block */ 277172d39feaSAlan Cox struct initio_host *host; 277272d39feaSAlan Cox struct scsi_ctrl_blk *cblk; 27731da177e4SLinus Torvalds 277472d39feaSAlan Cox host = (struct initio_host *) host_mem; 277572d39feaSAlan Cox cblk = (struct scsi_ctrl_blk *) cblk_mem; 277672d39feaSAlan Cox if ((cmnd = cblk->srb) == NULL) { 277772d39feaSAlan Cox printk(KERN_ERR "i91uSCBPost: SRB pointer is empty\n"); 277872d39feaSAlan Cox WARN_ON(1); 277972d39feaSAlan Cox initio_release_scb(host, cblk); /* Release SCB for current channel */ 27801da177e4SLinus Torvalds return; 27811da177e4SLinus Torvalds } 278272d39feaSAlan Cox 278372d39feaSAlan Cox /* 278472d39feaSAlan Cox * Remap the firmware error status into a mid layer one 278572d39feaSAlan Cox */ 278672d39feaSAlan Cox switch (cblk->hastat) { 27871da177e4SLinus Torvalds case 0x0: 27881da177e4SLinus Torvalds case 0xa: /* Linked command complete without error and linked normally */ 27891da177e4SLinus Torvalds case 0xb: /* Linked command complete without error interrupt generated */ 279072d39feaSAlan Cox cblk->hastat = 0; 27911da177e4SLinus Torvalds break; 27921da177e4SLinus Torvalds 27931da177e4SLinus Torvalds case 0x11: /* Selection time out-The initiator selection or target 27941da177e4SLinus Torvalds reselection was not complete within the SCSI Time out period */ 279572d39feaSAlan Cox cblk->hastat = DID_TIME_OUT; 27961da177e4SLinus Torvalds break; 27971da177e4SLinus Torvalds 27981da177e4SLinus Torvalds case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus 27991da177e4SLinus Torvalds phase sequence was requested by the target. The host adapter 28001da177e4SLinus Torvalds will generate a SCSI Reset Condition, notifying the host with 28011da177e4SLinus Torvalds a SCRD interrupt */ 280272d39feaSAlan Cox cblk->hastat = DID_RESET; 28031da177e4SLinus Torvalds break; 28041da177e4SLinus Torvalds 28051da177e4SLinus Torvalds case 0x1a: /* SCB Aborted. 07/21/98 */ 280672d39feaSAlan Cox cblk->hastat = DID_ABORT; 28071da177e4SLinus Torvalds break; 28081da177e4SLinus Torvalds 28091da177e4SLinus Torvalds case 0x12: /* Data overrun/underrun-The target attempted to transfer more data 28101da177e4SLinus Torvalds than was allocated by the Data Length field or the sum of the 28111da177e4SLinus Torvalds Scatter / Gather Data Length fields. */ 28121da177e4SLinus Torvalds case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */ 28131da177e4SLinus Torvalds case 0x16: /* Invalid SCB Operation Code. */ 28141da177e4SLinus Torvalds 28151da177e4SLinus Torvalds default: 281672d39feaSAlan Cox printk("ini9100u: %x %x\n", cblk->hastat, cblk->tastat); 281772d39feaSAlan Cox cblk->hastat = DID_ERROR; /* Couldn't find any better */ 28181da177e4SLinus Torvalds break; 28191da177e4SLinus Torvalds } 28201da177e4SLinus Torvalds 282172d39feaSAlan Cox cmnd->result = cblk->tastat | (cblk->hastat << 16); 282272d39feaSAlan Cox i91u_unmap_scb(host->pci_dev, cmnd); 282372d39feaSAlan Cox cmnd->scsi_done(cmnd); /* Notify system DONE */ 282472d39feaSAlan Cox initio_release_scb(host, cblk); /* Release SCB for current channel */ 28251da177e4SLinus Torvalds } 28261da177e4SLinus Torvalds 282772d39feaSAlan Cox static struct scsi_host_template initio_template = { 28281da177e4SLinus Torvalds .proc_name = "INI9100U", 282972d39feaSAlan Cox .name = "Initio INI-9X00U/UW SCSI device driver", 28301da177e4SLinus Torvalds .queuecommand = i91u_queuecommand, 28311da177e4SLinus Torvalds .eh_bus_reset_handler = i91u_bus_reset, 28321da177e4SLinus Torvalds .bios_param = i91u_biosparam, 283372d39feaSAlan Cox .can_queue = MAX_TARGETS * i91u_MAXQUEUE, 28341da177e4SLinus Torvalds .this_id = 1, 28351da177e4SLinus Torvalds .sg_tablesize = SG_ALL, 28361da177e4SLinus Torvalds .cmd_per_lun = 1, 28371da177e4SLinus Torvalds .use_clustering = ENABLE_CLUSTERING, 28381da177e4SLinus Torvalds }; 28391da177e4SLinus Torvalds 284072d39feaSAlan Cox static int initio_probe_one(struct pci_dev *pdev, 284172d39feaSAlan Cox const struct pci_device_id *id) 284272d39feaSAlan Cox { 284372d39feaSAlan Cox struct Scsi_Host *shost; 284472d39feaSAlan Cox struct initio_host *host; 284572d39feaSAlan Cox u32 reg; 284672d39feaSAlan Cox u16 bios_seg; 284772d39feaSAlan Cox struct scsi_ctrl_blk *scb, *tmp, *prev = NULL /* silence gcc */; 284872d39feaSAlan Cox int num_scb, i, error; 284972d39feaSAlan Cox 285072d39feaSAlan Cox error = pci_enable_device(pdev); 285172d39feaSAlan Cox if (error) 285272d39feaSAlan Cox return error; 285372d39feaSAlan Cox 285472d39feaSAlan Cox pci_read_config_dword(pdev, 0x44, (u32 *) & reg); 285572d39feaSAlan Cox bios_seg = (u16) (reg & 0xFF); 285672d39feaSAlan Cox if (((reg & 0xFF00) >> 8) == 0xFF) 285772d39feaSAlan Cox reg = 0; 285872d39feaSAlan Cox bios_seg = (bios_seg << 8) + ((u16) ((reg & 0xFF00) >> 8)); 285972d39feaSAlan Cox 2860284901a9SYang Hongyang if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { 286172d39feaSAlan Cox printk(KERN_WARNING "i91u: Could not set 32 bit DMA mask\n"); 286272d39feaSAlan Cox error = -ENODEV; 286372d39feaSAlan Cox goto out_disable_device; 286472d39feaSAlan Cox } 286572d39feaSAlan Cox shost = scsi_host_alloc(&initio_template, sizeof(struct initio_host)); 286672d39feaSAlan Cox if (!shost) { 286772d39feaSAlan Cox printk(KERN_WARNING "initio: Could not allocate host structure.\n"); 286872d39feaSAlan Cox error = -ENOMEM; 286972d39feaSAlan Cox goto out_disable_device; 287072d39feaSAlan Cox } 287172d39feaSAlan Cox host = (struct initio_host *)shost->hostdata; 287272d39feaSAlan Cox memset(host, 0, sizeof(struct initio_host)); 287399f1f534SAlan Cox host->addr = pci_resource_start(pdev, 0); 2874e2d435eaSStuart Swales host->bios_addr = bios_seg; 287572d39feaSAlan Cox 287672d39feaSAlan Cox if (!request_region(host->addr, 256, "i91u")) { 287772d39feaSAlan Cox printk(KERN_WARNING "initio: I/O port range 0x%x is busy.\n", host->addr); 287872d39feaSAlan Cox error = -ENODEV; 287972d39feaSAlan Cox goto out_host_put; 288072d39feaSAlan Cox } 288172d39feaSAlan Cox 288272d39feaSAlan Cox if (initio_tag_enable) /* 1.01i */ 288372d39feaSAlan Cox num_scb = MAX_TARGETS * i91u_MAXQUEUE; 288472d39feaSAlan Cox else 288572d39feaSAlan Cox num_scb = MAX_TARGETS + 3; /* 1-tape, 1-CD_ROM, 1- extra */ 288672d39feaSAlan Cox 288772d39feaSAlan Cox for (; num_scb >= MAX_TARGETS + 3; num_scb--) { 288872d39feaSAlan Cox i = num_scb * sizeof(struct scsi_ctrl_blk); 288972d39feaSAlan Cox if ((scb = kzalloc(i, GFP_DMA)) != NULL) 289072d39feaSAlan Cox break; 289172d39feaSAlan Cox } 289272d39feaSAlan Cox 289372d39feaSAlan Cox if (!scb) { 289472d39feaSAlan Cox printk(KERN_WARNING "initio: Cannot allocate SCB array.\n"); 289572d39feaSAlan Cox error = -ENOMEM; 289672d39feaSAlan Cox goto out_release_region; 289772d39feaSAlan Cox } 289872d39feaSAlan Cox 2899e9e42fafSAlan Cox host->pci_dev = pdev; 2900e9e42fafSAlan Cox 2901e2d435eaSStuart Swales host->semaph = 1; 2902e2d435eaSStuart Swales spin_lock_init(&host->semaph_lock); 290372d39feaSAlan Cox host->num_scbs = num_scb; 290472d39feaSAlan Cox host->scb = scb; 290572d39feaSAlan Cox host->next_pending = scb; 290672d39feaSAlan Cox host->next_avail = scb; 290772d39feaSAlan Cox for (i = 0, tmp = scb; i < num_scb; i++, tmp++) { 290872d39feaSAlan Cox tmp->tagid = i; 290972d39feaSAlan Cox if (i != 0) 291072d39feaSAlan Cox prev->next = tmp; 291172d39feaSAlan Cox prev = tmp; 291272d39feaSAlan Cox } 291372d39feaSAlan Cox prev->next = NULL; 291472d39feaSAlan Cox host->scb_end = tmp; 291572d39feaSAlan Cox host->first_avail = scb; 291672d39feaSAlan Cox host->last_avail = prev; 2917e9e42fafSAlan Cox spin_lock_init(&host->avail_lock); 291872d39feaSAlan Cox 2919e2d435eaSStuart Swales initio_init(host, phys_to_virt(((u32)bios_seg << 4))); 292072d39feaSAlan Cox 292172d39feaSAlan Cox host->jsstatus0 = 0; 292272d39feaSAlan Cox 292372d39feaSAlan Cox shost->io_port = host->addr; 292472d39feaSAlan Cox shost->n_io_port = 0xff; 292572d39feaSAlan Cox shost->can_queue = num_scb; /* 03/05/98 */ 292672d39feaSAlan Cox shost->unique_id = host->addr; 292772d39feaSAlan Cox shost->max_id = host->max_tar; 292872d39feaSAlan Cox shost->max_lun = 32; /* 10/21/97 */ 292972d39feaSAlan Cox shost->irq = pdev->irq; 293072d39feaSAlan Cox shost->this_id = host->scsi_id; /* Assign HCS index */ 293172d39feaSAlan Cox shost->base = host->addr; 293272d39feaSAlan Cox shost->sg_tablesize = TOTAL_SG_ENTRY; 293372d39feaSAlan Cox 293472d39feaSAlan Cox error = request_irq(pdev->irq, i91u_intr, IRQF_DISABLED|IRQF_SHARED, "i91u", shost); 293572d39feaSAlan Cox if (error < 0) { 293672d39feaSAlan Cox printk(KERN_WARNING "initio: Unable to request IRQ %d\n", pdev->irq); 293772d39feaSAlan Cox goto out_free_scbs; 293872d39feaSAlan Cox } 293972d39feaSAlan Cox 294072d39feaSAlan Cox pci_set_drvdata(pdev, shost); 294172d39feaSAlan Cox 294272d39feaSAlan Cox error = scsi_add_host(shost, &pdev->dev); 294372d39feaSAlan Cox if (error) 294472d39feaSAlan Cox goto out_free_irq; 294572d39feaSAlan Cox scsi_scan_host(shost); 294672d39feaSAlan Cox return 0; 294772d39feaSAlan Cox out_free_irq: 294872d39feaSAlan Cox free_irq(pdev->irq, shost); 294972d39feaSAlan Cox out_free_scbs: 295072d39feaSAlan Cox kfree(host->scb); 295172d39feaSAlan Cox out_release_region: 295272d39feaSAlan Cox release_region(host->addr, 256); 295372d39feaSAlan Cox out_host_put: 295472d39feaSAlan Cox scsi_host_put(shost); 295572d39feaSAlan Cox out_disable_device: 295672d39feaSAlan Cox pci_disable_device(pdev); 295772d39feaSAlan Cox return error; 295872d39feaSAlan Cox } 295972d39feaSAlan Cox 296072d39feaSAlan Cox /** 296172d39feaSAlan Cox * initio_remove_one - control shutdown 296272d39feaSAlan Cox * @pdev: PCI device being released 296372d39feaSAlan Cox * 296472d39feaSAlan Cox * Release the resources assigned to this adapter after it has 296572d39feaSAlan Cox * finished being used. 296672d39feaSAlan Cox */ 296772d39feaSAlan Cox 296872d39feaSAlan Cox static void initio_remove_one(struct pci_dev *pdev) 296972d39feaSAlan Cox { 297072d39feaSAlan Cox struct Scsi_Host *host = pci_get_drvdata(pdev); 297172d39feaSAlan Cox struct initio_host *s = (struct initio_host *)host->hostdata; 297272d39feaSAlan Cox scsi_remove_host(host); 297372d39feaSAlan Cox free_irq(pdev->irq, host); 297472d39feaSAlan Cox release_region(s->addr, 256); 297572d39feaSAlan Cox scsi_host_put(host); 297672d39feaSAlan Cox pci_disable_device(pdev); 297772d39feaSAlan Cox } 297872d39feaSAlan Cox 297972d39feaSAlan Cox MODULE_LICENSE("GPL"); 298072d39feaSAlan Cox 298172d39feaSAlan Cox static struct pci_device_id initio_pci_tbl[] = { 298272d39feaSAlan Cox {PCI_VENDOR_ID_INIT, 0x9500, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 298372d39feaSAlan Cox {PCI_VENDOR_ID_INIT, 0x9400, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 298472d39feaSAlan Cox {PCI_VENDOR_ID_INIT, 0x9401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 298572d39feaSAlan Cox {PCI_VENDOR_ID_INIT, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 298672d39feaSAlan Cox {PCI_VENDOR_ID_DOMEX, 0x0002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, 298772d39feaSAlan Cox {0,} 298872d39feaSAlan Cox }; 298972d39feaSAlan Cox MODULE_DEVICE_TABLE(pci, initio_pci_tbl); 299072d39feaSAlan Cox 299172d39feaSAlan Cox static struct pci_driver initio_pci_driver = { 299272d39feaSAlan Cox .name = "initio", 299372d39feaSAlan Cox .id_table = initio_pci_tbl, 299472d39feaSAlan Cox .probe = initio_probe_one, 29956f039790SGreg Kroah-Hartman .remove = initio_remove_one, 299672d39feaSAlan Cox }; 299772d39feaSAlan Cox 299872d39feaSAlan Cox static int __init initio_init_driver(void) 299972d39feaSAlan Cox { 300072d39feaSAlan Cox return pci_register_driver(&initio_pci_driver); 300172d39feaSAlan Cox } 300272d39feaSAlan Cox 300372d39feaSAlan Cox static void __exit initio_exit_driver(void) 300472d39feaSAlan Cox { 300572d39feaSAlan Cox pci_unregister_driver(&initio_pci_driver); 300672d39feaSAlan Cox } 300772d39feaSAlan Cox 300872d39feaSAlan Cox MODULE_DESCRIPTION("Initio INI-9X00U/UW SCSI device driver"); 300972d39feaSAlan Cox MODULE_AUTHOR("Initio Corporation"); 301072d39feaSAlan Cox MODULE_LICENSE("GPL"); 301172d39feaSAlan Cox 301272d39feaSAlan Cox module_init(initio_init_driver); 301372d39feaSAlan Cox module_exit(initio_exit_driver); 3014