11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Copyright (c) 1996 John Shifflett, GeoLog Consulting 31da177e4SLinus Torvalds * john@geolog.com 41da177e4SLinus Torvalds * jshiffle@netcom.com 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2, or (at your option) 91da177e4SLinus Torvalds * any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds */ 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds /* 181da177e4SLinus Torvalds * Drew Eckhardt's excellent 'Generic NCR5380' sources from Linux-PC 191da177e4SLinus Torvalds * provided much of the inspiration and some of the code for this 201da177e4SLinus Torvalds * driver. Everything I know about Amiga DMA was gleaned from careful 211da177e4SLinus Torvalds * reading of Hamish Mcdonald's original wd33c93 driver; in fact, I 221da177e4SLinus Torvalds * borrowed shamelessly from all over that source. Thanks Hamish! 231da177e4SLinus Torvalds * 241da177e4SLinus Torvalds * _This_ driver is (I feel) an improvement over the old one in 251da177e4SLinus Torvalds * several respects: 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds * - Target Disconnection/Reconnection is now supported. Any 281da177e4SLinus Torvalds * system with more than one device active on the SCSI bus 291da177e4SLinus Torvalds * will benefit from this. The driver defaults to what I 301da177e4SLinus Torvalds * call 'adaptive disconnect' - meaning that each command 311da177e4SLinus Torvalds * is evaluated individually as to whether or not it should 321da177e4SLinus Torvalds * be run with the option to disconnect/reselect (if the 331da177e4SLinus Torvalds * device chooses), or as a "SCSI-bus-hog". 341da177e4SLinus Torvalds * 351da177e4SLinus Torvalds * - Synchronous data transfers are now supported. Because of 361da177e4SLinus Torvalds * a few devices that choke after telling the driver that 371da177e4SLinus Torvalds * they can do sync transfers, we don't automatically use 381da177e4SLinus Torvalds * this faster protocol - it can be enabled via the command- 391da177e4SLinus Torvalds * line on a device-by-device basis. 401da177e4SLinus Torvalds * 411da177e4SLinus Torvalds * - Runtime operating parameters can now be specified through 421da177e4SLinus Torvalds * the 'amiboot' or the 'insmod' command line. For amiboot do: 431da177e4SLinus Torvalds * "amiboot [usual stuff] wd33c93=blah,blah,blah" 441da177e4SLinus Torvalds * The defaults should be good for most people. See the comment 451da177e4SLinus Torvalds * for 'setup_strings' below for more details. 461da177e4SLinus Torvalds * 471da177e4SLinus Torvalds * - The old driver relied exclusively on what the Western Digital 481da177e4SLinus Torvalds * docs call "Combination Level 2 Commands", which are a great 491da177e4SLinus Torvalds * idea in that the CPU is relieved of a lot of interrupt 501da177e4SLinus Torvalds * overhead. However, by accepting a certain (user-settable) 511da177e4SLinus Torvalds * amount of additional interrupts, this driver achieves 521da177e4SLinus Torvalds * better control over the SCSI bus, and data transfers are 531da177e4SLinus Torvalds * almost as fast while being much easier to define, track, 541da177e4SLinus Torvalds * and debug. 551da177e4SLinus Torvalds * 561da177e4SLinus Torvalds * 571da177e4SLinus Torvalds * TODO: 581da177e4SLinus Torvalds * more speed. linked commands. 591da177e4SLinus Torvalds * 601da177e4SLinus Torvalds * 611da177e4SLinus Torvalds * People with bug reports, wish-lists, complaints, comments, 621da177e4SLinus Torvalds * or improvements are asked to pah-leeez email me (John Shifflett) 631da177e4SLinus Torvalds * at john@geolog.com or jshiffle@netcom.com! I'm anxious to get 641da177e4SLinus Torvalds * this thing into as good a shape as possible, and I'm positive 651da177e4SLinus Torvalds * there are lots of lurking bugs and "Stupid Places". 661da177e4SLinus Torvalds * 671da177e4SLinus Torvalds * Updates: 681da177e4SLinus Torvalds * 691da177e4SLinus Torvalds * Added support for pre -A chips, which don't have advanced features 701da177e4SLinus Torvalds * and will generate CSR_RESEL rather than CSR_RESEL_AM. 711da177e4SLinus Torvalds * Richard Hirst <richard@sleepie.demon.co.uk> August 2000 72a5d8421bSpeter fuerst * 73a5d8421bSpeter fuerst * Added support for Burst Mode DMA and Fast SCSI. Enabled the use of 74a5d8421bSpeter fuerst * default_sx_per for asynchronous data transfers. Added adjustment 75a5d8421bSpeter fuerst * of transfer periods in sx_table to the actual input-clock. 76a5d8421bSpeter fuerst * peter fuerst <post@pfrst.de> February 2007 771da177e4SLinus Torvalds */ 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds #include <linux/module.h> 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds #include <linux/string.h> 821da177e4SLinus Torvalds #include <linux/delay.h> 831da177e4SLinus Torvalds #include <linux/init.h> 84cf7f5b45SRalf Baechle #include <linux/interrupt.h> 851da177e4SLinus Torvalds #include <linux/blkdev.h> 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds #include <scsi/scsi.h> 881da177e4SLinus Torvalds #include <scsi/scsi_cmnd.h> 891da177e4SLinus Torvalds #include <scsi/scsi_device.h> 901da177e4SLinus Torvalds #include <scsi/scsi_host.h> 911da177e4SLinus Torvalds 92078dda95SAdrian Bunk #include <asm/irq.h> 93078dda95SAdrian Bunk 941da177e4SLinus Torvalds #include "wd33c93.h" 951da177e4SLinus Torvalds 96a5d8421bSpeter fuerst #define optimum_sx_per(hostdata) (hostdata)->sx_table[1].period_ns 971da177e4SLinus Torvalds 98a5d8421bSpeter fuerst 99a5d8421bSpeter fuerst #define WD33C93_VERSION "1.26++" 100a5d8421bSpeter fuerst #define WD33C93_DATE "10/Feb/2007" 1011da177e4SLinus Torvalds 1021da177e4SLinus Torvalds MODULE_AUTHOR("John Shifflett"); 1031da177e4SLinus Torvalds MODULE_DESCRIPTION("Generic WD33C93 SCSI driver"); 1041da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 1051da177e4SLinus Torvalds 1061da177e4SLinus Torvalds /* 1071da177e4SLinus Torvalds * 'setup_strings' is a single string used to pass operating parameters and 1081da177e4SLinus Torvalds * settings from the kernel/module command-line to the driver. 'setup_args[]' 1091da177e4SLinus Torvalds * is an array of strings that define the compile-time default values for 1101da177e4SLinus Torvalds * these settings. If Linux boots with an amiboot or insmod command-line, 1111da177e4SLinus Torvalds * those settings are combined with 'setup_args[]'. Note that amiboot 1121da177e4SLinus Torvalds * command-lines are prefixed with "wd33c93=" while insmod uses a 1131da177e4SLinus Torvalds * "setup_strings=" prefix. The driver recognizes the following keywords 1141da177e4SLinus Torvalds * (lower case required) and arguments: 1151da177e4SLinus Torvalds * 1161da177e4SLinus Torvalds * - nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with 1171da177e4SLinus Torvalds * the 7 possible SCSI devices. Set a bit to negotiate for 1181da177e4SLinus Torvalds * asynchronous transfers on that device. To maintain 1191da177e4SLinus Torvalds * backwards compatibility, a command-line such as 1201da177e4SLinus Torvalds * "wd33c93=255" will be automatically translated to 1211da177e4SLinus Torvalds * "wd33c93=nosync:0xff". 1221da177e4SLinus Torvalds * - nodma:x -x = 1 to disable DMA, x = 0 to enable it. Argument is 1231da177e4SLinus Torvalds * optional - if not present, same as "nodma:1". 1241da177e4SLinus Torvalds * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer 1251da177e4SLinus Torvalds * period. Default is 500; acceptable values are 250 - 1000. 1261da177e4SLinus Torvalds * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them. 1271da177e4SLinus Torvalds * x = 1 does 'adaptive' disconnects, which is the default 1281da177e4SLinus Torvalds * and generally the best choice. 1291da177e4SLinus Torvalds * - debug:x -If 'DEBUGGING_ON' is defined, x is a bit mask that causes 1301da177e4SLinus Torvalds * various types of debug output to printed - see the DB_xxx 1311da177e4SLinus Torvalds * defines in wd33c93.h 1321da177e4SLinus Torvalds * - clock:x -x = clock input in MHz for WD33c93 chip. Normal values 1331da177e4SLinus Torvalds * would be from 8 through 20. Default is 8. 134a5d8421bSpeter fuerst * - burst:x -x = 1 to use Burst Mode (or Demand-Mode) DMA, x = 0 to use 135a5d8421bSpeter fuerst * Single Byte DMA, which is the default. Argument is 136a5d8421bSpeter fuerst * optional - if not present, same as "burst:1". 137a5d8421bSpeter fuerst * - fast:x -x = 1 to enable Fast SCSI, which is only effective with 138a5d8421bSpeter fuerst * input-clock divisor 4 (WD33C93_FS_16_20), x = 0 to disable 139a5d8421bSpeter fuerst * it, which is the default. Argument is optional - if not 140a5d8421bSpeter fuerst * present, same as "fast:1". 1411da177e4SLinus Torvalds * - next -No argument. Used to separate blocks of keywords when 1421da177e4SLinus Torvalds * there's more than one host adapter in the system. 1431da177e4SLinus Torvalds * 1441da177e4SLinus Torvalds * Syntax Notes: 1451da177e4SLinus Torvalds * - Numeric arguments can be decimal or the '0x' form of hex notation. There 1461da177e4SLinus Torvalds * _must_ be a colon between a keyword and its numeric argument, with no 1471da177e4SLinus Torvalds * spaces. 1481da177e4SLinus Torvalds * - Keywords are separated by commas, no spaces, in the standard kernel 1491da177e4SLinus Torvalds * command-line manner. 1501da177e4SLinus Torvalds * - A keyword in the 'nth' comma-separated command-line member will overwrite 1511da177e4SLinus Torvalds * the 'nth' element of setup_args[]. A blank command-line member (in 1521da177e4SLinus Torvalds * other words, a comma with no preceding keyword) will _not_ overwrite 1531da177e4SLinus Torvalds * the corresponding setup_args[] element. 1541da177e4SLinus Torvalds * - If a keyword is used more than once, the first one applies to the first 1551da177e4SLinus Torvalds * SCSI host found, the second to the second card, etc, unless the 'next' 1561da177e4SLinus Torvalds * keyword is used to change the order. 1571da177e4SLinus Torvalds * 1581da177e4SLinus Torvalds * Some amiboot examples (for insmod, use 'setup_strings' instead of 'wd33c93'): 1591da177e4SLinus Torvalds * - wd33c93=nosync:255 1601da177e4SLinus Torvalds * - wd33c93=nodma 1611da177e4SLinus Torvalds * - wd33c93=nodma:1 1621da177e4SLinus Torvalds * - wd33c93=disconnect:2,nosync:0x08,period:250 1631da177e4SLinus Torvalds * - wd33c93=debug:0x1c 1641da177e4SLinus Torvalds */ 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds /* Normally, no defaults are specified */ 167a5d8421bSpeter fuerst static char *setup_args[] = { "", "", "", "", "", "", "", "", "", "" }; 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds static char *setup_strings; 1701da177e4SLinus Torvalds module_param(setup_strings, charp, 0); 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds static void wd33c93_execute(struct Scsi_Host *instance); 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds #ifdef CONFIG_WD33C93_PIO 1751da177e4SLinus Torvalds static inline uchar 1761da177e4SLinus Torvalds read_wd33c93(const wd33c93_regs regs, uchar reg_num) 1771da177e4SLinus Torvalds { 1781da177e4SLinus Torvalds uchar data; 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds outb(reg_num, regs.SASR); 1811da177e4SLinus Torvalds data = inb(regs.SCMD); 1821da177e4SLinus Torvalds return data; 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds static inline unsigned long 1861da177e4SLinus Torvalds read_wd33c93_count(const wd33c93_regs regs) 1871da177e4SLinus Torvalds { 1881da177e4SLinus Torvalds unsigned long value; 1891da177e4SLinus Torvalds 1901da177e4SLinus Torvalds outb(WD_TRANSFER_COUNT_MSB, regs.SASR); 1911da177e4SLinus Torvalds value = inb(regs.SCMD) << 16; 1921da177e4SLinus Torvalds value |= inb(regs.SCMD) << 8; 1931da177e4SLinus Torvalds value |= inb(regs.SCMD); 1941da177e4SLinus Torvalds return value; 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds static inline uchar 1981da177e4SLinus Torvalds read_aux_stat(const wd33c93_regs regs) 1991da177e4SLinus Torvalds { 2001da177e4SLinus Torvalds return inb(regs.SASR); 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds static inline void 2041da177e4SLinus Torvalds write_wd33c93(const wd33c93_regs regs, uchar reg_num, uchar value) 2051da177e4SLinus Torvalds { 2061da177e4SLinus Torvalds outb(reg_num, regs.SASR); 2071da177e4SLinus Torvalds outb(value, regs.SCMD); 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds static inline void 2111da177e4SLinus Torvalds write_wd33c93_count(const wd33c93_regs regs, unsigned long value) 2121da177e4SLinus Torvalds { 2131da177e4SLinus Torvalds outb(WD_TRANSFER_COUNT_MSB, regs.SASR); 2141da177e4SLinus Torvalds outb((value >> 16) & 0xff, regs.SCMD); 2151da177e4SLinus Torvalds outb((value >> 8) & 0xff, regs.SCMD); 2161da177e4SLinus Torvalds outb( value & 0xff, regs.SCMD); 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds #define write_wd33c93_cmd(regs, cmd) \ 2201da177e4SLinus Torvalds write_wd33c93((regs), WD_COMMAND, (cmd)) 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds static inline void 2231da177e4SLinus Torvalds write_wd33c93_cdb(const wd33c93_regs regs, uint len, uchar cmnd[]) 2241da177e4SLinus Torvalds { 2251da177e4SLinus Torvalds int i; 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds outb(WD_CDB_1, regs.SASR); 2281da177e4SLinus Torvalds for (i=0; i<len; i++) 2291da177e4SLinus Torvalds outb(cmnd[i], regs.SCMD); 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds #else /* CONFIG_WD33C93_PIO */ 2331da177e4SLinus Torvalds static inline uchar 2341da177e4SLinus Torvalds read_wd33c93(const wd33c93_regs regs, uchar reg_num) 2351da177e4SLinus Torvalds { 2361da177e4SLinus Torvalds *regs.SASR = reg_num; 2371da177e4SLinus Torvalds mb(); 2381da177e4SLinus Torvalds return (*regs.SCMD); 2391da177e4SLinus Torvalds } 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds static unsigned long 2421da177e4SLinus Torvalds read_wd33c93_count(const wd33c93_regs regs) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds unsigned long value; 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds *regs.SASR = WD_TRANSFER_COUNT_MSB; 2471da177e4SLinus Torvalds mb(); 2481da177e4SLinus Torvalds value = *regs.SCMD << 16; 2491da177e4SLinus Torvalds value |= *regs.SCMD << 8; 2501da177e4SLinus Torvalds value |= *regs.SCMD; 2511da177e4SLinus Torvalds mb(); 2521da177e4SLinus Torvalds return value; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds static inline uchar 2561da177e4SLinus Torvalds read_aux_stat(const wd33c93_regs regs) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds return *regs.SASR; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds static inline void 2621da177e4SLinus Torvalds write_wd33c93(const wd33c93_regs regs, uchar reg_num, uchar value) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds *regs.SASR = reg_num; 2651da177e4SLinus Torvalds mb(); 2661da177e4SLinus Torvalds *regs.SCMD = value; 2671da177e4SLinus Torvalds mb(); 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds static void 2711da177e4SLinus Torvalds write_wd33c93_count(const wd33c93_regs regs, unsigned long value) 2721da177e4SLinus Torvalds { 2731da177e4SLinus Torvalds *regs.SASR = WD_TRANSFER_COUNT_MSB; 2741da177e4SLinus Torvalds mb(); 2751da177e4SLinus Torvalds *regs.SCMD = value >> 16; 2761da177e4SLinus Torvalds *regs.SCMD = value >> 8; 2771da177e4SLinus Torvalds *regs.SCMD = value; 2781da177e4SLinus Torvalds mb(); 2791da177e4SLinus Torvalds } 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds static inline void 2821da177e4SLinus Torvalds write_wd33c93_cmd(const wd33c93_regs regs, uchar cmd) 2831da177e4SLinus Torvalds { 2841da177e4SLinus Torvalds *regs.SASR = WD_COMMAND; 2851da177e4SLinus Torvalds mb(); 2861da177e4SLinus Torvalds *regs.SCMD = cmd; 2871da177e4SLinus Torvalds mb(); 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds static inline void 2911da177e4SLinus Torvalds write_wd33c93_cdb(const wd33c93_regs regs, uint len, uchar cmnd[]) 2921da177e4SLinus Torvalds { 2931da177e4SLinus Torvalds int i; 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds *regs.SASR = WD_CDB_1; 2961da177e4SLinus Torvalds for (i = 0; i < len; i++) 2971da177e4SLinus Torvalds *regs.SCMD = cmnd[i]; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds #endif /* CONFIG_WD33C93_PIO */ 3001da177e4SLinus Torvalds 3011da177e4SLinus Torvalds static inline uchar 3021da177e4SLinus Torvalds read_1_byte(const wd33c93_regs regs) 3031da177e4SLinus Torvalds { 3041da177e4SLinus Torvalds uchar asr; 3051da177e4SLinus Torvalds uchar x = 0; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); 3081da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO | 0x80); 3091da177e4SLinus Torvalds do { 3101da177e4SLinus Torvalds asr = read_aux_stat(regs); 3111da177e4SLinus Torvalds if (asr & ASR_DBR) 3121da177e4SLinus Torvalds x = read_wd33c93(regs, WD_DATA); 3131da177e4SLinus Torvalds } while (!(asr & ASR_INT)); 3141da177e4SLinus Torvalds return x; 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds static int 318a5d8421bSpeter fuerst round_period(unsigned int period, const struct sx_period *sx_table) 3191da177e4SLinus Torvalds { 3201da177e4SLinus Torvalds int x; 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds for (x = 1; sx_table[x].period_ns; x++) { 3231da177e4SLinus Torvalds if ((period <= sx_table[x - 0].period_ns) && 3241da177e4SLinus Torvalds (period > sx_table[x - 1].period_ns)) { 3251da177e4SLinus Torvalds return x; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds return 7; 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 331a5d8421bSpeter fuerst /* 332a5d8421bSpeter fuerst * Calculate Synchronous Transfer Register value from SDTR code. 333a5d8421bSpeter fuerst */ 3341da177e4SLinus Torvalds static uchar 335a5d8421bSpeter fuerst calc_sync_xfer(unsigned int period, unsigned int offset, unsigned int fast, 336a5d8421bSpeter fuerst const struct sx_period *sx_table) 3371da177e4SLinus Torvalds { 338a5d8421bSpeter fuerst /* When doing Fast SCSI synchronous data transfers, the corresponding 339a5d8421bSpeter fuerst * value in 'sx_table' is two times the actually used transfer period. 340a5d8421bSpeter fuerst */ 3411da177e4SLinus Torvalds uchar result; 3421da177e4SLinus Torvalds 343a5d8421bSpeter fuerst if (offset && fast) { 344a5d8421bSpeter fuerst fast = STR_FSS; 345a5d8421bSpeter fuerst period *= 2; 346a5d8421bSpeter fuerst } else { 347a5d8421bSpeter fuerst fast = 0; 348a5d8421bSpeter fuerst } 3491da177e4SLinus Torvalds period *= 4; /* convert SDTR code to ns */ 350a5d8421bSpeter fuerst result = sx_table[round_period(period,sx_table)].reg_value; 3511da177e4SLinus Torvalds result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF; 352a5d8421bSpeter fuerst result |= fast; 3531da177e4SLinus Torvalds return result; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 356a5d8421bSpeter fuerst /* 357a5d8421bSpeter fuerst * Calculate SDTR code bytes [3],[4] from period and offset. 358a5d8421bSpeter fuerst */ 359a5d8421bSpeter fuerst static inline void 360a5d8421bSpeter fuerst calc_sync_msg(unsigned int period, unsigned int offset, unsigned int fast, 361a5d8421bSpeter fuerst uchar msg[2]) 362a5d8421bSpeter fuerst { 363a5d8421bSpeter fuerst /* 'period' is a "normal"-mode value, like the ones in 'sx_table'. The 364a5d8421bSpeter fuerst * actually used transfer period for Fast SCSI synchronous data 365a5d8421bSpeter fuerst * transfers is half that value. 366a5d8421bSpeter fuerst */ 367a5d8421bSpeter fuerst period /= 4; 368a5d8421bSpeter fuerst if (offset && fast) 369a5d8421bSpeter fuerst period /= 2; 370a5d8421bSpeter fuerst msg[0] = period; 371a5d8421bSpeter fuerst msg[1] = offset; 372a5d8421bSpeter fuerst } 373a5d8421bSpeter fuerst 374f281233dSJeff Garzik static int 375f281233dSJeff Garzik wd33c93_queuecommand_lck(struct scsi_cmnd *cmd, 3761da177e4SLinus Torvalds void (*done)(struct scsi_cmnd *)) 3771da177e4SLinus Torvalds { 3781da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata; 3791da177e4SLinus Torvalds struct scsi_cmnd *tmp; 3801da177e4SLinus Torvalds 3811da177e4SLinus Torvalds hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata; 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds DB(DB_QUEUE_COMMAND, 3845cd049a5SChristoph Hellwig printk("Q-%d-%02x( ", cmd->device->id, cmd->cmnd[0])) 3851da177e4SLinus Torvalds 3861da177e4SLinus Torvalds /* Set up a few fields in the scsi_cmnd structure for our own use: 3871da177e4SLinus Torvalds * - host_scribble is the pointer to the next cmd in the input queue 3881da177e4SLinus Torvalds * - scsi_done points to the routine we call when a cmd is finished 3891da177e4SLinus Torvalds * - result is what you'd expect 3901da177e4SLinus Torvalds */ 3911da177e4SLinus Torvalds cmd->host_scribble = NULL; 3921da177e4SLinus Torvalds cmd->scsi_done = done; 3931da177e4SLinus Torvalds cmd->result = 0; 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds /* We use the Scsi_Pointer structure that's included with each command 3961da177e4SLinus Torvalds * as a scratchpad (as it's intended to be used!). The handy thing about 3971da177e4SLinus Torvalds * the SCp.xxx fields is that they're always associated with a given 3981da177e4SLinus Torvalds * cmd, and are preserved across disconnect-reselect. This means we 3991da177e4SLinus Torvalds * can pretty much ignore SAVE_POINTERS and RESTORE_POINTERS messages 4001da177e4SLinus Torvalds * if we keep all the critical pointers and counters in SCp: 4011da177e4SLinus Torvalds * - SCp.ptr is the pointer into the RAM buffer 4021da177e4SLinus Torvalds * - SCp.this_residual is the size of that buffer 4031da177e4SLinus Torvalds * - SCp.buffer points to the current scatter-gather buffer 4041da177e4SLinus Torvalds * - SCp.buffers_residual tells us how many S.G. buffers there are 4051da177e4SLinus Torvalds * - SCp.have_data_in is not used 4061da177e4SLinus Torvalds * - SCp.sent_command is not used 4071da177e4SLinus Torvalds * - SCp.phase records this command's SRCID_ER bit setting 4081da177e4SLinus Torvalds */ 4091da177e4SLinus Torvalds 410ee0ae927SBoaz Harrosh if (scsi_bufflen(cmd)) { 411ee0ae927SBoaz Harrosh cmd->SCp.buffer = scsi_sglist(cmd); 412ee0ae927SBoaz Harrosh cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1; 41345711f1aSJens Axboe cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); 4141da177e4SLinus Torvalds cmd->SCp.this_residual = cmd->SCp.buffer->length; 4151da177e4SLinus Torvalds } else { 4161da177e4SLinus Torvalds cmd->SCp.buffer = NULL; 4171da177e4SLinus Torvalds cmd->SCp.buffers_residual = 0; 418ee0ae927SBoaz Harrosh cmd->SCp.ptr = NULL; 419ee0ae927SBoaz Harrosh cmd->SCp.this_residual = 0; 4201da177e4SLinus Torvalds } 4211da177e4SLinus Torvalds 4221da177e4SLinus Torvalds /* WD docs state that at the conclusion of a "LEVEL2" command, the 4231da177e4SLinus Torvalds * status byte can be retrieved from the LUN register. Apparently, 4241da177e4SLinus Torvalds * this is the case only for *uninterrupted* LEVEL2 commands! If 4251da177e4SLinus Torvalds * there are any unexpected phases entered, even if they are 100% 4261da177e4SLinus Torvalds * legal (different devices may choose to do things differently), 4271da177e4SLinus Torvalds * the LEVEL2 command sequence is exited. This often occurs prior 4281da177e4SLinus Torvalds * to receiving the status byte, in which case the driver does a 4291da177e4SLinus Torvalds * status phase interrupt and gets the status byte on its own. 4301da177e4SLinus Torvalds * While such a command can then be "resumed" (ie restarted to 4311da177e4SLinus Torvalds * finish up as a LEVEL2 command), the LUN register will NOT be 4321da177e4SLinus Torvalds * a valid status byte at the command's conclusion, and we must 4331da177e4SLinus Torvalds * use the byte obtained during the earlier interrupt. Here, we 4341da177e4SLinus Torvalds * preset SCp.Status to an illegal value (0xff) so that when 4351da177e4SLinus Torvalds * this command finally completes, we can tell where the actual 4361da177e4SLinus Torvalds * status byte is stored. 4371da177e4SLinus Torvalds */ 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds cmd->SCp.Status = ILLEGAL_STATUS_BYTE; 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds /* 4421da177e4SLinus Torvalds * Add the cmd to the end of 'input_Q'. Note that REQUEST SENSE 4431da177e4SLinus Torvalds * commands are added to the head of the queue so that the desired 4441da177e4SLinus Torvalds * sense data is not lost before REQUEST_SENSE executes. 4451da177e4SLinus Torvalds */ 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds spin_lock_irq(&hostdata->lock); 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) { 4501da177e4SLinus Torvalds cmd->host_scribble = (uchar *) hostdata->input_Q; 4511da177e4SLinus Torvalds hostdata->input_Q = cmd; 4521da177e4SLinus Torvalds } else { /* find the end of the queue */ 4531da177e4SLinus Torvalds for (tmp = (struct scsi_cmnd *) hostdata->input_Q; 4541da177e4SLinus Torvalds tmp->host_scribble; 4551da177e4SLinus Torvalds tmp = (struct scsi_cmnd *) tmp->host_scribble) ; 4561da177e4SLinus Torvalds tmp->host_scribble = (uchar *) cmd; 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds /* We know that there's at least one command in 'input_Q' now. 4601da177e4SLinus Torvalds * Go see if any of them are runnable! 4611da177e4SLinus Torvalds */ 4621da177e4SLinus Torvalds 4631da177e4SLinus Torvalds wd33c93_execute(cmd->device->host); 4641da177e4SLinus Torvalds 4655cd049a5SChristoph Hellwig DB(DB_QUEUE_COMMAND, printk(")Q ")) 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds spin_unlock_irq(&hostdata->lock); 4681da177e4SLinus Torvalds return 0; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 471f281233dSJeff Garzik DEF_SCSI_QCMD(wd33c93_queuecommand) 472f281233dSJeff Garzik 4731da177e4SLinus Torvalds /* 4741da177e4SLinus Torvalds * This routine attempts to start a scsi command. If the host_card is 4751da177e4SLinus Torvalds * already connected, we give up immediately. Otherwise, look through 4761da177e4SLinus Torvalds * the input_Q, using the first command we find that's intended 4771da177e4SLinus Torvalds * for a currently non-busy target/lun. 4781da177e4SLinus Torvalds * 4791da177e4SLinus Torvalds * wd33c93_execute() is always called with interrupts disabled or from 4801da177e4SLinus Torvalds * the wd33c93_intr itself, which means that a wd33c93 interrupt 4811da177e4SLinus Torvalds * cannot occur while we are in here. 4821da177e4SLinus Torvalds */ 4831da177e4SLinus Torvalds static void 4841da177e4SLinus Torvalds wd33c93_execute(struct Scsi_Host *instance) 4851da177e4SLinus Torvalds { 4861da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata = 4871da177e4SLinus Torvalds (struct WD33C93_hostdata *) instance->hostdata; 4881da177e4SLinus Torvalds const wd33c93_regs regs = hostdata->regs; 4891da177e4SLinus Torvalds struct scsi_cmnd *cmd, *prev; 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds DB(DB_EXECUTE, printk("EX(")) 4921da177e4SLinus Torvalds if (hostdata->selecting || hostdata->connected) { 4931da177e4SLinus Torvalds DB(DB_EXECUTE, printk(")EX-0 ")) 4941da177e4SLinus Torvalds return; 4951da177e4SLinus Torvalds } 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds /* 4981da177e4SLinus Torvalds * Search through the input_Q for a command destined 4991da177e4SLinus Torvalds * for an idle target/lun. 5001da177e4SLinus Torvalds */ 5011da177e4SLinus Torvalds 5021da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hostdata->input_Q; 503a5d361fcSAl Viro prev = NULL; 5041da177e4SLinus Torvalds while (cmd) { 5059cb78c16SHannes Reinecke if (!(hostdata->busy[cmd->device->id] & 5069cb78c16SHannes Reinecke (1 << (cmd->device->lun & 0xff)))) 5071da177e4SLinus Torvalds break; 5081da177e4SLinus Torvalds prev = cmd; 5091da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) cmd->host_scribble; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds /* quit if queue empty or all possible targets are busy */ 5131da177e4SLinus Torvalds 5141da177e4SLinus Torvalds if (!cmd) { 5151da177e4SLinus Torvalds DB(DB_EXECUTE, printk(")EX-1 ")) 5161da177e4SLinus Torvalds return; 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds 5191da177e4SLinus Torvalds /* remove command from queue */ 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds if (prev) 5221da177e4SLinus Torvalds prev->host_scribble = cmd->host_scribble; 5231da177e4SLinus Torvalds else 5241da177e4SLinus Torvalds hostdata->input_Q = (struct scsi_cmnd *) cmd->host_scribble; 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds #ifdef PROC_STATISTICS 5271da177e4SLinus Torvalds hostdata->cmd_cnt[cmd->device->id]++; 5281da177e4SLinus Torvalds #endif 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds /* 5311da177e4SLinus Torvalds * Start the selection process 5321da177e4SLinus Torvalds */ 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds if (cmd->sc_data_direction == DMA_TO_DEVICE) 5351da177e4SLinus Torvalds write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id); 5361da177e4SLinus Torvalds else 5371da177e4SLinus Torvalds write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD); 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds /* Now we need to figure out whether or not this command is a good 5401da177e4SLinus Torvalds * candidate for disconnect/reselect. We guess to the best of our 5411da177e4SLinus Torvalds * ability, based on a set of hierarchical rules. When several 5421da177e4SLinus Torvalds * devices are operating simultaneously, disconnects are usually 5431da177e4SLinus Torvalds * an advantage. In a single device system, or if only 1 device 5441da177e4SLinus Torvalds * is being accessed, transfers usually go faster if disconnects 5451da177e4SLinus Torvalds * are not allowed: 5461da177e4SLinus Torvalds * 5471da177e4SLinus Torvalds * + Commands should NEVER disconnect if hostdata->disconnect = 5481da177e4SLinus Torvalds * DIS_NEVER (this holds for tape drives also), and ALWAYS 5491da177e4SLinus Torvalds * disconnect if hostdata->disconnect = DIS_ALWAYS. 5501da177e4SLinus Torvalds * + Tape drive commands should always be allowed to disconnect. 5511da177e4SLinus Torvalds * + Disconnect should be allowed if disconnected_Q isn't empty. 5521da177e4SLinus Torvalds * + Commands should NOT disconnect if input_Q is empty. 5531da177e4SLinus Torvalds * + Disconnect should be allowed if there are commands in input_Q 5541da177e4SLinus Torvalds * for a different target/lun. In this case, the other commands 5551da177e4SLinus Torvalds * should be made disconnect-able, if not already. 5561da177e4SLinus Torvalds * 5571da177e4SLinus Torvalds * I know, I know - this code would flunk me out of any 5581da177e4SLinus Torvalds * "C Programming 101" class ever offered. But it's easy 5591da177e4SLinus Torvalds * to change around and experiment with for now. 5601da177e4SLinus Torvalds */ 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds cmd->SCp.phase = 0; /* assume no disconnect */ 5631da177e4SLinus Torvalds if (hostdata->disconnect == DIS_NEVER) 5641da177e4SLinus Torvalds goto no; 5651da177e4SLinus Torvalds if (hostdata->disconnect == DIS_ALWAYS) 5661da177e4SLinus Torvalds goto yes; 5671da177e4SLinus Torvalds if (cmd->device->type == 1) /* tape drive? */ 5681da177e4SLinus Torvalds goto yes; 5691da177e4SLinus Torvalds if (hostdata->disconnected_Q) /* other commands disconnected? */ 5701da177e4SLinus Torvalds goto yes; 5711da177e4SLinus Torvalds if (!(hostdata->input_Q)) /* input_Q empty? */ 5721da177e4SLinus Torvalds goto no; 5731da177e4SLinus Torvalds for (prev = (struct scsi_cmnd *) hostdata->input_Q; prev; 5741da177e4SLinus Torvalds prev = (struct scsi_cmnd *) prev->host_scribble) { 5751da177e4SLinus Torvalds if ((prev->device->id != cmd->device->id) || 5761da177e4SLinus Torvalds (prev->device->lun != cmd->device->lun)) { 5771da177e4SLinus Torvalds for (prev = (struct scsi_cmnd *) hostdata->input_Q; prev; 5781da177e4SLinus Torvalds prev = (struct scsi_cmnd *) prev->host_scribble) 5791da177e4SLinus Torvalds prev->SCp.phase = 1; 5801da177e4SLinus Torvalds goto yes; 5811da177e4SLinus Torvalds } 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds 5841da177e4SLinus Torvalds goto no; 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds yes: 5871da177e4SLinus Torvalds cmd->SCp.phase = 1; 5881da177e4SLinus Torvalds 5891da177e4SLinus Torvalds #ifdef PROC_STATISTICS 5901da177e4SLinus Torvalds hostdata->disc_allowed_cnt[cmd->device->id]++; 5911da177e4SLinus Torvalds #endif 5921da177e4SLinus Torvalds 5931da177e4SLinus Torvalds no: 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds write_wd33c93(regs, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0)); 5961da177e4SLinus Torvalds 5979cb78c16SHannes Reinecke write_wd33c93(regs, WD_TARGET_LUN, (u8)cmd->device->lun); 5981da177e4SLinus Torvalds write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, 5991da177e4SLinus Torvalds hostdata->sync_xfer[cmd->device->id]); 6009cb78c16SHannes Reinecke hostdata->busy[cmd->device->id] |= (1 << (cmd->device->lun & 0xFF)); 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds if ((hostdata->level2 == L2_NONE) || 6031da177e4SLinus Torvalds (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) { 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds /* 6061da177e4SLinus Torvalds * Do a 'Select-With-ATN' command. This will end with 6071da177e4SLinus Torvalds * one of the following interrupts: 6081da177e4SLinus Torvalds * CSR_RESEL_AM: failure - can try again later. 6091da177e4SLinus Torvalds * CSR_TIMEOUT: failure - give up. 6101da177e4SLinus Torvalds * CSR_SELECT: success - proceed. 6111da177e4SLinus Torvalds */ 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds hostdata->selecting = cmd; 6141da177e4SLinus Torvalds 6151da177e4SLinus Torvalds /* Every target has its own synchronous transfer setting, kept in the 6161da177e4SLinus Torvalds * sync_xfer array, and a corresponding status byte in sync_stat[]. 6171da177e4SLinus Torvalds * Each target's sync_stat[] entry is initialized to SX_UNSET, and its 6181da177e4SLinus Torvalds * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET 6191da177e4SLinus Torvalds * means that the parameters are undetermined as yet, and that we 6201da177e4SLinus Torvalds * need to send an SDTR message to this device after selection is 6211da177e4SLinus Torvalds * complete: We set SS_FIRST to tell the interrupt routine to do so. 6221da177e4SLinus Torvalds * If we've been asked not to try synchronous transfers on this 6231da177e4SLinus Torvalds * target (and _all_ luns within it), we'll still send the SDTR message 6241da177e4SLinus Torvalds * later, but at that time we'll negotiate for async by specifying a 6251da177e4SLinus Torvalds * sync fifo depth of 0. 6261da177e4SLinus Torvalds */ 6271da177e4SLinus Torvalds if (hostdata->sync_stat[cmd->device->id] == SS_UNSET) 6281da177e4SLinus Torvalds hostdata->sync_stat[cmd->device->id] = SS_FIRST; 6291da177e4SLinus Torvalds hostdata->state = S_SELECTING; 6301da177e4SLinus Torvalds write_wd33c93_count(regs, 0); /* guarantee a DATA_PHASE interrupt */ 6311da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_SEL_ATN); 6321da177e4SLinus Torvalds } else { 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds /* 6351da177e4SLinus Torvalds * Do a 'Select-With-ATN-Xfer' command. This will end with 6361da177e4SLinus Torvalds * one of the following interrupts: 6371da177e4SLinus Torvalds * CSR_RESEL_AM: failure - can try again later. 6381da177e4SLinus Torvalds * CSR_TIMEOUT: failure - give up. 6391da177e4SLinus Torvalds * anything else: success - proceed. 6401da177e4SLinus Torvalds */ 6411da177e4SLinus Torvalds 6421da177e4SLinus Torvalds hostdata->connected = cmd; 6431da177e4SLinus Torvalds write_wd33c93(regs, WD_COMMAND_PHASE, 0); 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds /* copy command_descriptor_block into WD chip 6461da177e4SLinus Torvalds * (take advantage of auto-incrementing) 6471da177e4SLinus Torvalds */ 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds write_wd33c93_cdb(regs, cmd->cmd_len, cmd->cmnd); 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds /* The wd33c93 only knows about Group 0, 1, and 5 commands when 6521da177e4SLinus Torvalds * it's doing a 'select-and-transfer'. To be safe, we write the 6531da177e4SLinus Torvalds * size of the CDB into the OWN_ID register for every case. This 6541da177e4SLinus Torvalds * way there won't be problems with vendor-unique, audio, etc. 6551da177e4SLinus Torvalds */ 6561da177e4SLinus Torvalds 6571da177e4SLinus Torvalds write_wd33c93(regs, WD_OWN_ID, cmd->cmd_len); 6581da177e4SLinus Torvalds 6591da177e4SLinus Torvalds /* When doing a non-disconnect command with DMA, we can save 6601da177e4SLinus Torvalds * ourselves a DATA phase interrupt later by setting everything 6611da177e4SLinus Torvalds * up ahead of time. 6621da177e4SLinus Torvalds */ 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds if ((cmd->SCp.phase == 0) && (hostdata->no_dma == 0)) { 6651da177e4SLinus Torvalds if (hostdata->dma_setup(cmd, 6661da177e4SLinus Torvalds (cmd->sc_data_direction == DMA_TO_DEVICE) ? 6671da177e4SLinus Torvalds DATA_OUT_DIR : DATA_IN_DIR)) 6681da177e4SLinus Torvalds write_wd33c93_count(regs, 0); /* guarantee a DATA_PHASE interrupt */ 6691da177e4SLinus Torvalds else { 6701da177e4SLinus Torvalds write_wd33c93_count(regs, 6711da177e4SLinus Torvalds cmd->SCp.this_residual); 6721da177e4SLinus Torvalds write_wd33c93(regs, WD_CONTROL, 673a5d8421bSpeter fuerst CTRL_IDI | CTRL_EDI | hostdata->dma_mode); 6741da177e4SLinus Torvalds hostdata->dma = D_DMA_RUNNING; 6751da177e4SLinus Torvalds } 6761da177e4SLinus Torvalds } else 6771da177e4SLinus Torvalds write_wd33c93_count(regs, 0); /* guarantee a DATA_PHASE interrupt */ 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds hostdata->state = S_RUNNING_LEVEL2; 6801da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER); 6811da177e4SLinus Torvalds } 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds /* 6841da177e4SLinus Torvalds * Since the SCSI bus can handle only 1 connection at a time, 6851da177e4SLinus Torvalds * we get out of here now. If the selection fails, or when 6861da177e4SLinus Torvalds * the command disconnects, we'll come back to this routine 6871da177e4SLinus Torvalds * to search the input_Q again... 6881da177e4SLinus Torvalds */ 6891da177e4SLinus Torvalds 6901da177e4SLinus Torvalds DB(DB_EXECUTE, 6915cd049a5SChristoph Hellwig printk("%s)EX-2 ", (cmd->SCp.phase) ? "d:" : "")) 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds static void 6951da177e4SLinus Torvalds transfer_pio(const wd33c93_regs regs, uchar * buf, int cnt, 6961da177e4SLinus Torvalds int data_in_dir, struct WD33C93_hostdata *hostdata) 6971da177e4SLinus Torvalds { 6981da177e4SLinus Torvalds uchar asr; 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds DB(DB_TRANSFER, 7011da177e4SLinus Torvalds printk("(%p,%d,%s:", buf, cnt, data_in_dir ? "in" : "out")) 7021da177e4SLinus Torvalds 7031da177e4SLinus Torvalds write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); 7041da177e4SLinus Torvalds write_wd33c93_count(regs, cnt); 7051da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO); 7061da177e4SLinus Torvalds if (data_in_dir) { 7071da177e4SLinus Torvalds do { 7081da177e4SLinus Torvalds asr = read_aux_stat(regs); 7091da177e4SLinus Torvalds if (asr & ASR_DBR) 7101da177e4SLinus Torvalds *buf++ = read_wd33c93(regs, WD_DATA); 7111da177e4SLinus Torvalds } while (!(asr & ASR_INT)); 7121da177e4SLinus Torvalds } else { 7131da177e4SLinus Torvalds do { 7141da177e4SLinus Torvalds asr = read_aux_stat(regs); 7151da177e4SLinus Torvalds if (asr & ASR_DBR) 7161da177e4SLinus Torvalds write_wd33c93(regs, WD_DATA, *buf++); 7171da177e4SLinus Torvalds } while (!(asr & ASR_INT)); 7181da177e4SLinus Torvalds } 7191da177e4SLinus Torvalds 7201da177e4SLinus Torvalds /* Note: we are returning with the interrupt UN-cleared. 7211da177e4SLinus Torvalds * Since (presumably) an entire I/O operation has 7221da177e4SLinus Torvalds * completed, the bus phase is probably different, and 7231da177e4SLinus Torvalds * the interrupt routine will discover this when it 7241da177e4SLinus Torvalds * responds to the uncleared int. 7251da177e4SLinus Torvalds */ 7261da177e4SLinus Torvalds 7271da177e4SLinus Torvalds } 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds static void 7301da177e4SLinus Torvalds transfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd, 7311da177e4SLinus Torvalds int data_in_dir) 7321da177e4SLinus Torvalds { 7331da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata; 7341da177e4SLinus Torvalds unsigned long length; 7351da177e4SLinus Torvalds 7361da177e4SLinus Torvalds hostdata = (struct WD33C93_hostdata *) cmd->device->host->hostdata; 7371da177e4SLinus Torvalds 7381da177e4SLinus Torvalds /* Normally, you'd expect 'this_residual' to be non-zero here. 7391da177e4SLinus Torvalds * In a series of scatter-gather transfers, however, this 7401da177e4SLinus Torvalds * routine will usually be called with 'this_residual' equal 7411da177e4SLinus Torvalds * to 0 and 'buffers_residual' non-zero. This means that a 7421da177e4SLinus Torvalds * previous transfer completed, clearing 'this_residual', and 7431da177e4SLinus Torvalds * now we need to setup the next scatter-gather buffer as the 7441da177e4SLinus Torvalds * source or destination for THIS transfer. 7451da177e4SLinus Torvalds */ 7461da177e4SLinus Torvalds if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) { 7471da177e4SLinus Torvalds ++cmd->SCp.buffer; 7481da177e4SLinus Torvalds --cmd->SCp.buffers_residual; 7491da177e4SLinus Torvalds cmd->SCp.this_residual = cmd->SCp.buffer->length; 75045711f1aSJens Axboe cmd->SCp.ptr = sg_virt(cmd->SCp.buffer); 7511da177e4SLinus Torvalds } 752a5d8421bSpeter fuerst if (!cmd->SCp.this_residual) /* avoid bogus setups */ 753a5d8421bSpeter fuerst return; 7541da177e4SLinus Torvalds 7551da177e4SLinus Torvalds write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, 7561da177e4SLinus Torvalds hostdata->sync_xfer[cmd->device->id]); 7571da177e4SLinus Torvalds 7581da177e4SLinus Torvalds /* 'hostdata->no_dma' is TRUE if we don't even want to try DMA. 7591da177e4SLinus Torvalds * Update 'this_residual' and 'ptr' after 'transfer_pio()' returns. 7601da177e4SLinus Torvalds */ 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds if (hostdata->no_dma || hostdata->dma_setup(cmd, data_in_dir)) { 7631da177e4SLinus Torvalds #ifdef PROC_STATISTICS 7641da177e4SLinus Torvalds hostdata->pio_cnt++; 7651da177e4SLinus Torvalds #endif 7661da177e4SLinus Torvalds transfer_pio(regs, (uchar *) cmd->SCp.ptr, 7671da177e4SLinus Torvalds cmd->SCp.this_residual, data_in_dir, hostdata); 7681da177e4SLinus Torvalds length = cmd->SCp.this_residual; 7691da177e4SLinus Torvalds cmd->SCp.this_residual = read_wd33c93_count(regs); 7701da177e4SLinus Torvalds cmd->SCp.ptr += (length - cmd->SCp.this_residual); 7711da177e4SLinus Torvalds } 7721da177e4SLinus Torvalds 7731da177e4SLinus Torvalds /* We are able to do DMA (in fact, the Amiga hardware is 7741da177e4SLinus Torvalds * already going!), so start up the wd33c93 in DMA mode. 7751da177e4SLinus Torvalds * We set 'hostdata->dma' = D_DMA_RUNNING so that when the 7761da177e4SLinus Torvalds * transfer completes and causes an interrupt, we're 7771da177e4SLinus Torvalds * reminded to tell the Amiga to shut down its end. We'll 7781da177e4SLinus Torvalds * postpone the updating of 'this_residual' and 'ptr' 7791da177e4SLinus Torvalds * until then. 7801da177e4SLinus Torvalds */ 7811da177e4SLinus Torvalds 7821da177e4SLinus Torvalds else { 7831da177e4SLinus Torvalds #ifdef PROC_STATISTICS 7841da177e4SLinus Torvalds hostdata->dma_cnt++; 7851da177e4SLinus Torvalds #endif 786a5d8421bSpeter fuerst write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | hostdata->dma_mode); 7871da177e4SLinus Torvalds write_wd33c93_count(regs, cmd->SCp.this_residual); 7881da177e4SLinus Torvalds 7891da177e4SLinus Torvalds if ((hostdata->level2 >= L2_DATA) || 7901da177e4SLinus Torvalds (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) { 7911da177e4SLinus Torvalds write_wd33c93(regs, WD_COMMAND_PHASE, 0x45); 7921da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER); 7931da177e4SLinus Torvalds hostdata->state = S_RUNNING_LEVEL2; 7941da177e4SLinus Torvalds } else 7951da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO); 7961da177e4SLinus Torvalds 7971da177e4SLinus Torvalds hostdata->dma = D_DMA_RUNNING; 7981da177e4SLinus Torvalds } 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds void 8021da177e4SLinus Torvalds wd33c93_intr(struct Scsi_Host *instance) 8031da177e4SLinus Torvalds { 8041da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata = 8051da177e4SLinus Torvalds (struct WD33C93_hostdata *) instance->hostdata; 8061da177e4SLinus Torvalds const wd33c93_regs regs = hostdata->regs; 8071da177e4SLinus Torvalds struct scsi_cmnd *patch, *cmd; 8081da177e4SLinus Torvalds uchar asr, sr, phs, id, lun, *ucp, msg; 8091da177e4SLinus Torvalds unsigned long length, flags; 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds asr = read_aux_stat(regs); 8121da177e4SLinus Torvalds if (!(asr & ASR_INT) || (asr & ASR_BSY)) 8131da177e4SLinus Torvalds return; 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds spin_lock_irqsave(&hostdata->lock, flags); 8161da177e4SLinus Torvalds 8171da177e4SLinus Torvalds #ifdef PROC_STATISTICS 8181da177e4SLinus Torvalds hostdata->int_cnt++; 8191da177e4SLinus Torvalds #endif 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hostdata->connected; /* assume we're connected */ 8221da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); /* clear the interrupt */ 8231da177e4SLinus Torvalds phs = read_wd33c93(regs, WD_COMMAND_PHASE); 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds DB(DB_INTR, printk("{%02x:%02x-", asr, sr)) 8261da177e4SLinus Torvalds 8271da177e4SLinus Torvalds /* After starting a DMA transfer, the next interrupt 8281da177e4SLinus Torvalds * is guaranteed to be in response to completion of 8291da177e4SLinus Torvalds * the transfer. Since the Amiga DMA hardware runs in 8301da177e4SLinus Torvalds * in an open-ended fashion, it needs to be told when 8311da177e4SLinus Torvalds * to stop; do that here if D_DMA_RUNNING is true. 8321da177e4SLinus Torvalds * Also, we have to update 'this_residual' and 'ptr' 8331da177e4SLinus Torvalds * based on the contents of the TRANSFER_COUNT register, 8341da177e4SLinus Torvalds * in case the device decided to do an intermediate 8351da177e4SLinus Torvalds * disconnect (a device may do this if it has to do a 8361da177e4SLinus Torvalds * seek, or just to be nice and let other devices have 8371da177e4SLinus Torvalds * some bus time during long transfers). After doing 8381da177e4SLinus Torvalds * whatever is needed, we go on and service the WD3393 8391da177e4SLinus Torvalds * interrupt normally. 8401da177e4SLinus Torvalds */ 8411da177e4SLinus Torvalds if (hostdata->dma == D_DMA_RUNNING) { 8421da177e4SLinus Torvalds DB(DB_TRANSFER, 8431da177e4SLinus Torvalds printk("[%p/%d:", cmd->SCp.ptr, cmd->SCp.this_residual)) 8441da177e4SLinus Torvalds hostdata->dma_stop(cmd->device->host, cmd, 1); 8451da177e4SLinus Torvalds hostdata->dma = D_DMA_OFF; 8461da177e4SLinus Torvalds length = cmd->SCp.this_residual; 8471da177e4SLinus Torvalds cmd->SCp.this_residual = read_wd33c93_count(regs); 8481da177e4SLinus Torvalds cmd->SCp.ptr += (length - cmd->SCp.this_residual); 8491da177e4SLinus Torvalds DB(DB_TRANSFER, 8501da177e4SLinus Torvalds printk("%p/%d]", cmd->SCp.ptr, cmd->SCp.this_residual)) 8511da177e4SLinus Torvalds } 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds /* Respond to the specific WD3393 interrupt - there are quite a few! */ 8541da177e4SLinus Torvalds switch (sr) { 8551da177e4SLinus Torvalds case CSR_TIMEOUT: 8561da177e4SLinus Torvalds DB(DB_INTR, printk("TIMEOUT")) 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds if (hostdata->state == S_RUNNING_LEVEL2) 8591da177e4SLinus Torvalds hostdata->connected = NULL; 8601da177e4SLinus Torvalds else { 8611da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hostdata->selecting; /* get a valid cmd */ 8621da177e4SLinus Torvalds hostdata->selecting = NULL; 8631da177e4SLinus Torvalds } 8641da177e4SLinus Torvalds 8651da177e4SLinus Torvalds cmd->result = DID_NO_CONNECT << 16; 8669cb78c16SHannes Reinecke hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); 8671da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 8681da177e4SLinus Torvalds cmd->scsi_done(cmd); 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds /* From esp.c: 8711da177e4SLinus Torvalds * There is a window of time within the scsi_done() path 8721da177e4SLinus Torvalds * of execution where interrupts are turned back on full 8731da177e4SLinus Torvalds * blast and left that way. During that time we could 8741da177e4SLinus Torvalds * reconnect to a disconnected command, then we'd bomb 8751da177e4SLinus Torvalds * out below. We could also end up executing two commands 8761da177e4SLinus Torvalds * at _once_. ...just so you know why the restore_flags() 8771da177e4SLinus Torvalds * is here... 8781da177e4SLinus Torvalds */ 8791da177e4SLinus Torvalds 8801da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* We are not connected to a target - check to see if there 8831da177e4SLinus Torvalds * are commands waiting to be executed. 8841da177e4SLinus Torvalds */ 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds wd33c93_execute(instance); 8871da177e4SLinus Torvalds break; 8881da177e4SLinus Torvalds 8891da177e4SLinus Torvalds /* Note: this interrupt should not occur in a LEVEL2 command */ 8901da177e4SLinus Torvalds 8911da177e4SLinus Torvalds case CSR_SELECT: 8921da177e4SLinus Torvalds DB(DB_INTR, printk("SELECT")) 8931da177e4SLinus Torvalds hostdata->connected = cmd = 8941da177e4SLinus Torvalds (struct scsi_cmnd *) hostdata->selecting; 8951da177e4SLinus Torvalds hostdata->selecting = NULL; 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds /* construct an IDENTIFY message with correct disconnect bit */ 8981da177e4SLinus Torvalds 8999cb78c16SHannes Reinecke hostdata->outgoing_msg[0] = IDENTIFY(0, cmd->device->lun); 9001da177e4SLinus Torvalds if (cmd->SCp.phase) 9011da177e4SLinus Torvalds hostdata->outgoing_msg[0] |= 0x40; 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) { 9041da177e4SLinus Torvalds 9051da177e4SLinus Torvalds hostdata->sync_stat[cmd->device->id] = SS_WAITING; 9061da177e4SLinus Torvalds 9071da177e4SLinus Torvalds /* Tack on a 2nd message to ask about synchronous transfers. If we've 9081da177e4SLinus Torvalds * been asked to do only asynchronous transfers on this device, we 9091da177e4SLinus Torvalds * request a fifo depth of 0, which is equivalent to async - should 9101da177e4SLinus Torvalds * solve the problems some people have had with GVP's Guru ROM. 9111da177e4SLinus Torvalds */ 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds hostdata->outgoing_msg[1] = EXTENDED_MESSAGE; 9141da177e4SLinus Torvalds hostdata->outgoing_msg[2] = 3; 9151da177e4SLinus Torvalds hostdata->outgoing_msg[3] = EXTENDED_SDTR; 9161da177e4SLinus Torvalds if (hostdata->no_sync & (1 << cmd->device->id)) { 917a5d8421bSpeter fuerst calc_sync_msg(hostdata->default_sx_per, 0, 918a5d8421bSpeter fuerst 0, hostdata->outgoing_msg + 4); 9191da177e4SLinus Torvalds } else { 920a5d8421bSpeter fuerst calc_sync_msg(optimum_sx_per(hostdata), 921a5d8421bSpeter fuerst OPTIMUM_SX_OFF, 922a5d8421bSpeter fuerst hostdata->fast, 923a5d8421bSpeter fuerst hostdata->outgoing_msg + 4); 9241da177e4SLinus Torvalds } 9251da177e4SLinus Torvalds hostdata->outgoing_len = 6; 926a5d8421bSpeter fuerst #ifdef SYNC_DEBUG 927a5d8421bSpeter fuerst ucp = hostdata->outgoing_msg + 1; 928a5d8421bSpeter fuerst printk(" sending SDTR %02x03%02x%02x%02x ", 929a5d8421bSpeter fuerst ucp[0], ucp[2], ucp[3], ucp[4]); 930a5d8421bSpeter fuerst #endif 9311da177e4SLinus Torvalds } else 9321da177e4SLinus Torvalds hostdata->outgoing_len = 1; 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 9351da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 9361da177e4SLinus Torvalds break; 9371da177e4SLinus Torvalds 9381da177e4SLinus Torvalds case CSR_XFER_DONE | PHS_DATA_IN: 9391da177e4SLinus Torvalds case CSR_UNEXP | PHS_DATA_IN: 9401da177e4SLinus Torvalds case CSR_SRV_REQ | PHS_DATA_IN: 9411da177e4SLinus Torvalds DB(DB_INTR, 9421da177e4SLinus Torvalds printk("IN-%d.%d", cmd->SCp.this_residual, 9431da177e4SLinus Torvalds cmd->SCp.buffers_residual)) 9441da177e4SLinus Torvalds transfer_bytes(regs, cmd, DATA_IN_DIR); 9451da177e4SLinus Torvalds if (hostdata->state != S_RUNNING_LEVEL2) 9461da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 9471da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 9481da177e4SLinus Torvalds break; 9491da177e4SLinus Torvalds 9501da177e4SLinus Torvalds case CSR_XFER_DONE | PHS_DATA_OUT: 9511da177e4SLinus Torvalds case CSR_UNEXP | PHS_DATA_OUT: 9521da177e4SLinus Torvalds case CSR_SRV_REQ | PHS_DATA_OUT: 9531da177e4SLinus Torvalds DB(DB_INTR, 9541da177e4SLinus Torvalds printk("OUT-%d.%d", cmd->SCp.this_residual, 9551da177e4SLinus Torvalds cmd->SCp.buffers_residual)) 9561da177e4SLinus Torvalds transfer_bytes(regs, cmd, DATA_OUT_DIR); 9571da177e4SLinus Torvalds if (hostdata->state != S_RUNNING_LEVEL2) 9581da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 9591da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 9601da177e4SLinus Torvalds break; 9611da177e4SLinus Torvalds 9621da177e4SLinus Torvalds /* Note: this interrupt should not occur in a LEVEL2 command */ 9631da177e4SLinus Torvalds 9641da177e4SLinus Torvalds case CSR_XFER_DONE | PHS_COMMAND: 9651da177e4SLinus Torvalds case CSR_UNEXP | PHS_COMMAND: 9661da177e4SLinus Torvalds case CSR_SRV_REQ | PHS_COMMAND: 9675cd049a5SChristoph Hellwig DB(DB_INTR, printk("CMND-%02x", cmd->cmnd[0])) 9681da177e4SLinus Torvalds transfer_pio(regs, cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, 9691da177e4SLinus Torvalds hostdata); 9701da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 9711da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 9721da177e4SLinus Torvalds break; 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds case CSR_XFER_DONE | PHS_STATUS: 9751da177e4SLinus Torvalds case CSR_UNEXP | PHS_STATUS: 9761da177e4SLinus Torvalds case CSR_SRV_REQ | PHS_STATUS: 9771da177e4SLinus Torvalds DB(DB_INTR, printk("STATUS=")) 9781da177e4SLinus Torvalds cmd->SCp.Status = read_1_byte(regs); 9791da177e4SLinus Torvalds DB(DB_INTR, printk("%02x", cmd->SCp.Status)) 9801da177e4SLinus Torvalds if (hostdata->level2 >= L2_BASIC) { 9811da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); /* clear interrupt */ 982882905c7SRoman Zippel udelay(7); 9831da177e4SLinus Torvalds hostdata->state = S_RUNNING_LEVEL2; 9841da177e4SLinus Torvalds write_wd33c93(regs, WD_COMMAND_PHASE, 0x50); 9851da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER); 9861da177e4SLinus Torvalds } else { 9871da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 9881da177e4SLinus Torvalds } 9891da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 9901da177e4SLinus Torvalds break; 9911da177e4SLinus Torvalds 9921da177e4SLinus Torvalds case CSR_XFER_DONE | PHS_MESS_IN: 9931da177e4SLinus Torvalds case CSR_UNEXP | PHS_MESS_IN: 9941da177e4SLinus Torvalds case CSR_SRV_REQ | PHS_MESS_IN: 9951da177e4SLinus Torvalds DB(DB_INTR, printk("MSG_IN=")) 9961da177e4SLinus Torvalds 9971da177e4SLinus Torvalds msg = read_1_byte(regs); 9981da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); /* clear interrupt */ 999882905c7SRoman Zippel udelay(7); 10001da177e4SLinus Torvalds 10011da177e4SLinus Torvalds hostdata->incoming_msg[hostdata->incoming_ptr] = msg; 10021da177e4SLinus Torvalds if (hostdata->incoming_msg[0] == EXTENDED_MESSAGE) 10031da177e4SLinus Torvalds msg = EXTENDED_MESSAGE; 10041da177e4SLinus Torvalds else 10051da177e4SLinus Torvalds hostdata->incoming_ptr = 0; 10061da177e4SLinus Torvalds 10071da177e4SLinus Torvalds cmd->SCp.Message = msg; 10081da177e4SLinus Torvalds switch (msg) { 10091da177e4SLinus Torvalds 10101da177e4SLinus Torvalds case COMMAND_COMPLETE: 10115cd049a5SChristoph Hellwig DB(DB_INTR, printk("CCMP")) 10121da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 10131da177e4SLinus Torvalds hostdata->state = S_PRE_CMP_DISC; 10141da177e4SLinus Torvalds break; 10151da177e4SLinus Torvalds 10161da177e4SLinus Torvalds case SAVE_POINTERS: 10171da177e4SLinus Torvalds DB(DB_INTR, printk("SDP")) 10181da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 10191da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 10201da177e4SLinus Torvalds break; 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds case RESTORE_POINTERS: 10231da177e4SLinus Torvalds DB(DB_INTR, printk("RDP")) 10241da177e4SLinus Torvalds if (hostdata->level2 >= L2_BASIC) { 10251da177e4SLinus Torvalds write_wd33c93(regs, WD_COMMAND_PHASE, 0x45); 10261da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER); 10271da177e4SLinus Torvalds hostdata->state = S_RUNNING_LEVEL2; 10281da177e4SLinus Torvalds } else { 10291da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 10301da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 10311da177e4SLinus Torvalds } 10321da177e4SLinus Torvalds break; 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds case DISCONNECT: 10351da177e4SLinus Torvalds DB(DB_INTR, printk("DIS")) 10361da177e4SLinus Torvalds cmd->device->disconnect = 1; 10371da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 10381da177e4SLinus Torvalds hostdata->state = S_PRE_TMP_DISC; 10391da177e4SLinus Torvalds break; 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds case MESSAGE_REJECT: 10421da177e4SLinus Torvalds DB(DB_INTR, printk("REJ")) 10431da177e4SLinus Torvalds #ifdef SYNC_DEBUG 10441da177e4SLinus Torvalds printk("-REJ-"); 10451da177e4SLinus Torvalds #endif 1046a5d8421bSpeter fuerst if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) { 10471da177e4SLinus Torvalds hostdata->sync_stat[cmd->device->id] = SS_SET; 1048a5d8421bSpeter fuerst /* we want default_sx_per, not DEFAULT_SX_PER */ 1049a5d8421bSpeter fuerst hostdata->sync_xfer[cmd->device->id] = 1050a5d8421bSpeter fuerst calc_sync_xfer(hostdata->default_sx_per 1051a5d8421bSpeter fuerst / 4, 0, 0, hostdata->sx_table); 1052a5d8421bSpeter fuerst } 10531da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 10541da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 10551da177e4SLinus Torvalds break; 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds case EXTENDED_MESSAGE: 10581da177e4SLinus Torvalds DB(DB_INTR, printk("EXT")) 10591da177e4SLinus Torvalds 10601da177e4SLinus Torvalds ucp = hostdata->incoming_msg; 10611da177e4SLinus Torvalds 10621da177e4SLinus Torvalds #ifdef SYNC_DEBUG 10631da177e4SLinus Torvalds printk("%02x", ucp[hostdata->incoming_ptr]); 10641da177e4SLinus Torvalds #endif 10651da177e4SLinus Torvalds /* Is this the last byte of the extended message? */ 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds if ((hostdata->incoming_ptr >= 2) && 10681da177e4SLinus Torvalds (hostdata->incoming_ptr == (ucp[1] + 1))) { 10691da177e4SLinus Torvalds 10701da177e4SLinus Torvalds switch (ucp[2]) { /* what's the EXTENDED code? */ 10711da177e4SLinus Torvalds case EXTENDED_SDTR: 1072a5d8421bSpeter fuerst /* default to default async period */ 1073a5d8421bSpeter fuerst id = calc_sync_xfer(hostdata-> 1074a5d8421bSpeter fuerst default_sx_per / 4, 0, 1075a5d8421bSpeter fuerst 0, hostdata->sx_table); 10761da177e4SLinus Torvalds if (hostdata->sync_stat[cmd->device->id] != 10771da177e4SLinus Torvalds SS_WAITING) { 10781da177e4SLinus Torvalds 10791da177e4SLinus Torvalds /* A device has sent an unsolicited SDTR message; rather than go 10801da177e4SLinus Torvalds * through the effort of decoding it and then figuring out what 10811da177e4SLinus Torvalds * our reply should be, we're just gonna say that we have a 10821da177e4SLinus Torvalds * synchronous fifo depth of 0. This will result in asynchronous 10831da177e4SLinus Torvalds * transfers - not ideal but so much easier. 10841da177e4SLinus Torvalds * Actually, this is OK because it assures us that if we don't 10851da177e4SLinus Torvalds * specifically ask for sync transfers, we won't do any. 10861da177e4SLinus Torvalds */ 10871da177e4SLinus Torvalds 10881da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ 10891da177e4SLinus Torvalds hostdata->outgoing_msg[0] = 10901da177e4SLinus Torvalds EXTENDED_MESSAGE; 10911da177e4SLinus Torvalds hostdata->outgoing_msg[1] = 3; 10921da177e4SLinus Torvalds hostdata->outgoing_msg[2] = 10931da177e4SLinus Torvalds EXTENDED_SDTR; 1094a5d8421bSpeter fuerst calc_sync_msg(hostdata-> 1095a5d8421bSpeter fuerst default_sx_per, 0, 1096a5d8421bSpeter fuerst 0, hostdata->outgoing_msg + 3); 10971da177e4SLinus Torvalds hostdata->outgoing_len = 5; 10981da177e4SLinus Torvalds } else { 1099a5d8421bSpeter fuerst if (ucp[4]) /* well, sync transfer */ 1100a5d8421bSpeter fuerst id = calc_sync_xfer(ucp[3], ucp[4], 1101a5d8421bSpeter fuerst hostdata->fast, 1102a5d8421bSpeter fuerst hostdata->sx_table); 1103a5d8421bSpeter fuerst else if (ucp[3]) /* very unlikely... */ 1104a5d8421bSpeter fuerst id = calc_sync_xfer(ucp[3], ucp[4], 1105a5d8421bSpeter fuerst 0, hostdata->sx_table); 11061da177e4SLinus Torvalds } 1107a5d8421bSpeter fuerst hostdata->sync_xfer[cmd->device->id] = id; 11081da177e4SLinus Torvalds #ifdef SYNC_DEBUG 1109a5d8421bSpeter fuerst printk(" sync_xfer=%02x\n", 11101da177e4SLinus Torvalds hostdata->sync_xfer[cmd->device->id]); 11111da177e4SLinus Torvalds #endif 11121da177e4SLinus Torvalds hostdata->sync_stat[cmd->device->id] = 11131da177e4SLinus Torvalds SS_SET; 11141da177e4SLinus Torvalds write_wd33c93_cmd(regs, 11151da177e4SLinus Torvalds WD_CMD_NEGATE_ACK); 11161da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 11171da177e4SLinus Torvalds break; 11181da177e4SLinus Torvalds case EXTENDED_WDTR: 11191da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ 11201da177e4SLinus Torvalds printk("sending WDTR "); 11211da177e4SLinus Torvalds hostdata->outgoing_msg[0] = 11221da177e4SLinus Torvalds EXTENDED_MESSAGE; 11231da177e4SLinus Torvalds hostdata->outgoing_msg[1] = 2; 11241da177e4SLinus Torvalds hostdata->outgoing_msg[2] = 11251da177e4SLinus Torvalds EXTENDED_WDTR; 11261da177e4SLinus Torvalds hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */ 11271da177e4SLinus Torvalds hostdata->outgoing_len = 4; 11281da177e4SLinus Torvalds write_wd33c93_cmd(regs, 11291da177e4SLinus Torvalds WD_CMD_NEGATE_ACK); 11301da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 11311da177e4SLinus Torvalds break; 11321da177e4SLinus Torvalds default: 11331da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ 11341da177e4SLinus Torvalds printk 11351da177e4SLinus Torvalds ("Rejecting Unknown Extended Message(%02x). ", 11361da177e4SLinus Torvalds ucp[2]); 11371da177e4SLinus Torvalds hostdata->outgoing_msg[0] = 11381da177e4SLinus Torvalds MESSAGE_REJECT; 11391da177e4SLinus Torvalds hostdata->outgoing_len = 1; 11401da177e4SLinus Torvalds write_wd33c93_cmd(regs, 11411da177e4SLinus Torvalds WD_CMD_NEGATE_ACK); 11421da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 11431da177e4SLinus Torvalds break; 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds hostdata->incoming_ptr = 0; 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds 11481da177e4SLinus Torvalds /* We need to read more MESS_IN bytes for the extended message */ 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds else { 11511da177e4SLinus Torvalds hostdata->incoming_ptr++; 11521da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 11531da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 11541da177e4SLinus Torvalds } 11551da177e4SLinus Torvalds break; 11561da177e4SLinus Torvalds 11571da177e4SLinus Torvalds default: 11581da177e4SLinus Torvalds printk("Rejecting Unknown Message(%02x) ", msg); 11591da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_ASSERT_ATN); /* want MESS_OUT */ 11601da177e4SLinus Torvalds hostdata->outgoing_msg[0] = MESSAGE_REJECT; 11611da177e4SLinus Torvalds hostdata->outgoing_len = 1; 11621da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 11631da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 11641da177e4SLinus Torvalds } 11651da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 11661da177e4SLinus Torvalds break; 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds /* Note: this interrupt will occur only after a LEVEL2 command */ 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds case CSR_SEL_XFER_DONE: 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds /* Make sure that reselection is enabled at this point - it may 11731da177e4SLinus Torvalds * have been turned off for the command that just completed. 11741da177e4SLinus Torvalds */ 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER); 11771da177e4SLinus Torvalds if (phs == 0x60) { 11785cd049a5SChristoph Hellwig DB(DB_INTR, printk("SX-DONE")) 11791da177e4SLinus Torvalds cmd->SCp.Message = COMMAND_COMPLETE; 11801da177e4SLinus Torvalds lun = read_wd33c93(regs, WD_TARGET_LUN); 11811da177e4SLinus Torvalds DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun)) 11821da177e4SLinus Torvalds hostdata->connected = NULL; 11839cb78c16SHannes Reinecke hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); 11841da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 11851da177e4SLinus Torvalds if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE) 11861da177e4SLinus Torvalds cmd->SCp.Status = lun; 11871da177e4SLinus Torvalds if (cmd->cmnd[0] == REQUEST_SENSE 11881da177e4SLinus Torvalds && cmd->SCp.Status != GOOD) 11891da177e4SLinus Torvalds cmd->result = 11901da177e4SLinus Torvalds (cmd-> 11911da177e4SLinus Torvalds result & 0x00ffff) | (DID_ERROR << 16); 11921da177e4SLinus Torvalds else 11931da177e4SLinus Torvalds cmd->result = 11941da177e4SLinus Torvalds cmd->SCp.Status | (cmd->SCp.Message << 8); 11951da177e4SLinus Torvalds cmd->scsi_done(cmd); 11961da177e4SLinus Torvalds 11971da177e4SLinus Torvalds /* We are no longer connected to a target - check to see if 11981da177e4SLinus Torvalds * there are commands waiting to be executed. 11991da177e4SLinus Torvalds */ 12001da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 12011da177e4SLinus Torvalds wd33c93_execute(instance); 12021da177e4SLinus Torvalds } else { 12031da177e4SLinus Torvalds printk 12045cd049a5SChristoph Hellwig ("%02x:%02x:%02x: Unknown SEL_XFER_DONE phase!!---", 12055cd049a5SChristoph Hellwig asr, sr, phs); 12061da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 12071da177e4SLinus Torvalds } 12081da177e4SLinus Torvalds break; 12091da177e4SLinus Torvalds 12101da177e4SLinus Torvalds /* Note: this interrupt will occur only after a LEVEL2 command */ 12111da177e4SLinus Torvalds 12121da177e4SLinus Torvalds case CSR_SDP: 12131da177e4SLinus Torvalds DB(DB_INTR, printk("SDP")) 12141da177e4SLinus Torvalds hostdata->state = S_RUNNING_LEVEL2; 12151da177e4SLinus Torvalds write_wd33c93(regs, WD_COMMAND_PHASE, 0x41); 12161da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER); 12171da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 12181da177e4SLinus Torvalds break; 12191da177e4SLinus Torvalds 12201da177e4SLinus Torvalds case CSR_XFER_DONE | PHS_MESS_OUT: 12211da177e4SLinus Torvalds case CSR_UNEXP | PHS_MESS_OUT: 12221da177e4SLinus Torvalds case CSR_SRV_REQ | PHS_MESS_OUT: 12231da177e4SLinus Torvalds DB(DB_INTR, printk("MSG_OUT=")) 12241da177e4SLinus Torvalds 12251da177e4SLinus Torvalds /* To get here, we've probably requested MESSAGE_OUT and have 12261da177e4SLinus Torvalds * already put the correct bytes in outgoing_msg[] and filled 12271da177e4SLinus Torvalds * in outgoing_len. We simply send them out to the SCSI bus. 12281da177e4SLinus Torvalds * Sometimes we get MESSAGE_OUT phase when we're not expecting 12291da177e4SLinus Torvalds * it - like when our SDTR message is rejected by a target. Some 12301da177e4SLinus Torvalds * targets send the REJECT before receiving all of the extended 12311da177e4SLinus Torvalds * message, and then seem to go back to MESSAGE_OUT for a byte 12321da177e4SLinus Torvalds * or two. Not sure why, or if I'm doing something wrong to 12331da177e4SLinus Torvalds * cause this to happen. Regardless, it seems that sending 12341da177e4SLinus Torvalds * NOP messages in these situations results in no harm and 12351da177e4SLinus Torvalds * makes everyone happy. 12361da177e4SLinus Torvalds */ 12371da177e4SLinus Torvalds if (hostdata->outgoing_len == 0) { 12381da177e4SLinus Torvalds hostdata->outgoing_len = 1; 12391da177e4SLinus Torvalds hostdata->outgoing_msg[0] = NOP; 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds transfer_pio(regs, hostdata->outgoing_msg, 12421da177e4SLinus Torvalds hostdata->outgoing_len, DATA_OUT_DIR, hostdata); 12431da177e4SLinus Torvalds DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0])) 12441da177e4SLinus Torvalds hostdata->outgoing_len = 0; 12451da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 12461da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 12471da177e4SLinus Torvalds break; 12481da177e4SLinus Torvalds 12491da177e4SLinus Torvalds case CSR_UNEXP_DISC: 12501da177e4SLinus Torvalds 12511da177e4SLinus Torvalds /* I think I've seen this after a request-sense that was in response 12521da177e4SLinus Torvalds * to an error condition, but not sure. We certainly need to do 12531da177e4SLinus Torvalds * something when we get this interrupt - the question is 'what?'. 12541da177e4SLinus Torvalds * Let's think positively, and assume some command has finished 12551da177e4SLinus Torvalds * in a legal manner (like a command that provokes a request-sense), 12561da177e4SLinus Torvalds * so we treat it as a normal command-complete-disconnect. 12571da177e4SLinus Torvalds */ 12581da177e4SLinus Torvalds 12591da177e4SLinus Torvalds /* Make sure that reselection is enabled at this point - it may 12601da177e4SLinus Torvalds * have been turned off for the command that just completed. 12611da177e4SLinus Torvalds */ 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER); 12641da177e4SLinus Torvalds if (cmd == NULL) { 12651da177e4SLinus Torvalds printk(" - Already disconnected! "); 12661da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 12671da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 12681da177e4SLinus Torvalds return; 12691da177e4SLinus Torvalds } 12705cd049a5SChristoph Hellwig DB(DB_INTR, printk("UNEXP_DISC")) 12711da177e4SLinus Torvalds hostdata->connected = NULL; 12729cb78c16SHannes Reinecke hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); 12731da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 12741da177e4SLinus Torvalds if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD) 12751da177e4SLinus Torvalds cmd->result = 12761da177e4SLinus Torvalds (cmd->result & 0x00ffff) | (DID_ERROR << 16); 12771da177e4SLinus Torvalds else 12781da177e4SLinus Torvalds cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 12791da177e4SLinus Torvalds cmd->scsi_done(cmd); 12801da177e4SLinus Torvalds 12811da177e4SLinus Torvalds /* We are no longer connected to a target - check to see if 12821da177e4SLinus Torvalds * there are commands waiting to be executed. 12831da177e4SLinus Torvalds */ 12841da177e4SLinus Torvalds /* look above for comments on scsi_done() */ 12851da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 12861da177e4SLinus Torvalds wd33c93_execute(instance); 12871da177e4SLinus Torvalds break; 12881da177e4SLinus Torvalds 12891da177e4SLinus Torvalds case CSR_DISC: 12901da177e4SLinus Torvalds 12911da177e4SLinus Torvalds /* Make sure that reselection is enabled at this point - it may 12921da177e4SLinus Torvalds * have been turned off for the command that just completed. 12931da177e4SLinus Torvalds */ 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER); 12965cd049a5SChristoph Hellwig DB(DB_INTR, printk("DISC")) 12971da177e4SLinus Torvalds if (cmd == NULL) { 12981da177e4SLinus Torvalds printk(" - Already disconnected! "); 12991da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds switch (hostdata->state) { 13021da177e4SLinus Torvalds case S_PRE_CMP_DISC: 13031da177e4SLinus Torvalds hostdata->connected = NULL; 13049cb78c16SHannes Reinecke hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); 13051da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 13061da177e4SLinus Torvalds DB(DB_INTR, printk(":%d", cmd->SCp.Status)) 13071da177e4SLinus Torvalds if (cmd->cmnd[0] == REQUEST_SENSE 13081da177e4SLinus Torvalds && cmd->SCp.Status != GOOD) 13091da177e4SLinus Torvalds cmd->result = 13101da177e4SLinus Torvalds (cmd-> 13111da177e4SLinus Torvalds result & 0x00ffff) | (DID_ERROR << 16); 13121da177e4SLinus Torvalds else 13131da177e4SLinus Torvalds cmd->result = 13141da177e4SLinus Torvalds cmd->SCp.Status | (cmd->SCp.Message << 8); 13151da177e4SLinus Torvalds cmd->scsi_done(cmd); 13161da177e4SLinus Torvalds break; 13171da177e4SLinus Torvalds case S_PRE_TMP_DISC: 13181da177e4SLinus Torvalds case S_RUNNING_LEVEL2: 13191da177e4SLinus Torvalds cmd->host_scribble = (uchar *) hostdata->disconnected_Q; 13201da177e4SLinus Torvalds hostdata->disconnected_Q = cmd; 13211da177e4SLinus Torvalds hostdata->connected = NULL; 13221da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 13231da177e4SLinus Torvalds 13241da177e4SLinus Torvalds #ifdef PROC_STATISTICS 13251da177e4SLinus Torvalds hostdata->disc_done_cnt[cmd->device->id]++; 13261da177e4SLinus Torvalds #endif 13271da177e4SLinus Torvalds 13281da177e4SLinus Torvalds break; 13291da177e4SLinus Torvalds default: 13301da177e4SLinus Torvalds printk("*** Unexpected DISCONNECT interrupt! ***"); 13311da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 13321da177e4SLinus Torvalds } 13331da177e4SLinus Torvalds 13341da177e4SLinus Torvalds /* We are no longer connected to a target - check to see if 13351da177e4SLinus Torvalds * there are commands waiting to be executed. 13361da177e4SLinus Torvalds */ 13371da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 13381da177e4SLinus Torvalds wd33c93_execute(instance); 13391da177e4SLinus Torvalds break; 13401da177e4SLinus Torvalds 13411da177e4SLinus Torvalds case CSR_RESEL_AM: 13421da177e4SLinus Torvalds case CSR_RESEL: 13431da177e4SLinus Torvalds DB(DB_INTR, printk("RESEL%s", sr == CSR_RESEL_AM ? "_AM" : "")) 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds /* Old chips (pre -A ???) don't have advanced features and will 13461da177e4SLinus Torvalds * generate CSR_RESEL. In that case we have to extract the LUN the 13471da177e4SLinus Torvalds * hard way (see below). 13481da177e4SLinus Torvalds * First we have to make sure this reselection didn't 13491da177e4SLinus Torvalds * happen during Arbitration/Selection of some other device. 13501da177e4SLinus Torvalds * If yes, put losing command back on top of input_Q. 13511da177e4SLinus Torvalds */ 13521da177e4SLinus Torvalds if (hostdata->level2 <= L2_NONE) { 13531da177e4SLinus Torvalds 13541da177e4SLinus Torvalds if (hostdata->selecting) { 13551da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hostdata->selecting; 13561da177e4SLinus Torvalds hostdata->selecting = NULL; 13579cb78c16SHannes Reinecke hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); 13581da177e4SLinus Torvalds cmd->host_scribble = 13591da177e4SLinus Torvalds (uchar *) hostdata->input_Q; 13601da177e4SLinus Torvalds hostdata->input_Q = cmd; 13611da177e4SLinus Torvalds } 13621da177e4SLinus Torvalds } 13631da177e4SLinus Torvalds 13641da177e4SLinus Torvalds else { 13651da177e4SLinus Torvalds 13661da177e4SLinus Torvalds if (cmd) { 13671da177e4SLinus Torvalds if (phs == 0x00) { 13681da177e4SLinus Torvalds hostdata->busy[cmd->device->id] &= 13699cb78c16SHannes Reinecke ~(1 << (cmd->device->lun & 0xff)); 13701da177e4SLinus Torvalds cmd->host_scribble = 13711da177e4SLinus Torvalds (uchar *) hostdata->input_Q; 13721da177e4SLinus Torvalds hostdata->input_Q = cmd; 13731da177e4SLinus Torvalds } else { 13741da177e4SLinus Torvalds printk 13751da177e4SLinus Torvalds ("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---", 13761da177e4SLinus Torvalds asr, sr, phs); 13771da177e4SLinus Torvalds while (1) 13781da177e4SLinus Torvalds printk("\r"); 13791da177e4SLinus Torvalds } 13801da177e4SLinus Torvalds } 13811da177e4SLinus Torvalds 13821da177e4SLinus Torvalds } 13831da177e4SLinus Torvalds 13841da177e4SLinus Torvalds /* OK - find out which device reselected us. */ 13851da177e4SLinus Torvalds 13861da177e4SLinus Torvalds id = read_wd33c93(regs, WD_SOURCE_ID); 13871da177e4SLinus Torvalds id &= SRCID_MASK; 13881da177e4SLinus Torvalds 13891da177e4SLinus Torvalds /* and extract the lun from the ID message. (Note that we don't 13901da177e4SLinus Torvalds * bother to check for a valid message here - I guess this is 13911da177e4SLinus Torvalds * not the right way to go, but...) 13921da177e4SLinus Torvalds */ 13931da177e4SLinus Torvalds 13941da177e4SLinus Torvalds if (sr == CSR_RESEL_AM) { 13951da177e4SLinus Torvalds lun = read_wd33c93(regs, WD_DATA); 13961da177e4SLinus Torvalds if (hostdata->level2 < L2_RESELECT) 13971da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); 13981da177e4SLinus Torvalds lun &= 7; 13991da177e4SLinus Torvalds } else { 14001da177e4SLinus Torvalds /* Old chip; wait for msgin phase to pick up the LUN. */ 14011da177e4SLinus Torvalds for (lun = 255; lun; lun--) { 14021da177e4SLinus Torvalds if ((asr = read_aux_stat(regs)) & ASR_INT) 14031da177e4SLinus Torvalds break; 14041da177e4SLinus Torvalds udelay(10); 14051da177e4SLinus Torvalds } 14061da177e4SLinus Torvalds if (!(asr & ASR_INT)) { 14071da177e4SLinus Torvalds printk 14081da177e4SLinus Torvalds ("wd33c93: Reselected without IDENTIFY\n"); 14091da177e4SLinus Torvalds lun = 0; 14101da177e4SLinus Torvalds } else { 14111da177e4SLinus Torvalds /* Verify this is a change to MSG_IN and read the message */ 14121da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); 1413882905c7SRoman Zippel udelay(7); 14141da177e4SLinus Torvalds if (sr == (CSR_ABORT | PHS_MESS_IN) || 14151da177e4SLinus Torvalds sr == (CSR_UNEXP | PHS_MESS_IN) || 14161da177e4SLinus Torvalds sr == (CSR_SRV_REQ | PHS_MESS_IN)) { 14171da177e4SLinus Torvalds /* Got MSG_IN, grab target LUN */ 14181da177e4SLinus Torvalds lun = read_1_byte(regs); 14191da177e4SLinus Torvalds /* Now we expect a 'paused with ACK asserted' int.. */ 14201da177e4SLinus Torvalds asr = read_aux_stat(regs); 14211da177e4SLinus Torvalds if (!(asr & ASR_INT)) { 14221da177e4SLinus Torvalds udelay(10); 14231da177e4SLinus Torvalds asr = read_aux_stat(regs); 14241da177e4SLinus Torvalds if (!(asr & ASR_INT)) 14251da177e4SLinus Torvalds printk 14261da177e4SLinus Torvalds ("wd33c93: No int after LUN on RESEL (%02x)\n", 14271da177e4SLinus Torvalds asr); 14281da177e4SLinus Torvalds } 14291da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); 1430882905c7SRoman Zippel udelay(7); 14311da177e4SLinus Torvalds if (sr != CSR_MSGIN) 14321da177e4SLinus Torvalds printk 14331da177e4SLinus Torvalds ("wd33c93: Not paused with ACK on RESEL (%02x)\n", 14341da177e4SLinus Torvalds sr); 14351da177e4SLinus Torvalds lun &= 7; 14361da177e4SLinus Torvalds write_wd33c93_cmd(regs, 14371da177e4SLinus Torvalds WD_CMD_NEGATE_ACK); 14381da177e4SLinus Torvalds } else { 14391da177e4SLinus Torvalds printk 14401da177e4SLinus Torvalds ("wd33c93: Not MSG_IN on reselect (%02x)\n", 14411da177e4SLinus Torvalds sr); 14421da177e4SLinus Torvalds lun = 0; 14431da177e4SLinus Torvalds } 14441da177e4SLinus Torvalds } 14451da177e4SLinus Torvalds } 14461da177e4SLinus Torvalds 14471da177e4SLinus Torvalds /* Now we look for the command that's reconnecting. */ 14481da177e4SLinus Torvalds 14491da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hostdata->disconnected_Q; 14501da177e4SLinus Torvalds patch = NULL; 14511da177e4SLinus Torvalds while (cmd) { 14529cb78c16SHannes Reinecke if (id == cmd->device->id && lun == (u8)cmd->device->lun) 14531da177e4SLinus Torvalds break; 14541da177e4SLinus Torvalds patch = cmd; 14551da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) cmd->host_scribble; 14561da177e4SLinus Torvalds } 14571da177e4SLinus Torvalds 14581da177e4SLinus Torvalds /* Hmm. Couldn't find a valid command.... What to do? */ 14591da177e4SLinus Torvalds 14601da177e4SLinus Torvalds if (!cmd) { 14611da177e4SLinus Torvalds printk 14621da177e4SLinus Torvalds ("---TROUBLE: target %d.%d not in disconnect queue---", 14639cb78c16SHannes Reinecke id, (u8)lun); 14641da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 14651da177e4SLinus Torvalds return; 14661da177e4SLinus Torvalds } 14671da177e4SLinus Torvalds 14681da177e4SLinus Torvalds /* Ok, found the command - now start it up again. */ 14691da177e4SLinus Torvalds 14701da177e4SLinus Torvalds if (patch) 14711da177e4SLinus Torvalds patch->host_scribble = cmd->host_scribble; 14721da177e4SLinus Torvalds else 14731da177e4SLinus Torvalds hostdata->disconnected_Q = 14741da177e4SLinus Torvalds (struct scsi_cmnd *) cmd->host_scribble; 14751da177e4SLinus Torvalds hostdata->connected = cmd; 14761da177e4SLinus Torvalds 14771da177e4SLinus Torvalds /* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]' 14781da177e4SLinus Torvalds * because these things are preserved over a disconnect. 14791da177e4SLinus Torvalds * But we DO need to fix the DPD bit so it's correct for this command. 14801da177e4SLinus Torvalds */ 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds if (cmd->sc_data_direction == DMA_TO_DEVICE) 14831da177e4SLinus Torvalds write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id); 14841da177e4SLinus Torvalds else 14851da177e4SLinus Torvalds write_wd33c93(regs, WD_DESTINATION_ID, 14861da177e4SLinus Torvalds cmd->device->id | DSTID_DPD); 14871da177e4SLinus Torvalds if (hostdata->level2 >= L2_RESELECT) { 14881da177e4SLinus Torvalds write_wd33c93_count(regs, 0); /* we want a DATA_PHASE interrupt */ 14891da177e4SLinus Torvalds write_wd33c93(regs, WD_COMMAND_PHASE, 0x45); 14901da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER); 14911da177e4SLinus Torvalds hostdata->state = S_RUNNING_LEVEL2; 14921da177e4SLinus Torvalds } else 14931da177e4SLinus Torvalds hostdata->state = S_CONNECTED; 14941da177e4SLinus Torvalds 14951da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 14961da177e4SLinus Torvalds break; 14971da177e4SLinus Torvalds 14981da177e4SLinus Torvalds default: 14991da177e4SLinus Torvalds printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs); 15001da177e4SLinus Torvalds spin_unlock_irqrestore(&hostdata->lock, flags); 15011da177e4SLinus Torvalds } 15021da177e4SLinus Torvalds 15031da177e4SLinus Torvalds DB(DB_INTR, printk("} ")) 15041da177e4SLinus Torvalds 15051da177e4SLinus Torvalds } 15061da177e4SLinus Torvalds 15071da177e4SLinus Torvalds static void 15081da177e4SLinus Torvalds reset_wd33c93(struct Scsi_Host *instance) 15091da177e4SLinus Torvalds { 15101da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata = 15111da177e4SLinus Torvalds (struct WD33C93_hostdata *) instance->hostdata; 15121da177e4SLinus Torvalds const wd33c93_regs regs = hostdata->regs; 15131da177e4SLinus Torvalds uchar sr; 15141da177e4SLinus Torvalds 15151da177e4SLinus Torvalds #ifdef CONFIG_SGI_IP22 15161da177e4SLinus Torvalds { 15171da177e4SLinus Torvalds int busycount = 0; 15181da177e4SLinus Torvalds extern void sgiwd93_reset(unsigned long); 15191da177e4SLinus Torvalds /* wait 'til the chip gets some time for us */ 15201da177e4SLinus Torvalds while ((read_aux_stat(regs) & ASR_BSY) && busycount++ < 100) 15211da177e4SLinus Torvalds udelay (10); 15221da177e4SLinus Torvalds /* 15231da177e4SLinus Torvalds * there are scsi devices out there, which manage to lock up 15241da177e4SLinus Torvalds * the wd33c93 in a busy condition. In this state it won't 15251da177e4SLinus Torvalds * accept the reset command. The only way to solve this is to 15261da177e4SLinus Torvalds * give the chip a hardware reset (if possible). The code below 15271da177e4SLinus Torvalds * does this for the SGI Indy, where this is possible 15281da177e4SLinus Torvalds */ 15291da177e4SLinus Torvalds /* still busy ? */ 15301da177e4SLinus Torvalds if (read_aux_stat(regs) & ASR_BSY) 15311da177e4SLinus Torvalds sgiwd93_reset(instance->base); /* yeah, give it the hard one */ 15321da177e4SLinus Torvalds } 15331da177e4SLinus Torvalds #endif 15341da177e4SLinus Torvalds 15351da177e4SLinus Torvalds write_wd33c93(regs, WD_OWN_ID, OWNID_EAF | OWNID_RAF | 15361da177e4SLinus Torvalds instance->this_id | hostdata->clock_freq); 15371da177e4SLinus Torvalds write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); 15381da177e4SLinus Torvalds write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, 15391da177e4SLinus Torvalds calc_sync_xfer(hostdata->default_sx_per / 4, 1540a5d8421bSpeter fuerst DEFAULT_SX_OFF, 0, hostdata->sx_table)); 15411da177e4SLinus Torvalds write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET); 15421da177e4SLinus Torvalds 15431da177e4SLinus Torvalds 15441da177e4SLinus Torvalds #ifdef CONFIG_MVME147_SCSI 15451da177e4SLinus Torvalds udelay(25); /* The old wd33c93 on MVME147 needs this, at least */ 15461da177e4SLinus Torvalds #endif 15471da177e4SLinus Torvalds 15481da177e4SLinus Torvalds while (!(read_aux_stat(regs) & ASR_INT)) 15491da177e4SLinus Torvalds ; 15501da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); 15511da177e4SLinus Torvalds 15521da177e4SLinus Torvalds hostdata->microcode = read_wd33c93(regs, WD_CDB_1); 15531da177e4SLinus Torvalds if (sr == 0x00) 15541da177e4SLinus Torvalds hostdata->chip = C_WD33C93; 15551da177e4SLinus Torvalds else if (sr == 0x01) { 15561da177e4SLinus Torvalds write_wd33c93(regs, WD_QUEUE_TAG, 0xa5); /* any random number */ 15571da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_QUEUE_TAG); 15581da177e4SLinus Torvalds if (sr == 0xa5) { 15591da177e4SLinus Torvalds hostdata->chip = C_WD33C93B; 15601da177e4SLinus Torvalds write_wd33c93(regs, WD_QUEUE_TAG, 0); 15611da177e4SLinus Torvalds } else 15621da177e4SLinus Torvalds hostdata->chip = C_WD33C93A; 15631da177e4SLinus Torvalds } else 15641da177e4SLinus Torvalds hostdata->chip = C_UNKNOWN_CHIP; 15651da177e4SLinus Torvalds 1566a5d8421bSpeter fuerst if (hostdata->chip != C_WD33C93B) /* Fast SCSI unavailable */ 1567a5d8421bSpeter fuerst hostdata->fast = 0; 1568a5d8421bSpeter fuerst 15691da177e4SLinus Torvalds write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE); 15701da177e4SLinus Torvalds write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); 15711da177e4SLinus Torvalds } 15721da177e4SLinus Torvalds 15731da177e4SLinus Torvalds int 15741da177e4SLinus Torvalds wd33c93_host_reset(struct scsi_cmnd * SCpnt) 15751da177e4SLinus Torvalds { 15761da177e4SLinus Torvalds struct Scsi_Host *instance; 15771da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata; 15781da177e4SLinus Torvalds int i; 15791da177e4SLinus Torvalds 15801da177e4SLinus Torvalds instance = SCpnt->device->host; 1581*ec05e238SHannes Reinecke spin_lock_irq(instance->host_lock); 15821da177e4SLinus Torvalds hostdata = (struct WD33C93_hostdata *) instance->hostdata; 15831da177e4SLinus Torvalds 15841da177e4SLinus Torvalds printk("scsi%d: reset. ", instance->host_no); 15851da177e4SLinus Torvalds disable_irq(instance->irq); 15861da177e4SLinus Torvalds 15871da177e4SLinus Torvalds hostdata->dma_stop(instance, NULL, 0); 15881da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 15891da177e4SLinus Torvalds hostdata->busy[i] = 0; 15901da177e4SLinus Torvalds hostdata->sync_xfer[i] = 1591a5d8421bSpeter fuerst calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, 1592a5d8421bSpeter fuerst 0, hostdata->sx_table); 15931da177e4SLinus Torvalds hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ 15941da177e4SLinus Torvalds } 15951da177e4SLinus Torvalds hostdata->input_Q = NULL; 15961da177e4SLinus Torvalds hostdata->selecting = NULL; 15971da177e4SLinus Torvalds hostdata->connected = NULL; 15981da177e4SLinus Torvalds hostdata->disconnected_Q = NULL; 15991da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 16001da177e4SLinus Torvalds hostdata->dma = D_DMA_OFF; 16011da177e4SLinus Torvalds hostdata->incoming_ptr = 0; 16021da177e4SLinus Torvalds hostdata->outgoing_len = 0; 16031da177e4SLinus Torvalds 16041da177e4SLinus Torvalds reset_wd33c93(instance); 16051da177e4SLinus Torvalds SCpnt->result = DID_RESET << 16; 16061da177e4SLinus Torvalds enable_irq(instance->irq); 1607*ec05e238SHannes Reinecke spin_unlock_irq(instance->host_lock); 16081da177e4SLinus Torvalds return SUCCESS; 16091da177e4SLinus Torvalds } 16101da177e4SLinus Torvalds 16111da177e4SLinus Torvalds int 16121da177e4SLinus Torvalds wd33c93_abort(struct scsi_cmnd * cmd) 16131da177e4SLinus Torvalds { 16141da177e4SLinus Torvalds struct Scsi_Host *instance; 16151da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata; 16161da177e4SLinus Torvalds wd33c93_regs regs; 16171da177e4SLinus Torvalds struct scsi_cmnd *tmp, *prev; 16181da177e4SLinus Torvalds 16191da177e4SLinus Torvalds disable_irq(cmd->device->host->irq); 16201da177e4SLinus Torvalds 16211da177e4SLinus Torvalds instance = cmd->device->host; 16221da177e4SLinus Torvalds hostdata = (struct WD33C93_hostdata *) instance->hostdata; 16231da177e4SLinus Torvalds regs = hostdata->regs; 16241da177e4SLinus Torvalds 16251da177e4SLinus Torvalds /* 16261da177e4SLinus Torvalds * Case 1 : If the command hasn't been issued yet, we simply remove it 16271da177e4SLinus Torvalds * from the input_Q. 16281da177e4SLinus Torvalds */ 16291da177e4SLinus Torvalds 16301da177e4SLinus Torvalds tmp = (struct scsi_cmnd *) hostdata->input_Q; 1631a5d361fcSAl Viro prev = NULL; 16321da177e4SLinus Torvalds while (tmp) { 16331da177e4SLinus Torvalds if (tmp == cmd) { 16341da177e4SLinus Torvalds if (prev) 16351da177e4SLinus Torvalds prev->host_scribble = cmd->host_scribble; 16361da177e4SLinus Torvalds else 16371da177e4SLinus Torvalds hostdata->input_Q = 16381da177e4SLinus Torvalds (struct scsi_cmnd *) cmd->host_scribble; 16391da177e4SLinus Torvalds cmd->host_scribble = NULL; 16401da177e4SLinus Torvalds cmd->result = DID_ABORT << 16; 16411da177e4SLinus Torvalds printk 16425cd049a5SChristoph Hellwig ("scsi%d: Abort - removing command from input_Q. ", 16435cd049a5SChristoph Hellwig instance->host_no); 16441da177e4SLinus Torvalds enable_irq(cmd->device->host->irq); 16451da177e4SLinus Torvalds cmd->scsi_done(cmd); 16461da177e4SLinus Torvalds return SUCCESS; 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds prev = tmp; 16491da177e4SLinus Torvalds tmp = (struct scsi_cmnd *) tmp->host_scribble; 16501da177e4SLinus Torvalds } 16511da177e4SLinus Torvalds 16521da177e4SLinus Torvalds /* 16531da177e4SLinus Torvalds * Case 2 : If the command is connected, we're going to fail the abort 16541da177e4SLinus Torvalds * and let the high level SCSI driver retry at a later time or 16551da177e4SLinus Torvalds * issue a reset. 16561da177e4SLinus Torvalds * 16571da177e4SLinus Torvalds * Timeouts, and therefore aborted commands, will be highly unlikely 16581da177e4SLinus Torvalds * and handling them cleanly in this situation would make the common 16591da177e4SLinus Torvalds * case of noresets less efficient, and would pollute our code. So, 16601da177e4SLinus Torvalds * we fail. 16611da177e4SLinus Torvalds */ 16621da177e4SLinus Torvalds 16631da177e4SLinus Torvalds if (hostdata->connected == cmd) { 16641da177e4SLinus Torvalds uchar sr, asr; 16651da177e4SLinus Torvalds unsigned long timeout; 16661da177e4SLinus Torvalds 16675cd049a5SChristoph Hellwig printk("scsi%d: Aborting connected command - ", 16685cd049a5SChristoph Hellwig instance->host_no); 16691da177e4SLinus Torvalds 16701da177e4SLinus Torvalds printk("stopping DMA - "); 16711da177e4SLinus Torvalds if (hostdata->dma == D_DMA_RUNNING) { 16721da177e4SLinus Torvalds hostdata->dma_stop(instance, cmd, 0); 16731da177e4SLinus Torvalds hostdata->dma = D_DMA_OFF; 16741da177e4SLinus Torvalds } 16751da177e4SLinus Torvalds 16761da177e4SLinus Torvalds printk("sending wd33c93 ABORT command - "); 16771da177e4SLinus Torvalds write_wd33c93(regs, WD_CONTROL, 16781da177e4SLinus Torvalds CTRL_IDI | CTRL_EDI | CTRL_POLLED); 16791da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_ABORT); 16801da177e4SLinus Torvalds 16811da177e4SLinus Torvalds /* Now we have to attempt to flush out the FIFO... */ 16821da177e4SLinus Torvalds 16831da177e4SLinus Torvalds printk("flushing fifo - "); 16841da177e4SLinus Torvalds timeout = 1000000; 16851da177e4SLinus Torvalds do { 16861da177e4SLinus Torvalds asr = read_aux_stat(regs); 16871da177e4SLinus Torvalds if (asr & ASR_DBR) 16881da177e4SLinus Torvalds read_wd33c93(regs, WD_DATA); 16891da177e4SLinus Torvalds } while (!(asr & ASR_INT) && timeout-- > 0); 16901da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); 16911da177e4SLinus Torvalds printk 16921da177e4SLinus Torvalds ("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ", 16931da177e4SLinus Torvalds asr, sr, read_wd33c93_count(regs), timeout); 16941da177e4SLinus Torvalds 16951da177e4SLinus Torvalds /* 16961da177e4SLinus Torvalds * Abort command processed. 16971da177e4SLinus Torvalds * Still connected. 16981da177e4SLinus Torvalds * We must disconnect. 16991da177e4SLinus Torvalds */ 17001da177e4SLinus Torvalds 17011da177e4SLinus Torvalds printk("sending wd33c93 DISCONNECT command - "); 17021da177e4SLinus Torvalds write_wd33c93_cmd(regs, WD_CMD_DISCONNECT); 17031da177e4SLinus Torvalds 17041da177e4SLinus Torvalds timeout = 1000000; 17051da177e4SLinus Torvalds asr = read_aux_stat(regs); 17061da177e4SLinus Torvalds while ((asr & ASR_CIP) && timeout-- > 0) 17071da177e4SLinus Torvalds asr = read_aux_stat(regs); 17081da177e4SLinus Torvalds sr = read_wd33c93(regs, WD_SCSI_STATUS); 17091da177e4SLinus Torvalds printk("asr=%02x, sr=%02x.", asr, sr); 17101da177e4SLinus Torvalds 17119cb78c16SHannes Reinecke hostdata->busy[cmd->device->id] &= ~(1 << (cmd->device->lun & 0xff)); 17121da177e4SLinus Torvalds hostdata->connected = NULL; 17131da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 17141da177e4SLinus Torvalds cmd->result = DID_ABORT << 16; 17151da177e4SLinus Torvalds 17161da177e4SLinus Torvalds /* sti();*/ 17171da177e4SLinus Torvalds wd33c93_execute(instance); 17181da177e4SLinus Torvalds 17191da177e4SLinus Torvalds enable_irq(cmd->device->host->irq); 17201da177e4SLinus Torvalds cmd->scsi_done(cmd); 17211da177e4SLinus Torvalds return SUCCESS; 17221da177e4SLinus Torvalds } 17231da177e4SLinus Torvalds 17241da177e4SLinus Torvalds /* 17251da177e4SLinus Torvalds * Case 3: If the command is currently disconnected from the bus, 17261da177e4SLinus Torvalds * we're not going to expend much effort here: Let's just return 17271da177e4SLinus Torvalds * an ABORT_SNOOZE and hope for the best... 17281da177e4SLinus Torvalds */ 17291da177e4SLinus Torvalds 17301da177e4SLinus Torvalds tmp = (struct scsi_cmnd *) hostdata->disconnected_Q; 17311da177e4SLinus Torvalds while (tmp) { 17321da177e4SLinus Torvalds if (tmp == cmd) { 17331da177e4SLinus Torvalds printk 17345cd049a5SChristoph Hellwig ("scsi%d: Abort - command found on disconnected_Q - ", 17355cd049a5SChristoph Hellwig instance->host_no); 17361da177e4SLinus Torvalds printk("Abort SNOOZE. "); 17371da177e4SLinus Torvalds enable_irq(cmd->device->host->irq); 17381da177e4SLinus Torvalds return FAILED; 17391da177e4SLinus Torvalds } 17401da177e4SLinus Torvalds tmp = (struct scsi_cmnd *) tmp->host_scribble; 17411da177e4SLinus Torvalds } 17421da177e4SLinus Torvalds 17431da177e4SLinus Torvalds /* 17441da177e4SLinus Torvalds * Case 4 : If we reached this point, the command was not found in any of 17451da177e4SLinus Torvalds * the queues. 17461da177e4SLinus Torvalds * 17471da177e4SLinus Torvalds * We probably reached this point because of an unlikely race condition 17481da177e4SLinus Torvalds * between the command completing successfully and the abortion code, 17491da177e4SLinus Torvalds * so we won't panic, but we will notify the user in case something really 17501da177e4SLinus Torvalds * broke. 17511da177e4SLinus Torvalds */ 17521da177e4SLinus Torvalds 17531da177e4SLinus Torvalds /* sti();*/ 17541da177e4SLinus Torvalds wd33c93_execute(instance); 17551da177e4SLinus Torvalds 17561da177e4SLinus Torvalds enable_irq(cmd->device->host->irq); 17571da177e4SLinus Torvalds printk("scsi%d: warning : SCSI command probably completed successfully" 17581da177e4SLinus Torvalds " before abortion. ", instance->host_no); 17591da177e4SLinus Torvalds return FAILED; 17601da177e4SLinus Torvalds } 17611da177e4SLinus Torvalds 17621da177e4SLinus Torvalds #define MAX_WD33C93_HOSTS 4 17636391a113STobias Klauser #define MAX_SETUP_ARGS ARRAY_SIZE(setup_args) 17641da177e4SLinus Torvalds #define SETUP_BUFFER_SIZE 200 17651da177e4SLinus Torvalds static char setup_buffer[SETUP_BUFFER_SIZE]; 17661da177e4SLinus Torvalds static char setup_used[MAX_SETUP_ARGS]; 17671da177e4SLinus Torvalds static int done_setup = 0; 17681da177e4SLinus Torvalds 1769078dda95SAdrian Bunk static int 17701da177e4SLinus Torvalds wd33c93_setup(char *str) 17711da177e4SLinus Torvalds { 17721da177e4SLinus Torvalds int i; 17731da177e4SLinus Torvalds char *p1, *p2; 17741da177e4SLinus Torvalds 17751da177e4SLinus Torvalds /* The kernel does some processing of the command-line before calling 17761da177e4SLinus Torvalds * this function: If it begins with any decimal or hex number arguments, 17771da177e4SLinus Torvalds * ints[0] = how many numbers found and ints[1] through [n] are the values 17781da177e4SLinus Torvalds * themselves. str points to where the non-numeric arguments (if any) 17791da177e4SLinus Torvalds * start: We do our own parsing of those. We construct synthetic 'nosync' 17801da177e4SLinus Torvalds * keywords out of numeric args (to maintain compatibility with older 17811da177e4SLinus Torvalds * versions) and then add the rest of the arguments. 17821da177e4SLinus Torvalds */ 17831da177e4SLinus Torvalds 17841da177e4SLinus Torvalds p1 = setup_buffer; 17851da177e4SLinus Torvalds *p1 = '\0'; 17861da177e4SLinus Torvalds if (str) 17871da177e4SLinus Torvalds strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer)); 17881da177e4SLinus Torvalds setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0'; 17891da177e4SLinus Torvalds p1 = setup_buffer; 17901da177e4SLinus Torvalds i = 0; 17911da177e4SLinus Torvalds while (*p1 && (i < MAX_SETUP_ARGS)) { 17921da177e4SLinus Torvalds p2 = strchr(p1, ','); 17931da177e4SLinus Torvalds if (p2) { 17941da177e4SLinus Torvalds *p2 = '\0'; 17951da177e4SLinus Torvalds if (p1 != p2) 17961da177e4SLinus Torvalds setup_args[i] = p1; 17971da177e4SLinus Torvalds p1 = p2 + 1; 17981da177e4SLinus Torvalds i++; 17991da177e4SLinus Torvalds } else { 18001da177e4SLinus Torvalds setup_args[i] = p1; 18011da177e4SLinus Torvalds break; 18021da177e4SLinus Torvalds } 18031da177e4SLinus Torvalds } 18041da177e4SLinus Torvalds for (i = 0; i < MAX_SETUP_ARGS; i++) 18051da177e4SLinus Torvalds setup_used[i] = 0; 18061da177e4SLinus Torvalds done_setup = 1; 18071da177e4SLinus Torvalds 18081da177e4SLinus Torvalds return 1; 18091da177e4SLinus Torvalds } 18101da177e4SLinus Torvalds __setup("wd33c93=", wd33c93_setup); 18111da177e4SLinus Torvalds 18121da177e4SLinus Torvalds /* check_setup_args() returns index if key found, 0 if not 18131da177e4SLinus Torvalds */ 18141da177e4SLinus Torvalds static int 18151da177e4SLinus Torvalds check_setup_args(char *key, int *flags, int *val, char *buf) 18161da177e4SLinus Torvalds { 18171da177e4SLinus Torvalds int x; 18181da177e4SLinus Torvalds char *cp; 18191da177e4SLinus Torvalds 18201da177e4SLinus Torvalds for (x = 0; x < MAX_SETUP_ARGS; x++) { 18211da177e4SLinus Torvalds if (setup_used[x]) 18221da177e4SLinus Torvalds continue; 18231da177e4SLinus Torvalds if (!strncmp(setup_args[x], key, strlen(key))) 18241da177e4SLinus Torvalds break; 18251da177e4SLinus Torvalds if (!strncmp(setup_args[x], "next", strlen("next"))) 18261da177e4SLinus Torvalds return 0; 18271da177e4SLinus Torvalds } 18281da177e4SLinus Torvalds if (x == MAX_SETUP_ARGS) 18291da177e4SLinus Torvalds return 0; 18301da177e4SLinus Torvalds setup_used[x] = 1; 18311da177e4SLinus Torvalds cp = setup_args[x] + strlen(key); 18321da177e4SLinus Torvalds *val = -1; 18331da177e4SLinus Torvalds if (*cp != ':') 18341da177e4SLinus Torvalds return ++x; 18351da177e4SLinus Torvalds cp++; 18361da177e4SLinus Torvalds if ((*cp >= '0') && (*cp <= '9')) { 18371da177e4SLinus Torvalds *val = simple_strtoul(cp, NULL, 0); 18381da177e4SLinus Torvalds } 18391da177e4SLinus Torvalds return ++x; 18401da177e4SLinus Torvalds } 18411da177e4SLinus Torvalds 1842a5d8421bSpeter fuerst /* 1843a5d8421bSpeter fuerst * Calculate internal data-transfer-clock cycle from input-clock 1844a5d8421bSpeter fuerst * frequency (/MHz) and fill 'sx_table'. 1845a5d8421bSpeter fuerst * 1846a5d8421bSpeter fuerst * The original driver used to rely on a fixed sx_table, containing periods 1847a5d8421bSpeter fuerst * for (only) the lower limits of the respective input-clock-frequency ranges 184825985edcSLucas De Marchi * (8-10/12-15/16-20 MHz). Although it seems, that no problems occurred with 1849a5d8421bSpeter fuerst * this setting so far, it might be desirable to adjust the transfer periods 1850a5d8421bSpeter fuerst * closer to the really attached, possibly 25% higher, input-clock, since 1851a5d8421bSpeter fuerst * - the wd33c93 may really use a significant shorter period, than it has 1852a5d8421bSpeter fuerst * negotiated (eg. thrashing the target, which expects 4/8MHz, with 5/10MHz 1853a5d8421bSpeter fuerst * instead). 1854a5d8421bSpeter fuerst * - the wd33c93 may ask the target for a lower transfer rate, than the target 1855a5d8421bSpeter fuerst * is capable of (eg. negotiating for an assumed minimum of 252ns instead of 1856a5d8421bSpeter fuerst * possible 200ns, which indeed shows up in tests as an approx. 10% lower 1857a5d8421bSpeter fuerst * transfer rate). 1858a5d8421bSpeter fuerst */ 1859a5d8421bSpeter fuerst static inline unsigned int 1860a5d8421bSpeter fuerst round_4(unsigned int x) 1861a5d8421bSpeter fuerst { 1862a5d8421bSpeter fuerst switch (x & 3) { 1863a5d8421bSpeter fuerst case 1: --x; 1864a5d8421bSpeter fuerst break; 1865a5d8421bSpeter fuerst case 2: ++x; 1866a5d8421bSpeter fuerst case 3: ++x; 1867a5d8421bSpeter fuerst } 1868a5d8421bSpeter fuerst return x; 1869a5d8421bSpeter fuerst } 1870a5d8421bSpeter fuerst 1871a5d8421bSpeter fuerst static void 1872a5d8421bSpeter fuerst calc_sx_table(unsigned int mhz, struct sx_period sx_table[9]) 1873a5d8421bSpeter fuerst { 1874a5d8421bSpeter fuerst unsigned int d, i; 1875a5d8421bSpeter fuerst if (mhz < 11) 1876a5d8421bSpeter fuerst d = 2; /* divisor for 8-10 MHz input-clock */ 1877a5d8421bSpeter fuerst else if (mhz < 16) 1878a5d8421bSpeter fuerst d = 3; /* divisor for 12-15 MHz input-clock */ 1879a5d8421bSpeter fuerst else 1880a5d8421bSpeter fuerst d = 4; /* divisor for 16-20 MHz input-clock */ 1881a5d8421bSpeter fuerst 1882a5d8421bSpeter fuerst d = (100000 * d) / 2 / mhz; /* 100 x DTCC / nanosec */ 1883a5d8421bSpeter fuerst 1884a5d8421bSpeter fuerst sx_table[0].period_ns = 1; 1885a5d8421bSpeter fuerst sx_table[0].reg_value = 0x20; 1886a5d8421bSpeter fuerst for (i = 1; i < 8; i++) { 1887a5d8421bSpeter fuerst sx_table[i].period_ns = round_4((i+1)*d / 100); 1888a5d8421bSpeter fuerst sx_table[i].reg_value = (i+1)*0x10; 1889a5d8421bSpeter fuerst } 1890a5d8421bSpeter fuerst sx_table[7].reg_value = 0; 1891a5d8421bSpeter fuerst sx_table[8].period_ns = 0; 1892a5d8421bSpeter fuerst sx_table[8].reg_value = 0; 1893a5d8421bSpeter fuerst } 1894a5d8421bSpeter fuerst 1895a5d8421bSpeter fuerst /* 1896a5d8421bSpeter fuerst * check and, maybe, map an init- or "clock:"- argument. 1897a5d8421bSpeter fuerst */ 1898a5d8421bSpeter fuerst static uchar 1899a5d8421bSpeter fuerst set_clk_freq(int freq, int *mhz) 1900a5d8421bSpeter fuerst { 1901a5d8421bSpeter fuerst int x = freq; 1902a5d8421bSpeter fuerst if (WD33C93_FS_8_10 == freq) 1903a5d8421bSpeter fuerst freq = 8; 1904a5d8421bSpeter fuerst else if (WD33C93_FS_12_15 == freq) 1905a5d8421bSpeter fuerst freq = 12; 1906a5d8421bSpeter fuerst else if (WD33C93_FS_16_20 == freq) 1907a5d8421bSpeter fuerst freq = 16; 1908a5d8421bSpeter fuerst else if (freq > 7 && freq < 11) 1909a5d8421bSpeter fuerst x = WD33C93_FS_8_10; 1910a5d8421bSpeter fuerst else if (freq > 11 && freq < 16) 1911a5d8421bSpeter fuerst x = WD33C93_FS_12_15; 1912a5d8421bSpeter fuerst else if (freq > 15 && freq < 21) 1913a5d8421bSpeter fuerst x = WD33C93_FS_16_20; 1914a5d8421bSpeter fuerst else { 1915a5d8421bSpeter fuerst /* Hmm, wouldn't it be safer to assume highest freq here? */ 1916a5d8421bSpeter fuerst x = WD33C93_FS_8_10; 1917a5d8421bSpeter fuerst freq = 8; 1918a5d8421bSpeter fuerst } 1919a5d8421bSpeter fuerst *mhz = freq; 1920a5d8421bSpeter fuerst return x; 1921a5d8421bSpeter fuerst } 1922a5d8421bSpeter fuerst 1923a5d8421bSpeter fuerst /* 1924a5d8421bSpeter fuerst * to be used with the resync: fast: ... options 1925a5d8421bSpeter fuerst */ 1926a5d8421bSpeter fuerst static inline void set_resync ( struct WD33C93_hostdata *hd, int mask ) 1927a5d8421bSpeter fuerst { 1928a5d8421bSpeter fuerst int i; 1929a5d8421bSpeter fuerst for (i = 0; i < 8; i++) 1930a5d8421bSpeter fuerst if (mask & (1 << i)) 1931a5d8421bSpeter fuerst hd->sync_stat[i] = SS_UNSET; 1932a5d8421bSpeter fuerst } 1933a5d8421bSpeter fuerst 19341da177e4SLinus Torvalds void 19351da177e4SLinus Torvalds wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, 19361da177e4SLinus Torvalds dma_setup_t setup, dma_stop_t stop, int clock_freq) 19371da177e4SLinus Torvalds { 19381da177e4SLinus Torvalds struct WD33C93_hostdata *hostdata; 19391da177e4SLinus Torvalds int i; 19401da177e4SLinus Torvalds int flags; 19411da177e4SLinus Torvalds int val; 19421da177e4SLinus Torvalds char buf[32]; 19431da177e4SLinus Torvalds 19441da177e4SLinus Torvalds if (!done_setup && setup_strings) 19451da177e4SLinus Torvalds wd33c93_setup(setup_strings); 19461da177e4SLinus Torvalds 19471da177e4SLinus Torvalds hostdata = (struct WD33C93_hostdata *) instance->hostdata; 19481da177e4SLinus Torvalds 19491da177e4SLinus Torvalds hostdata->regs = regs; 1950a5d8421bSpeter fuerst hostdata->clock_freq = set_clk_freq(clock_freq, &i); 1951a5d8421bSpeter fuerst calc_sx_table(i, hostdata->sx_table); 19521da177e4SLinus Torvalds hostdata->dma_setup = setup; 19531da177e4SLinus Torvalds hostdata->dma_stop = stop; 19541da177e4SLinus Torvalds hostdata->dma_bounce_buffer = NULL; 19551da177e4SLinus Torvalds hostdata->dma_bounce_len = 0; 19561da177e4SLinus Torvalds for (i = 0; i < 8; i++) { 19571da177e4SLinus Torvalds hostdata->busy[i] = 0; 19581da177e4SLinus Torvalds hostdata->sync_xfer[i] = 1959a5d8421bSpeter fuerst calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, 1960a5d8421bSpeter fuerst 0, hostdata->sx_table); 19611da177e4SLinus Torvalds hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ 19621da177e4SLinus Torvalds #ifdef PROC_STATISTICS 19631da177e4SLinus Torvalds hostdata->cmd_cnt[i] = 0; 19641da177e4SLinus Torvalds hostdata->disc_allowed_cnt[i] = 0; 19651da177e4SLinus Torvalds hostdata->disc_done_cnt[i] = 0; 19661da177e4SLinus Torvalds #endif 19671da177e4SLinus Torvalds } 19681da177e4SLinus Torvalds hostdata->input_Q = NULL; 19691da177e4SLinus Torvalds hostdata->selecting = NULL; 19701da177e4SLinus Torvalds hostdata->connected = NULL; 19711da177e4SLinus Torvalds hostdata->disconnected_Q = NULL; 19721da177e4SLinus Torvalds hostdata->state = S_UNCONNECTED; 19731da177e4SLinus Torvalds hostdata->dma = D_DMA_OFF; 19741da177e4SLinus Torvalds hostdata->level2 = L2_BASIC; 19751da177e4SLinus Torvalds hostdata->disconnect = DIS_ADAPTIVE; 19761da177e4SLinus Torvalds hostdata->args = DEBUG_DEFAULTS; 19771da177e4SLinus Torvalds hostdata->incoming_ptr = 0; 19781da177e4SLinus Torvalds hostdata->outgoing_len = 0; 19791da177e4SLinus Torvalds hostdata->default_sx_per = DEFAULT_SX_PER; 19801da177e4SLinus Torvalds hostdata->no_dma = 0; /* default is DMA enabled */ 19811da177e4SLinus Torvalds 19821da177e4SLinus Torvalds #ifdef PROC_INTERFACE 19831da177e4SLinus Torvalds hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | 19841da177e4SLinus Torvalds PR_CONNECTED | PR_INPUTQ | PR_DISCQ | PR_STOP; 19851da177e4SLinus Torvalds #ifdef PROC_STATISTICS 19861da177e4SLinus Torvalds hostdata->dma_cnt = 0; 19871da177e4SLinus Torvalds hostdata->pio_cnt = 0; 19881da177e4SLinus Torvalds hostdata->int_cnt = 0; 19891da177e4SLinus Torvalds #endif 19901da177e4SLinus Torvalds #endif 19911da177e4SLinus Torvalds 1992a5d8421bSpeter fuerst if (check_setup_args("clock", &flags, &val, buf)) { 1993a5d8421bSpeter fuerst hostdata->clock_freq = set_clk_freq(val, &val); 1994a5d8421bSpeter fuerst calc_sx_table(val, hostdata->sx_table); 1995a5d8421bSpeter fuerst } 1996a5d8421bSpeter fuerst 19971da177e4SLinus Torvalds if (check_setup_args("nosync", &flags, &val, buf)) 19981da177e4SLinus Torvalds hostdata->no_sync = val; 19991da177e4SLinus Torvalds 20001da177e4SLinus Torvalds if (check_setup_args("nodma", &flags, &val, buf)) 20011da177e4SLinus Torvalds hostdata->no_dma = (val == -1) ? 1 : val; 20021da177e4SLinus Torvalds 20031da177e4SLinus Torvalds if (check_setup_args("period", &flags, &val, buf)) 20041da177e4SLinus Torvalds hostdata->default_sx_per = 2005a5d8421bSpeter fuerst hostdata->sx_table[round_period((unsigned int) val, 2006a5d8421bSpeter fuerst hostdata->sx_table)].period_ns; 20071da177e4SLinus Torvalds 20081da177e4SLinus Torvalds if (check_setup_args("disconnect", &flags, &val, buf)) { 20091da177e4SLinus Torvalds if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) 20101da177e4SLinus Torvalds hostdata->disconnect = val; 20111da177e4SLinus Torvalds else 20121da177e4SLinus Torvalds hostdata->disconnect = DIS_ADAPTIVE; 20131da177e4SLinus Torvalds } 20141da177e4SLinus Torvalds 20151da177e4SLinus Torvalds if (check_setup_args("level2", &flags, &val, buf)) 20161da177e4SLinus Torvalds hostdata->level2 = val; 20171da177e4SLinus Torvalds 20181da177e4SLinus Torvalds if (check_setup_args("debug", &flags, &val, buf)) 20191da177e4SLinus Torvalds hostdata->args = val & DB_MASK; 20201da177e4SLinus Torvalds 2021a5d8421bSpeter fuerst if (check_setup_args("burst", &flags, &val, buf)) 2022a5d8421bSpeter fuerst hostdata->dma_mode = val ? CTRL_BURST:CTRL_DMA; 2023a5d8421bSpeter fuerst 2024a5d8421bSpeter fuerst if (WD33C93_FS_16_20 == hostdata->clock_freq /* divisor 4 */ 2025a5d8421bSpeter fuerst && check_setup_args("fast", &flags, &val, buf)) 2026a5d8421bSpeter fuerst hostdata->fast = !!val; 20271da177e4SLinus Torvalds 20281da177e4SLinus Torvalds if ((i = check_setup_args("next", &flags, &val, buf))) { 20291da177e4SLinus Torvalds while (i) 20301da177e4SLinus Torvalds setup_used[--i] = 1; 20311da177e4SLinus Torvalds } 20321da177e4SLinus Torvalds #ifdef PROC_INTERFACE 20331da177e4SLinus Torvalds if (check_setup_args("proc", &flags, &val, buf)) 20341da177e4SLinus Torvalds hostdata->proc = val; 20351da177e4SLinus Torvalds #endif 20361da177e4SLinus Torvalds 20371da177e4SLinus Torvalds spin_lock_irq(&hostdata->lock); 20381da177e4SLinus Torvalds reset_wd33c93(instance); 20391da177e4SLinus Torvalds spin_unlock_irq(&hostdata->lock); 20401da177e4SLinus Torvalds 20411da177e4SLinus Torvalds printk("wd33c93-%d: chip=%s/%d no_sync=0x%x no_dma=%d", 20421da177e4SLinus Torvalds instance->host_no, 20431da177e4SLinus Torvalds (hostdata->chip == C_WD33C93) ? "WD33c93" : (hostdata->chip == 20441da177e4SLinus Torvalds C_WD33C93A) ? 20451da177e4SLinus Torvalds "WD33c93A" : (hostdata->chip == 20461da177e4SLinus Torvalds C_WD33C93B) ? "WD33c93B" : "unknown", 20471da177e4SLinus Torvalds hostdata->microcode, hostdata->no_sync, hostdata->no_dma); 20481da177e4SLinus Torvalds #ifdef DEBUGGING_ON 20491da177e4SLinus Torvalds printk(" debug_flags=0x%02x\n", hostdata->args); 20501da177e4SLinus Torvalds #else 20511da177e4SLinus Torvalds printk(" debugging=OFF\n"); 20521da177e4SLinus Torvalds #endif 20531da177e4SLinus Torvalds printk(" setup_args="); 20541da177e4SLinus Torvalds for (i = 0; i < MAX_SETUP_ARGS; i++) 20551da177e4SLinus Torvalds printk("%s,", setup_args[i]); 20561da177e4SLinus Torvalds printk("\n"); 2057565502f8SMichal Marek printk(" Version %s - %s\n", WD33C93_VERSION, WD33C93_DATE); 20581da177e4SLinus Torvalds } 20591da177e4SLinus Torvalds 2060408bb25bSAl Viro int wd33c93_write_info(struct Scsi_Host *instance, char *buf, int len) 20611da177e4SLinus Torvalds { 20621da177e4SLinus Torvalds #ifdef PROC_INTERFACE 20631da177e4SLinus Torvalds char *bp; 20641da177e4SLinus Torvalds struct WD33C93_hostdata *hd; 2065a5d8421bSpeter fuerst int x; 20661da177e4SLinus Torvalds 20671da177e4SLinus Torvalds hd = (struct WD33C93_hostdata *) instance->hostdata; 20681da177e4SLinus Torvalds 2069408bb25bSAl Viro /* We accept the following 2070a5d8421bSpeter fuerst * keywords (same format as command-line, but arguments are not optional): 20711da177e4SLinus Torvalds * debug 20721da177e4SLinus Torvalds * disconnect 20731da177e4SLinus Torvalds * period 20741da177e4SLinus Torvalds * resync 20751da177e4SLinus Torvalds * proc 20761da177e4SLinus Torvalds * nodma 2077a5d8421bSpeter fuerst * level2 2078a5d8421bSpeter fuerst * burst 2079a5d8421bSpeter fuerst * fast 2080a5d8421bSpeter fuerst * nosync 20811da177e4SLinus Torvalds */ 20821da177e4SLinus Torvalds 20831da177e4SLinus Torvalds buf[len] = '\0'; 2084a5d8421bSpeter fuerst for (bp = buf; *bp; ) { 2085a5d8421bSpeter fuerst while (',' == *bp || ' ' == *bp) 2086a5d8421bSpeter fuerst ++bp; 20871da177e4SLinus Torvalds if (!strncmp(bp, "debug:", 6)) { 2088a5d8421bSpeter fuerst hd->args = simple_strtoul(bp+6, &bp, 0) & DB_MASK; 20891da177e4SLinus Torvalds } else if (!strncmp(bp, "disconnect:", 11)) { 2090a5d8421bSpeter fuerst x = simple_strtoul(bp+11, &bp, 0); 20911da177e4SLinus Torvalds if (x < DIS_NEVER || x > DIS_ALWAYS) 20921da177e4SLinus Torvalds x = DIS_ADAPTIVE; 20931da177e4SLinus Torvalds hd->disconnect = x; 20941da177e4SLinus Torvalds } else if (!strncmp(bp, "period:", 7)) { 2095a5d8421bSpeter fuerst x = simple_strtoul(bp+7, &bp, 0); 20961da177e4SLinus Torvalds hd->default_sx_per = 2097a5d8421bSpeter fuerst hd->sx_table[round_period((unsigned int) x, 2098a5d8421bSpeter fuerst hd->sx_table)].period_ns; 20991da177e4SLinus Torvalds } else if (!strncmp(bp, "resync:", 7)) { 2100a5d8421bSpeter fuerst set_resync(hd, (int)simple_strtoul(bp+7, &bp, 0)); 21011da177e4SLinus Torvalds } else if (!strncmp(bp, "proc:", 5)) { 2102a5d8421bSpeter fuerst hd->proc = simple_strtoul(bp+5, &bp, 0); 21031da177e4SLinus Torvalds } else if (!strncmp(bp, "nodma:", 6)) { 2104a5d8421bSpeter fuerst hd->no_dma = simple_strtoul(bp+6, &bp, 0); 21051da177e4SLinus Torvalds } else if (!strncmp(bp, "level2:", 7)) { 2106a5d8421bSpeter fuerst hd->level2 = simple_strtoul(bp+7, &bp, 0); 2107a5d8421bSpeter fuerst } else if (!strncmp(bp, "burst:", 6)) { 2108a5d8421bSpeter fuerst hd->dma_mode = 2109a5d8421bSpeter fuerst simple_strtol(bp+6, &bp, 0) ? CTRL_BURST:CTRL_DMA; 2110a5d8421bSpeter fuerst } else if (!strncmp(bp, "fast:", 5)) { 2111a5d8421bSpeter fuerst x = !!simple_strtol(bp+5, &bp, 0); 2112a5d8421bSpeter fuerst if (x != hd->fast) 2113a5d8421bSpeter fuerst set_resync(hd, 0xff); 2114a5d8421bSpeter fuerst hd->fast = x; 2115a5d8421bSpeter fuerst } else if (!strncmp(bp, "nosync:", 7)) { 2116a5d8421bSpeter fuerst x = simple_strtoul(bp+7, &bp, 0); 2117a5d8421bSpeter fuerst set_resync(hd, x ^ hd->no_sync); 2118a5d8421bSpeter fuerst hd->no_sync = x; 2119a5d8421bSpeter fuerst } else { 2120a5d8421bSpeter fuerst break; /* unknown keyword,syntax-error,... */ 2121a5d8421bSpeter fuerst } 21221da177e4SLinus Torvalds } 21231da177e4SLinus Torvalds return len; 2124408bb25bSAl Viro #else 2125408bb25bSAl Viro return 0; 2126408bb25bSAl Viro #endif 21271da177e4SLinus Torvalds } 21281da177e4SLinus Torvalds 2129408bb25bSAl Viro int 2130408bb25bSAl Viro wd33c93_show_info(struct seq_file *m, struct Scsi_Host *instance) 2131408bb25bSAl Viro { 2132408bb25bSAl Viro #ifdef PROC_INTERFACE 2133408bb25bSAl Viro struct WD33C93_hostdata *hd; 2134408bb25bSAl Viro struct scsi_cmnd *cmd; 2135408bb25bSAl Viro int x; 2136408bb25bSAl Viro 2137408bb25bSAl Viro hd = (struct WD33C93_hostdata *) instance->hostdata; 2138408bb25bSAl Viro 21391da177e4SLinus Torvalds spin_lock_irq(&hd->lock); 2140408bb25bSAl Viro if (hd->proc & PR_VERSION) 2141408bb25bSAl Viro seq_printf(m, "\nVersion %s - %s.", 2142565502f8SMichal Marek WD33C93_VERSION, WD33C93_DATE); 2143408bb25bSAl Viro 21441da177e4SLinus Torvalds if (hd->proc & PR_INFO) { 2145408bb25bSAl Viro seq_printf(m, "\nclock_freq=%02x no_sync=%02x no_dma=%d" 2146a5d8421bSpeter fuerst " dma_mode=%02x fast=%d", 2147a5d8421bSpeter fuerst hd->clock_freq, hd->no_sync, hd->no_dma, hd->dma_mode, hd->fast); 214891c40f24SRasmus Villemoes seq_puts(m, "\nsync_xfer[] = "); 2149408bb25bSAl Viro for (x = 0; x < 7; x++) 2150408bb25bSAl Viro seq_printf(m, "\t%02x", hd->sync_xfer[x]); 215191c40f24SRasmus Villemoes seq_puts(m, "\nsync_stat[] = "); 2152408bb25bSAl Viro for (x = 0; x < 7; x++) 2153408bb25bSAl Viro seq_printf(m, "\t%02x", hd->sync_stat[x]); 21541da177e4SLinus Torvalds } 21551da177e4SLinus Torvalds #ifdef PROC_STATISTICS 21561da177e4SLinus Torvalds if (hd->proc & PR_STATISTICS) { 215791c40f24SRasmus Villemoes seq_puts(m, "\ncommands issued: "); 2158408bb25bSAl Viro for (x = 0; x < 7; x++) 2159408bb25bSAl Viro seq_printf(m, "\t%ld", hd->cmd_cnt[x]); 216091c40f24SRasmus Villemoes seq_puts(m, "\ndisconnects allowed:"); 2161408bb25bSAl Viro for (x = 0; x < 7; x++) 2162408bb25bSAl Viro seq_printf(m, "\t%ld", hd->disc_allowed_cnt[x]); 216391c40f24SRasmus Villemoes seq_puts(m, "\ndisconnects done: "); 2164408bb25bSAl Viro for (x = 0; x < 7; x++) 2165408bb25bSAl Viro seq_printf(m, "\t%ld", hd->disc_done_cnt[x]); 2166408bb25bSAl Viro seq_printf(m, 21671da177e4SLinus Torvalds "\ninterrupts: %ld, DATA_PHASE ints: %ld DMA, %ld PIO", 21681da177e4SLinus Torvalds hd->int_cnt, hd->dma_cnt, hd->pio_cnt); 21691da177e4SLinus Torvalds } 21701da177e4SLinus Torvalds #endif 21711da177e4SLinus Torvalds if (hd->proc & PR_CONNECTED) { 217291c40f24SRasmus Villemoes seq_puts(m, "\nconnected: "); 21731da177e4SLinus Torvalds if (hd->connected) { 21741da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hd->connected; 21759cb78c16SHannes Reinecke seq_printf(m, " %d:%llu(%02x)", 21765cd049a5SChristoph Hellwig cmd->device->id, cmd->device->lun, cmd->cmnd[0]); 21771da177e4SLinus Torvalds } 21781da177e4SLinus Torvalds } 21791da177e4SLinus Torvalds if (hd->proc & PR_INPUTQ) { 218091c40f24SRasmus Villemoes seq_puts(m, "\ninput_Q: "); 21811da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hd->input_Q; 21821da177e4SLinus Torvalds while (cmd) { 21839cb78c16SHannes Reinecke seq_printf(m, " %d:%llu(%02x)", 21845cd049a5SChristoph Hellwig cmd->device->id, cmd->device->lun, cmd->cmnd[0]); 21851da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) cmd->host_scribble; 21861da177e4SLinus Torvalds } 21871da177e4SLinus Torvalds } 21881da177e4SLinus Torvalds if (hd->proc & PR_DISCQ) { 218991c40f24SRasmus Villemoes seq_puts(m, "\ndisconnected_Q:"); 21901da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) hd->disconnected_Q; 21911da177e4SLinus Torvalds while (cmd) { 21929cb78c16SHannes Reinecke seq_printf(m, " %d:%llu(%02x)", 21935cd049a5SChristoph Hellwig cmd->device->id, cmd->device->lun, cmd->cmnd[0]); 21941da177e4SLinus Torvalds cmd = (struct scsi_cmnd *) cmd->host_scribble; 21951da177e4SLinus Torvalds } 21961da177e4SLinus Torvalds } 2197f50332ffSRasmus Villemoes seq_putc(m, '\n'); 21981da177e4SLinus Torvalds spin_unlock_irq(&hd->lock); 21991da177e4SLinus Torvalds #endif /* PROC_INTERFACE */ 2200408bb25bSAl Viro return 0; 22011da177e4SLinus Torvalds } 22021da177e4SLinus Torvalds 22031da177e4SLinus Torvalds EXPORT_SYMBOL(wd33c93_host_reset); 22041da177e4SLinus Torvalds EXPORT_SYMBOL(wd33c93_init); 22051da177e4SLinus Torvalds EXPORT_SYMBOL(wd33c93_abort); 22061da177e4SLinus Torvalds EXPORT_SYMBOL(wd33c93_queuecommand); 22071da177e4SLinus Torvalds EXPORT_SYMBOL(wd33c93_intr); 2208408bb25bSAl Viro EXPORT_SYMBOL(wd33c93_show_info); 2209408bb25bSAl Viro EXPORT_SYMBOL(wd33c93_write_info); 2210