2921 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/acorn/scsi/acornscsi.c4 *5 * Acorn SCSI 3 driver6 * By R.M.King.7 *8 * Abandoned using the Select and Transfer command since there were9 * some nasty races between our software and the target devices that10 * were not easy to solve, and the device errata had a lot of entries11 * for this command, some of them quite nasty...12 *13 * Changelog:14 * 26-Sep-1997 RMK Re-jigged to use the queue module.15 * Re-coded state machine to be based on driver16 * state not scsi state. Should be easier to debug.17 * Added acornscsi_release to clean up properly.18 * Updated proc/scsi reporting.19 * 05-Oct-1997 RMK Implemented writing to SCSI devices.20 * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/21 * reconnect race condition causing a warning message.22 * 12-Oct-1997 RMK Added catch for re-entering interrupt routine.23 * 15-Oct-1997 RMK Improved handling of commands.24 * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h.25 * 13-Dec-1998 RMK Better abort code and command handling. Extra state26 * transitions added to allow dodgy devices to work.27 */28#define DEBUG_NO_WRITE 129#define DEBUG_QUEUES 230#define DEBUG_DMA 431#define DEBUG_ABORT 832#define DEBUG_DISCON 1633#define DEBUG_CONNECT 3234#define DEBUG_PHASES 6435#define DEBUG_WRITE 12836#define DEBUG_LINK 25637#define DEBUG_MESSAGES 51238#define DEBUG_RESET 102439#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\40 DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\41 DEBUG_DMA|DEBUG_QUEUES)42 43/* DRIVER CONFIGURATION44 *45 * SCSI-II Tagged queue support.46 *47 * I don't have any SCSI devices that support it, so it is totally untested48 * (except to make sure that it doesn't interfere with any non-tagging49 * devices). It is not fully implemented either - what happens when a50 * tagging device reconnects???51 *52 * You can tell if you have a device that supports tagged queueing my53 * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported54 * as '2 TAG'.55 */56 57/*58 * SCSI-II Synchronous transfer support.59 *60 * Tried and tested...61 *62 * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max)63 * SDTR_PERIOD - period of REQ signal (min=125, max=1020)64 * DEFAULT_PERIOD - default REQ period.65 */66#define SDTR_SIZE 1267#define SDTR_PERIOD 12568#define DEFAULT_PERIOD 50069 70/*71 * Debugging information72 *73 * DEBUG - bit mask from list above74 * DEBUG_TARGET - is defined to the target number if you want to debug75 * a specific target. [only recon/write/dma].76 */77#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE)78/* only allow writing to SCSI device 0 */79#define NO_WRITE 0xFE80/*#define DEBUG_TARGET 2*/81/*82 * Select timeout time (in 10ms units)83 *84 * This is the timeout used between the start of selection and the WD33C9385 * chip deciding that the device isn't responding.86 */87#define TIMEOUT_TIME 1088/*89 * Define this if you want to have verbose explanation of SCSI90 * status/messages.91 */92#undef CONFIG_ACORNSCSI_CONSTANTS93/*94 * Define this if you want to use the on board DMAC [don't remove this option]95 * If not set, then use PIO mode (not currently supported).96 */97#define USE_DMAC98 99/*100 * ====================================================================================101 */102 103#ifdef DEBUG_TARGET104#define DBG(cmd,xxx...) \105 if (cmd->device->id == DEBUG_TARGET) { \106 xxx; \107 }108#else109#define DBG(cmd,xxx...) xxx110#endif111 112#include <linux/module.h>113#include <linux/kernel.h>114#include <linux/string.h>115#include <linux/signal.h>116#include <linux/errno.h>117#include <linux/proc_fs.h>118#include <linux/ioport.h>119#include <linux/blkdev.h>120#include <linux/delay.h>121#include <linux/interrupt.h>122#include <linux/init.h>123#include <linux/bitops.h>124#include <linux/stringify.h>125#include <linux/io.h>126 127#include <asm/ecard.h>128 129#include <scsi/scsi.h>130#include <scsi/scsi_cmnd.h>131#include <scsi/scsi_dbg.h>132#include <scsi/scsi_device.h>133#include <scsi/scsi_eh.h>134#include <scsi/scsi_host.h>135#include <scsi/scsi_tcq.h>136#include <scsi/scsi_transport_spi.h>137#include "acornscsi.h"138#include "msgqueue.h"139#include "arm_scsi.h"140 141#include <scsi/scsicam.h>142 143#define VER_MAJOR 2144#define VER_MINOR 0145#define VER_PATCH 6146 147#ifdef USE_DMAC148/*149 * DMAC setup parameters150 */ 151#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP)152#define INIT_DEVCON1 (DEVCON1_BHLD)153#define DMAC_READ (MODECON_READ)154#define DMAC_WRITE (MODECON_WRITE)155#define INIT_SBICDMA (CTRL_DMABURST)156 157#define scsi_xferred have_data_in158 159/*160 * Size of on-board DMA buffer161 */162#define DMAC_BUFFER_SIZE 65536163#endif164 165#define STATUS_BUFFER_TO_PRINT 24166 167unsigned int sdtr_period = SDTR_PERIOD;168unsigned int sdtr_size = SDTR_SIZE;169 170static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,171 unsigned int result);172static int acornscsi_reconnect_finish(AS_Host *host);173static void acornscsi_dma_cleanup(AS_Host *host);174static void acornscsi_abortcmd(AS_Host *host);175 176/* ====================================================================================177 * Miscellaneous178 */179 180/* Offsets from MEMC base */181#define SBIC_REGIDX 0x2000182#define SBIC_REGVAL 0x2004183#define DMAC_OFFSET 0x3000184 185/* Offsets from FAST IOC base */186#define INT_REG 0x2000187#define PAGE_REG 0x3000188 189static inline void sbic_arm_write(AS_Host *host, unsigned int reg, unsigned int value)190{191 writeb(reg, host->base + SBIC_REGIDX);192 writeb(value, host->base + SBIC_REGVAL);193}194 195static inline int sbic_arm_read(AS_Host *host, unsigned int reg)196{197 if(reg == SBIC_ASR)198 return readl(host->base + SBIC_REGIDX) & 255;199 writeb(reg, host->base + SBIC_REGIDX);200 return readl(host->base + SBIC_REGVAL) & 255;201}202 203#define sbic_arm_writenext(host, val) writeb((val), (host)->base + SBIC_REGVAL)204#define sbic_arm_readnext(host) readb((host)->base + SBIC_REGVAL)205 206#ifdef USE_DMAC207#define dmac_read(host,reg) \208 readb((host)->base + DMAC_OFFSET + ((reg) << 2))209 210#define dmac_write(host,reg,value) \211 ({ writeb((value), (host)->base + DMAC_OFFSET + ((reg) << 2)); })212 213#define dmac_clearintr(host) writeb(0, (host)->fast + INT_REG)214 215static inline unsigned int dmac_address(AS_Host *host)216{217 return dmac_read(host, DMAC_TXADRHI) << 16 |218 dmac_read(host, DMAC_TXADRMD) << 8 |219 dmac_read(host, DMAC_TXADRLO);220}221 222static223void acornscsi_dumpdma(AS_Host *host, char *where)224{225 unsigned int mode, addr, len;226 227 mode = dmac_read(host, DMAC_MODECON);228 addr = dmac_address(host);229 len = dmac_read(host, DMAC_TXCNTHI) << 8 |230 dmac_read(host, DMAC_TXCNTLO);231 232 printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ",233 host->host->host_no, where,234 mode, addr, (len + 1) & 0xffff,235 dmac_read(host, DMAC_MASKREG));236 237 printk("DMA @%06x, ", host->dma.start_addr);238 printk("BH @%p +%04x, ", host->scsi.SCp.ptr,239 host->scsi.SCp.this_residual);240 printk("DT @+%04x ST @+%04x", host->dma.transferred,241 host->scsi.SCp.scsi_xferred);242 printk("\n");243}244#endif245 246static247unsigned long acornscsi_sbic_xfcount(AS_Host *host)248{249 unsigned long length;250 251 length = sbic_arm_read(host, SBIC_TRANSCNTH) << 16;252 length |= sbic_arm_readnext(host) << 8;253 length |= sbic_arm_readnext(host);254 255 return length;256}257 258static int259acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg)260{261 int asr;262 263 do {264 asr = sbic_arm_read(host, SBIC_ASR);265 266 if ((asr & stat_mask) == stat)267 return 0;268 269 udelay(1);270 } while (--timeout);271 272 printk("scsi%d: timeout while %s\n", host->host->host_no, msg);273 274 return -1;275}276 277static278int acornscsi_sbic_issuecmd(AS_Host *host, int command)279{280 if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command"))281 return -1;282 283 sbic_arm_write(host, SBIC_CMND, command);284 285 return 0;286}287 288static void289acornscsi_csdelay(unsigned int cs)290{291 unsigned long target_jiffies, flags;292 293 target_jiffies = jiffies + 1 + cs * HZ / 100;294 295 local_save_flags(flags);296 local_irq_enable();297 298 while (time_before(jiffies, target_jiffies)) barrier();299 300 local_irq_restore(flags);301}302 303static304void acornscsi_resetcard(AS_Host *host)305{306 unsigned int i, timeout;307 308 /* assert reset line */309 host->card.page_reg = 0x80;310 writeb(host->card.page_reg, host->fast + PAGE_REG);311 312 /* wait 3 cs. SCSI standard says 25ms. */313 acornscsi_csdelay(3);314 315 host->card.page_reg = 0;316 writeb(host->card.page_reg, host->fast + PAGE_REG);317 318 /*319 * Should get a reset from the card320 */321 timeout = 1000;322 do {323 if (readb(host->fast + INT_REG) & 8)324 break;325 udelay(1);326 } while (--timeout);327 328 if (timeout == 0)329 printk("scsi%d: timeout while resetting card\n",330 host->host->host_no);331 332 sbic_arm_read(host, SBIC_ASR);333 sbic_arm_read(host, SBIC_SSR);334 335 /* setup sbic - WD33C93A */336 sbic_arm_write(host, SBIC_OWNID, OWNID_EAF | host->host->this_id);337 sbic_arm_write(host, SBIC_CMND, CMND_RESET);338 339 /*340 * Command should cause a reset interrupt341 */342 timeout = 1000;343 do {344 if (readb(host->fast + INT_REG) & 8)345 break;346 udelay(1);347 } while (--timeout);348 349 if (timeout == 0)350 printk("scsi%d: timeout while resetting card\n",351 host->host->host_no);352 353 sbic_arm_read(host, SBIC_ASR);354 if (sbic_arm_read(host, SBIC_SSR) != 0x01)355 printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n",356 host->host->host_no);357 358 sbic_arm_write(host, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);359 sbic_arm_write(host, SBIC_TIMEOUT, TIMEOUT_TIME);360 sbic_arm_write(host, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);361 sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);362 363 host->card.page_reg = 0x40;364 writeb(host->card.page_reg, host->fast + PAGE_REG);365 366 /* setup dmac - uPC71071 */367 dmac_write(host, DMAC_INIT, 0);368#ifdef USE_DMAC369 dmac_write(host, DMAC_INIT, INIT_8BIT);370 dmac_write(host, DMAC_CHANNEL, CHANNEL_0);371 dmac_write(host, DMAC_DEVCON0, INIT_DEVCON0);372 dmac_write(host, DMAC_DEVCON1, INIT_DEVCON1);373#endif374 375 host->SCpnt = NULL;376 host->scsi.phase = PHASE_IDLE;377 host->scsi.disconnectable = 0;378 379 memset(host->busyluns, 0, sizeof(host->busyluns));380 381 for (i = 0; i < 8; i++) {382 host->device[i].sync_state = SYNC_NEGOCIATE;383 host->device[i].disconnect_ok = 1;384 }385 386 /* wait 25 cs. SCSI standard says 250ms. */387 acornscsi_csdelay(25);388}389 390/*=============================================================================================391 * Utility routines (eg. debug)392 */393#ifdef CONFIG_ACORNSCSI_CONSTANTS394static char *acornscsi_interrupttype[] = {395 "rst", "suc", "p/a", "3",396 "term", "5", "6", "7",397 "serv", "9", "a", "b",398 "c", "d", "e", "f"399};400 401static signed char acornscsi_map[] = {402 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,403 -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11,404 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11,405 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,406 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11,407 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,408 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,409 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,410 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11,411 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,412 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,413 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,414 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,415 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,416 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,417 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1418}; 419 420static char *acornscsi_interruptcode[] = {421 /* 0 */422 "reset - normal mode", /* 00 */423 "reset - advanced mode", /* 01 */424 425 /* 2 */426 "sel", /* 11 */427 "sel+xfer", /* 16 */428 "data-out", /* 18 */429 "data-in", /* 19 */430 "cmd", /* 1A */431 "stat", /* 1B */432 "??-out", /* 1C */433 "??-in", /* 1D */434 "msg-out", /* 1E */435 "msg-in", /* 1F */436 437 /* 12 */438 "/ACK asserted", /* 20 */439 "save-data-ptr", /* 21 */440 "{re}sel", /* 22 */441 442 /* 15 */443 "inv cmd", /* 40 */444 "unexpected disconnect", /* 41 */445 "sel timeout", /* 42 */446 "P err", /* 43 */447 "P err+ATN", /* 44 */448 "bad status byte", /* 47 */449 450 /* 21 */451 "resel, no id", /* 80 */452 "resel", /* 81 */453 "discon", /* 85 */454};455 456static457void print_scsi_status(unsigned int ssr)458{459 if (acornscsi_map[ssr] != -1)460 printk("%s:%s",461 acornscsi_interrupttype[(ssr >> 4)],462 acornscsi_interruptcode[acornscsi_map[ssr]]);463 else464 printk("%X:%X", ssr >> 4, ssr & 0x0f); 465} 466#endif467 468static469void print_sbic_status(int asr, int ssr, int cmdphase)470{471#ifdef CONFIG_ACORNSCSI_CONSTANTS472 printk("sbic: %c%c%c%c%c%c ",473 asr & ASR_INT ? 'I' : 'i',474 asr & ASR_LCI ? 'L' : 'l',475 asr & ASR_BSY ? 'B' : 'b',476 asr & ASR_CIP ? 'C' : 'c',477 asr & ASR_PE ? 'P' : 'p',478 asr & ASR_DBR ? 'D' : 'd');479 printk("scsi: ");480 print_scsi_status(ssr);481 printk(" ph %02X\n", cmdphase);482#else483 printk("sbic: %02X scsi: %X:%X ph: %02X\n",484 asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase);485#endif486}487 488static void489acornscsi_dumplogline(AS_Host *host, int target, int line)490{491 unsigned long prev;492 signed int ptr;493 494 ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT;495 if (ptr < 0)496 ptr += STATUS_BUFFER_SIZE;497 498 printk("%c: %3s:", target == 8 ? 'H' : '0' + target,499 line == 0 ? "ph" : line == 1 ? "ssr" : "int");500 501 prev = host->status[target][ptr].when;502 503 for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) {504 unsigned long time_diff;505 506 if (!host->status[target][ptr].when)507 continue;508 509 switch (line) {510 case 0:511 printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ',512 host->status[target][ptr].ph);513 break;514 515 case 1:516 printk(" %02X", host->status[target][ptr].ssr);517 break;518 519 case 2:520 time_diff = host->status[target][ptr].when - prev;521 prev = host->status[target][ptr].when;522 if (time_diff == 0)523 printk("==^");524 else if (time_diff >= 100)525 printk(" ");526 else527 printk(" %02ld", time_diff);528 break;529 }530 }531 532 printk("\n");533}534 535static536void acornscsi_dumplog(AS_Host *host, int target)537{538 do {539 acornscsi_dumplogline(host, target, 0);540 acornscsi_dumplogline(host, target, 1);541 acornscsi_dumplogline(host, target, 2);542 543 if (target == 8)544 break;545 546 target = 8;547 } while (1);548}549 550static551char acornscsi_target(AS_Host *host)552{553 if (host->SCpnt)554 return '0' + host->SCpnt->device->id;555 return 'H';556}557 558/*559 * Prototype: cmdtype_t acornscsi_cmdtype(int command)560 * Purpose : differentiate READ from WRITE from other commands561 * Params : command - command to interpret562 * Returns : CMD_READ - command reads data,563 * CMD_WRITE - command writes data,564 * CMD_MISC - everything else565 */566static inline567cmdtype_t acornscsi_cmdtype(int command)568{569 switch (command) {570 case WRITE_6: case WRITE_10: case WRITE_12:571 return CMD_WRITE;572 case READ_6: case READ_10: case READ_12:573 return CMD_READ;574 default:575 return CMD_MISC;576 }577}578 579/*580 * Prototype: int acornscsi_datadirection(int command)581 * Purpose : differentiate between commands that have a DATA IN phase582 * and a DATA OUT phase583 * Params : command - command to interpret584 * Returns : DATADIR_OUT - data out phase expected585 * DATADIR_IN - data in phase expected586 */587static588datadir_t acornscsi_datadirection(int command)589{590 switch (command) {591 case CHANGE_DEFINITION: case COMPARE: case COPY:592 case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:593 case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:594 case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:595 case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:596 case WRITE_6: case WRITE_10: case WRITE_VERIFY:597 case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:598 case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:599 case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:600 case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea:601 return DATADIR_OUT;602 default:603 return DATADIR_IN;604 }605}606 607/*608 * Purpose : provide values for synchronous transfers with 33C93.609 * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting610 * Modified by Russell King for 8MHz WD33C93A611 */612static struct sync_xfer_tbl {613 unsigned int period_ns;614 unsigned char reg_value;615} sync_xfer_table[] = {616 { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 },617 { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 },618 { 874, 0x70 }, { 999, 0x00 }, { 0, 0 }619};620 621/*622 * Prototype: int acornscsi_getperiod(unsigned char syncxfer)623 * Purpose : period for the synchronous transfer setting624 * Params : syncxfer SYNCXFER register value625 * Returns : period in ns.626 */627static628int acornscsi_getperiod(unsigned char syncxfer)629{630 int i;631 632 syncxfer &= 0xf0;633 if (syncxfer == 0x10)634 syncxfer = 0;635 636 for (i = 1; sync_xfer_table[i].period_ns; i++)637 if (syncxfer == sync_xfer_table[i].reg_value)638 return sync_xfer_table[i].period_ns;639 return 0;640}641 642/*643 * Prototype: int round_period(unsigned int period)644 * Purpose : return index into above table for a required REQ period645 * Params : period - time (ns) for REQ646 * Returns : table index647 * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting648 */649static inline650int round_period(unsigned int period)651{652 int i;653 654 for (i = 1; sync_xfer_table[i].period_ns; i++) {655 if ((period <= sync_xfer_table[i].period_ns) &&656 (period > sync_xfer_table[i - 1].period_ns))657 return i;658 }659 return 7;660}661 662/*663 * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset)664 * Purpose : calculate value for 33c93s SYNC register665 * Params : period - time (ns) for REQ666 * offset - offset in bytes between REQ/ACK667 * Returns : value for SYNC register668 * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting669 */670static671unsigned char __maybe_unused calc_sync_xfer(unsigned int period,672 unsigned int offset)673{674 return sync_xfer_table[round_period(period)].reg_value |675 ((offset < SDTR_SIZE) ? offset : SDTR_SIZE);676}677 678/* ====================================================================================679 * Command functions680 */681/*682 * Function: acornscsi_kick(AS_Host *host)683 * Purpose : kick next command to interface684 * Params : host - host to send command to685 * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING686 * Notes : interrupts are always disabled!687 */688static689intr_ret_t acornscsi_kick(AS_Host *host)690{691 int from_queue = 0;692 struct scsi_cmnd *SCpnt;693 694 /* first check to see if a command is waiting to be executed */695 SCpnt = host->origSCpnt;696 host->origSCpnt = NULL;697 698 /* retrieve next command */699 if (!SCpnt) {700 SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns);701 if (!SCpnt)702 return INTR_IDLE;703 704 from_queue = 1;705 }706 707 if (host->scsi.disconnectable && host->SCpnt) {708 queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);709 host->scsi.disconnectable = 0;710#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))711 DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n",712 host->host->host_no, acornscsi_target(host)));713#endif714 host->SCpnt = NULL;715 }716 717 /*718 * If we have an interrupt pending, then we may have been reselected.719 * In this case, we don't want to write to the registers720 */721 if (!(sbic_arm_read(host, SBIC_ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) {722 sbic_arm_write(host, SBIC_DESTID, SCpnt->device->id);723 sbic_arm_write(host, SBIC_CMND, CMND_SELWITHATN);724 }725 726 /*727 * claim host busy - all of these must happen atomically wrt728 * our interrupt routine. Failure means command loss.729 */730 host->scsi.phase = PHASE_CONNECTING;731 host->SCpnt = SCpnt;732 host->scsi.SCp = *arm_scsi_pointer(SCpnt);733 host->dma.xfer_setup = 0;734 host->dma.xfer_required = 0;735 host->dma.xfer_done = 0;736 737#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT))738 DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n",739 host->host->host_no, '0' + SCpnt->device->id,740 SCpnt->cmnd[0]));741#endif742 743 if (from_queue) {744 set_bit(SCpnt->device->id * 8 +745 (u8)(SCpnt->device->lun & 0x07), host->busyluns);746 747 host->stats.removes += 1;748 749 switch (acornscsi_cmdtype(SCpnt->cmnd[0])) {750 case CMD_WRITE:751 host->stats.writes += 1;752 break;753 case CMD_READ:754 host->stats.reads += 1;755 break;756 case CMD_MISC:757 host->stats.miscs += 1;758 break;759 }760 }761 762 return INTR_PROCESSING;763} 764 765/*766 * Function: void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, unsigned int result)767 * Purpose : complete processing for command768 * Params : host - interface that completed769 * result - driver byte of result770 */771static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp,772 unsigned int result)773{774 struct scsi_cmnd *SCpnt = *SCpntp;775 776 /* clean up */777 sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);778 779 host->stats.fins += 1;780 781 if (SCpnt) {782 *SCpntp = NULL;783 784 acornscsi_dma_cleanup(host);785 786 set_host_byte(SCpnt, result);787 if (result == DID_OK)788 scsi_msg_to_host_byte(SCpnt, host->scsi.SCp.Message);789 set_status_byte(SCpnt, host->scsi.SCp.Status);790 791 /*792 * In theory, this should not happen. In practice, it seems to.793 * Only trigger an error if the device attempts to report all happy794 * but with untransferred buffers... If we don't do something, then795 * data loss will occur. Should we check SCpnt->underflow here?796 * It doesn't appear to be set to something meaningful by the higher797 * levels all the time.798 */799 if (result == DID_OK) {800 int xfer_warn = 0;801 802 if (SCpnt->underflow == 0) {803 if (host->scsi.SCp.ptr &&804 acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC)805 xfer_warn = 1;806 } else {807 if (host->scsi.SCp.scsi_xferred < SCpnt->underflow ||808 host->scsi.SCp.scsi_xferred != host->dma.transferred)809 xfer_warn = 1;810 }811 812 /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6)813 * Targets which break data transfers into multiple814 * connections shall end each successful connection815 * (except possibly the last) with a SAVE DATA816 * POINTER - DISCONNECT message sequence.817 *818 * This makes it difficult to ensure that a transfer has819 * completed. If we reach the end of a transfer during820 * the command, then we can only have finished the transfer.821 * therefore, if we seem to have some data remaining, this822 * is not a problem.823 */824 if (host->dma.xfer_done)825 xfer_warn = 0;826 827 if (xfer_warn) {828 switch (get_status_byte(SCpnt)) {829 case SAM_STAT_CHECK_CONDITION:830 case SAM_STAT_COMMAND_TERMINATED:831 case SAM_STAT_BUSY:832 case SAM_STAT_TASK_SET_FULL:833 case SAM_STAT_RESERVATION_CONFLICT:834 break;835 836 default:837 scmd_printk(KERN_ERR, SCpnt,838 "incomplete data transfer detected: "839 "result=%08X", SCpnt->result);840 scsi_print_command(SCpnt);841 acornscsi_dumpdma(host, "done");842 acornscsi_dumplog(host, SCpnt->device->id);843 set_host_byte(SCpnt, DID_ERROR);844 }845 }846 }847 848 clear_bit(SCpnt->device->id * 8 +849 (u8)(SCpnt->device->lun & 0x7), host->busyluns);850 851 scsi_done(SCpnt);852 } else853 printk("scsi%d: null command in acornscsi_done", host->host->host_no);854 855 host->scsi.phase = PHASE_IDLE;856}857 858/* ====================================================================================859 * DMA routines860 */861/*862 * Purpose : update SCSI Data Pointer863 * Notes : this will only be one SG entry or less864 */865static866void acornscsi_data_updateptr(AS_Host *host, struct scsi_pointer *SCp, unsigned int length)867{868 SCp->ptr += length;869 SCp->this_residual -= length;870 871 if (SCp->this_residual == 0 && next_SCp(SCp) == 0)872 host->dma.xfer_done = 1;873}874 875/*876 * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr,877 * unsigned int start_addr, unsigned int length)878 * Purpose : read data from DMA RAM879 * Params : host - host to transfer from880 * ptr - DRAM address881 * start_addr - host mem address882 * length - number of bytes to transfer883 * Notes : this will only be one SG entry or less884 */885static886void acornscsi_data_read(AS_Host *host, char *ptr,887 unsigned int start_addr, unsigned int length)888{889 extern void __acornscsi_in(void __iomem *, char *buf, int len);890 unsigned int page, offset, len = length;891 892 page = (start_addr >> 12);893 offset = start_addr & ((1 << 12) - 1);894 895 writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);896 897 while (len > 0) {898 unsigned int this_len;899 900 if (len + offset > (1 << 12))901 this_len = (1 << 12) - offset;902 else903 this_len = len;904 905 __acornscsi_in(host->base + (offset << 1), ptr, this_len);906 907 offset += this_len;908 ptr += this_len;909 len -= this_len;910 911 if (offset == (1 << 12)) {912 offset = 0;913 page ++;914 writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);915 }916 }917 writeb(host->card.page_reg, host->fast + PAGE_REG);918}919 920/*921 * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr,922 * unsigned int start_addr, unsigned int length)923 * Purpose : write data to DMA RAM924 * Params : host - host to transfer from925 * ptr - DRAM address926 * start_addr - host mem address927 * length - number of bytes to transfer928 * Notes : this will only be one SG entry or less929 */930static931void acornscsi_data_write(AS_Host *host, char *ptr,932 unsigned int start_addr, unsigned int length)933{934 extern void __acornscsi_out(void __iomem *, char *buf, int len);935 unsigned int page, offset, len = length;936 937 page = (start_addr >> 12);938 offset = start_addr & ((1 << 12) - 1);939 940 writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);941 942 while (len > 0) {943 unsigned int this_len;944 945 if (len + offset > (1 << 12))946 this_len = (1 << 12) - offset;947 else948 this_len = len;949 950 __acornscsi_out(host->base + (offset << 1), ptr, this_len);951 952 offset += this_len;953 ptr += this_len;954 len -= this_len;955 956 if (offset == (1 << 12)) {957 offset = 0;958 page ++;959 writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG);960 }961 }962 writeb(host->card.page_reg, host->fast + PAGE_REG);963}964 965/* =========================================================================================966 * On-board DMA routines967 */968#ifdef USE_DMAC969/*970 * Prototype: void acornscsi_dmastop(AS_Host *host)971 * Purpose : stop all DMA972 * Params : host - host on which to stop DMA973 * Notes : This is called when leaving DATA IN/OUT phase,974 * or when interface is RESET975 */976static inline977void acornscsi_dma_stop(AS_Host *host)978{979 dmac_write(host, DMAC_MASKREG, MASK_ON);980 dmac_clearintr(host);981 982#if (DEBUG & DEBUG_DMA)983 DBG(host->SCpnt, acornscsi_dumpdma(host, "stop"));984#endif985}986 987/*988 * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)989 * Purpose : setup DMA controller for data transfer990 * Params : host - host to setup991 * direction - data transfer direction992 * Notes : This is called when entering DATA I/O phase, not993 * while we're in a DATA I/O phase994 */995static996void acornscsi_dma_setup(AS_Host *host, dmadir_t direction)997{998 unsigned int address, length, mode;999 1000 host->dma.direction = direction;1001 1002 dmac_write(host, DMAC_MASKREG, MASK_ON);1003 1004 if (direction == DMA_OUT) {1005#if (DEBUG & DEBUG_NO_WRITE)1006 if (NO_WRITE & (1 << host->SCpnt->device->id)) {1007 printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n",1008 host->host->host_no, acornscsi_target(host));1009 return;1010 }1011#endif1012 mode = DMAC_WRITE;1013 } else1014 mode = DMAC_READ;1015 1016 /*1017 * Allocate some buffer space, limited to half the buffer size1018 */1019 length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);1020 if (length) {1021 host->dma.start_addr = address = host->dma.free_addr;1022 host->dma.free_addr = (host->dma.free_addr + length) &1023 (DMAC_BUFFER_SIZE - 1);1024 1025 /*1026 * Transfer data to DMA memory1027 */1028 if (direction == DMA_OUT)1029 acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,1030 length);1031 1032 length -= 1;1033 dmac_write(host, DMAC_TXCNTLO, length);1034 dmac_write(host, DMAC_TXCNTHI, length >> 8);1035 dmac_write(host, DMAC_TXADRLO, address);1036 dmac_write(host, DMAC_TXADRMD, address >> 8);1037 dmac_write(host, DMAC_TXADRHI, 0);1038 dmac_write(host, DMAC_MODECON, mode);1039 dmac_write(host, DMAC_MASKREG, MASK_OFF);1040 1041#if (DEBUG & DEBUG_DMA)1042 DBG(host->SCpnt, acornscsi_dumpdma(host, "strt"));1043#endif1044 host->dma.xfer_setup = 1;1045 }1046}1047 1048/*1049 * Function: void acornscsi_dma_cleanup(AS_Host *host)1050 * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct1051 * Params : host - host to finish1052 * Notes : This is called when a command is:1053 * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONNECT1054 * : This must not return until all transfers are completed.1055 */1056static1057void acornscsi_dma_cleanup(AS_Host *host)1058{1059 dmac_write(host, DMAC_MASKREG, MASK_ON);1060 dmac_clearintr(host);1061 1062 /*1063 * Check for a pending transfer1064 */1065 if (host->dma.xfer_required) {1066 host->dma.xfer_required = 0;1067 if (host->dma.direction == DMA_IN)1068 acornscsi_data_read(host, host->dma.xfer_ptr,1069 host->dma.xfer_start, host->dma.xfer_length);1070 }1071 1072 /*1073 * Has a transfer been setup?1074 */1075 if (host->dma.xfer_setup) {1076 unsigned int transferred;1077 1078 host->dma.xfer_setup = 0;1079 1080#if (DEBUG & DEBUG_DMA)1081 DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi"));1082#endif1083 1084 /*1085 * Calculate number of bytes transferred from DMA.1086 */1087 transferred = dmac_address(host) - host->dma.start_addr;1088 host->dma.transferred += transferred;1089 1090 if (host->dma.direction == DMA_IN)1091 acornscsi_data_read(host, host->scsi.SCp.ptr,1092 host->dma.start_addr, transferred);1093 1094 /*1095 * Update SCSI pointers1096 */1097 acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);1098#if (DEBUG & DEBUG_DMA)1099 DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo"));1100#endif1101 }1102}1103 1104/*1105 * Function: void acornscsi_dmacintr(AS_Host *host)1106 * Purpose : handle interrupts from DMAC device1107 * Params : host - host to process1108 * Notes : If reading, we schedule the read to main memory &1109 * allow the transfer to continue.1110 * : If writing, we fill the onboard DMA memory from main1111 * memory.1112 * : Called whenever DMAC finished it's current transfer.1113 */1114static1115void acornscsi_dma_intr(AS_Host *host)1116{1117 unsigned int address, length, transferred;1118 1119#if (DEBUG & DEBUG_DMA)1120 DBG(host->SCpnt, acornscsi_dumpdma(host, "inti"));1121#endif1122 1123 dmac_write(host, DMAC_MASKREG, MASK_ON);1124 dmac_clearintr(host);1125 1126 /*1127 * Calculate amount transferred via DMA1128 */1129 transferred = dmac_address(host) - host->dma.start_addr;1130 host->dma.transferred += transferred;1131 1132 /*1133 * Schedule DMA transfer off board1134 */1135 if (host->dma.direction == DMA_IN) {1136 host->dma.xfer_start = host->dma.start_addr;1137 host->dma.xfer_length = transferred;1138 host->dma.xfer_ptr = host->scsi.SCp.ptr;1139 host->dma.xfer_required = 1;1140 }1141 1142 acornscsi_data_updateptr(host, &host->scsi.SCp, transferred);1143 1144 /*1145 * Allocate some buffer space, limited to half the on-board RAM size1146 */1147 length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2);1148 if (length) {1149 host->dma.start_addr = address = host->dma.free_addr;1150 host->dma.free_addr = (host->dma.free_addr + length) &1151 (DMAC_BUFFER_SIZE - 1);1152 1153 /*1154 * Transfer data to DMA memory1155 */1156 if (host->dma.direction == DMA_OUT)1157 acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr,1158 length);1159 1160 length -= 1;1161 dmac_write(host, DMAC_TXCNTLO, length);1162 dmac_write(host, DMAC_TXCNTHI, length >> 8);1163 dmac_write(host, DMAC_TXADRLO, address);1164 dmac_write(host, DMAC_TXADRMD, address >> 8);1165 dmac_write(host, DMAC_TXADRHI, 0);1166 dmac_write(host, DMAC_MASKREG, MASK_OFF);1167 1168#if (DEBUG & DEBUG_DMA)1169 DBG(host->SCpnt, acornscsi_dumpdma(host, "into"));1170#endif1171 } else {1172 host->dma.xfer_setup = 0;1173#if 01174 /*1175 * If the interface still wants more, then this is an error.1176 * We give it another byte, but we also attempt to raise an1177 * attention condition. We continue giving one byte until1178 * the device recognises the attention.1179 */1180 if (dmac_read(host, DMAC_STATUS) & STATUS_RQ0) {1181 acornscsi_abortcmd(host);1182 1183 dmac_write(host, DMAC_TXCNTLO, 0);1184 dmac_write(host, DMAC_TXCNTHI, 0);1185 dmac_write(host, DMAC_TXADRLO, 0);1186 dmac_write(host, DMAC_TXADRMD, 0);1187 dmac_write(host, DMAC_TXADRHI, 0);1188 dmac_write(host, DMAC_MASKREG, MASK_OFF);1189 }1190#endif1191 }1192}1193 1194/*1195 * Function: void acornscsi_dma_xfer(AS_Host *host)1196 * Purpose : transfer data between AcornSCSI and memory1197 * Params : host - host to process1198 */1199static1200void acornscsi_dma_xfer(AS_Host *host)1201{1202 host->dma.xfer_required = 0;1203 1204 if (host->dma.direction == DMA_IN)1205 acornscsi_data_read(host, host->dma.xfer_ptr,1206 host->dma.xfer_start, host->dma.xfer_length);1207}1208 1209/*1210 * Function: void acornscsi_dma_adjust(AS_Host *host)1211 * Purpose : adjust DMA pointers & count for bytes transferred to1212 * SBIC but not SCSI bus.1213 * Params : host - host to adjust DMA count for1214 */1215static1216void acornscsi_dma_adjust(AS_Host *host)1217{1218 if (host->dma.xfer_setup) {1219 signed long transferred;1220#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))1221 DBG(host->SCpnt, acornscsi_dumpdma(host, "adji"));1222#endif1223 /*1224 * Calculate correct DMA address - DMA is ahead of SCSI bus while1225 * writing.1226 * host->scsi.SCp.scsi_xferred is the number of bytes1227 * actually transferred to/from the SCSI bus.1228 * host->dma.transferred is the number of bytes transferred1229 * over DMA since host->dma.start_addr was last set.1230 *1231 * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred1232 * - host->dma.transferred1233 */1234 transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred;1235 if (transferred < 0)1236 printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n",1237 host->host->host_no, acornscsi_target(host), transferred);1238 else if (transferred == 0)1239 host->dma.xfer_setup = 0;1240 else {1241 transferred += host->dma.start_addr;1242 dmac_write(host, DMAC_TXADRLO, transferred);1243 dmac_write(host, DMAC_TXADRMD, transferred >> 8);1244 dmac_write(host, DMAC_TXADRHI, transferred >> 16);1245#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE))1246 DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo"));1247#endif1248 }1249 }1250}1251#endif1252 1253/* =========================================================================================1254 * Data I/O1255 */1256static int1257acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout)1258{1259 unsigned int asr, timeout = max_timeout;1260 int my_ptr = *ptr;1261 1262 while (my_ptr < len) {1263 asr = sbic_arm_read(host, SBIC_ASR);1264 1265 if (asr & ASR_DBR) {1266 timeout = max_timeout;1267 1268 sbic_arm_write(host, SBIC_DATA, bytes[my_ptr++]);1269 } else if (asr & ASR_INT)1270 break;1271 else if (--timeout == 0)1272 break;1273 udelay(1);1274 }1275 1276 *ptr = my_ptr;1277 1278 return (timeout == 0) ? -1 : 0;1279}1280 1281/*1282 * Function: void acornscsi_sendcommand(AS_Host *host)1283 * Purpose : send a command to a target1284 * Params : host - host which is connected to target1285 */1286static void1287acornscsi_sendcommand(AS_Host *host)1288{1289 struct scsi_cmnd *SCpnt = host->SCpnt;1290 1291 sbic_arm_write(host, SBIC_TRANSCNTH, 0);1292 sbic_arm_writenext(host, 0);1293 sbic_arm_writenext(host, SCpnt->cmd_len - host->scsi.SCp.sent_command);1294 1295 acornscsi_sbic_issuecmd(host, CMND_XFERINFO);1296 1297 if (acornscsi_write_pio(host, SCpnt->cmnd,1298 (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000))1299 printk("scsi%d: timeout while sending command\n", host->host->host_no);1300 1301 host->scsi.phase = PHASE_COMMAND;1302}1303 1304static1305void acornscsi_sendmessage(AS_Host *host)1306{1307 unsigned int message_length = msgqueue_msglength(&host->scsi.msgs);1308 unsigned int msgnr;1309 struct message *msg;1310 1311#if (DEBUG & DEBUG_MESSAGES)1312 printk("scsi%d.%c: sending message ",1313 host->host->host_no, acornscsi_target(host));1314#endif1315 1316 switch (message_length) {1317 case 0:1318 acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);1319 1320 acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1");1321 1322 sbic_arm_write(host, SBIC_DATA, NOP);1323 1324 host->scsi.last_message = NOP;1325#if (DEBUG & DEBUG_MESSAGES)1326 printk("NOP");1327#endif1328 break;1329 1330 case 1:1331 acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);1332 msg = msgqueue_getmsg(&host->scsi.msgs, 0);1333 1334 acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2");1335 1336 sbic_arm_write(host, SBIC_DATA, msg->msg[0]);1337 1338 host->scsi.last_message = msg->msg[0];1339#if (DEBUG & DEBUG_MESSAGES)1340 spi_print_msg(msg->msg);1341#endif1342 break;1343 1344 default:1345 /*1346 * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14)1347 * 'When a target sends this (MESSAGE_REJECT) message, it1348 * shall change to MESSAGE IN phase and send this message1349 * prior to requesting additional message bytes from the1350 * initiator. This provides an interlock so that the1351 * initiator can determine which message byte is rejected.1352 */1353 sbic_arm_write(host, SBIC_TRANSCNTH, 0);1354 sbic_arm_writenext(host, 0);1355 sbic_arm_writenext(host, message_length);1356 acornscsi_sbic_issuecmd(host, CMND_XFERINFO);1357 1358 msgnr = 0;1359 while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) {1360 unsigned int i;1361#if (DEBUG & DEBUG_MESSAGES)1362 spi_print_msg(msg);1363#endif1364 i = 0;1365 if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000))1366 printk("scsi%d: timeout while sending message\n", host->host->host_no);1367 1368 host->scsi.last_message = msg->msg[0];1369 if (msg->msg[0] == EXTENDED_MESSAGE)1370 host->scsi.last_message |= msg->msg[2] << 8;1371 1372 if (i != msg->length)1373 break;1374 }1375 break;1376 }1377#if (DEBUG & DEBUG_MESSAGES)1378 printk("\n");1379#endif1380}1381 1382/*1383 * Function: void acornscsi_readstatusbyte(AS_Host *host)1384 * Purpose : Read status byte from connected target1385 * Params : host - host connected to target1386 */1387static1388void acornscsi_readstatusbyte(AS_Host *host)1389{1390 acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT);1391 acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte");1392 host->scsi.SCp.Status = sbic_arm_read(host, SBIC_DATA);1393}1394 1395/*1396 * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host)1397 * Purpose : Read one message byte from connected target1398 * Params : host - host connected to target1399 */1400static1401unsigned char acornscsi_readmessagebyte(AS_Host *host)1402{1403 unsigned char message;1404 1405 acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT);1406 1407 acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte");1408 1409 message = sbic_arm_read(host, SBIC_DATA);1410 1411 /* wait for MSGIN-XFER-PAUSED */1412 acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte");1413 1414 sbic_arm_read(host, SBIC_SSR);1415 1416 return message;1417}1418 1419/*1420 * Function: void acornscsi_message(AS_Host *host)1421 * Purpose : Read complete message from connected target & action message1422 * Params : host - host connected to target1423 */1424static1425void acornscsi_message(AS_Host *host)1426{1427 struct scsi_pointer *scsi_pointer;1428 unsigned char message[16];1429 unsigned int msgidx = 0, msglen = 1;1430 1431 do {1432 message[msgidx] = acornscsi_readmessagebyte(host);1433 1434 switch (msgidx) {1435 case 0:1436 if (message[0] == EXTENDED_MESSAGE ||1437 (message[0] >= 0x20 && message[0] <= 0x2f))1438 msglen = 2;1439 break;1440 1441 case 1:1442 if (message[0] == EXTENDED_MESSAGE)1443 msglen += message[msgidx];1444 break;1445 }1446 msgidx += 1;1447 if (msgidx < msglen) {1448 acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);1449 1450 /* wait for next msg-in */1451 acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack");1452 sbic_arm_read(host, SBIC_SSR);1453 }1454 } while (msgidx < msglen);1455 1456#if (DEBUG & DEBUG_MESSAGES)1457 printk("scsi%d.%c: message in: ",1458 host->host->host_no, acornscsi_target(host));1459 spi_print_msg(message);1460 printk("\n");1461#endif1462 1463 if (host->scsi.phase == PHASE_RECONNECTED) {1464 /*1465 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17)1466 * 'Whenever a target reconnects to an initiator to continue1467 * a tagged I/O process, the SIMPLE QUEUE TAG message shall1468 * be sent immediately following the IDENTIFY message...'1469 */1470 if (message[0] == SIMPLE_QUEUE_TAG)1471 host->scsi.reconnected.tag = message[1];1472 if (acornscsi_reconnect_finish(host))1473 host->scsi.phase = PHASE_MSGIN;1474 }1475 1476 switch (message[0]) {1477 case ABORT_TASK_SET:1478 case ABORT_TASK:1479 case COMMAND_COMPLETE:1480 if (host->scsi.phase != PHASE_STATUSIN) {1481 printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n",1482 host->host->host_no, acornscsi_target(host));1483 acornscsi_dumplog(host, host->SCpnt->device->id);1484 }1485 host->scsi.phase = PHASE_DONE;1486 host->scsi.SCp.Message = message[0];1487 break;1488 1489 case SAVE_POINTERS:1490 /*1491 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20)1492 * 'The SAVE DATA POINTER message is sent from a target to1493 * direct the initiator to copy the active data pointer to1494 * the saved data pointer for the current I/O process.1495 */1496 acornscsi_dma_cleanup(host);1497 scsi_pointer = arm_scsi_pointer(host->SCpnt);1498 *scsi_pointer = host->scsi.SCp;1499 scsi_pointer->sent_command = 0;1500 host->scsi.phase = PHASE_MSGIN;1501 break;1502 1503 case RESTORE_POINTERS:1504 /*1505 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19)1506 * 'The RESTORE POINTERS message is sent from a target to1507 * direct the initiator to copy the most recently saved1508 * command, data, and status pointers for the I/O process1509 * to the corresponding active pointers. The command and1510 * status pointers shall be restored to the beginning of1511 * the present command and status areas.'1512 */1513 acornscsi_dma_cleanup(host);1514 host->scsi.SCp = *arm_scsi_pointer(host->SCpnt);1515 host->scsi.phase = PHASE_MSGIN;1516 break;1517 1518 case DISCONNECT:1519 /*1520 * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2)1521 * 'On those occasions when an error or exception condition occurs1522 * and the target elects to repeat the information transfer, the1523 * target may repeat the transfer either issuing a RESTORE POINTERS1524 * message or by disconnecting without issuing a SAVE POINTERS1525 * message. When reconnection is completed, the most recent1526 * saved pointer values are restored.'1527 */1528 acornscsi_dma_cleanup(host);1529 host->scsi.phase = PHASE_DISCONNECT;1530 break;1531 1532 case MESSAGE_REJECT:1533#if 0 /* this isn't needed any more */1534 /*1535 * If we were negociating sync transfer, we don't yet know if1536 * this REJECT is for the sync transfer or for the tagged queue/wide1537 * transfer. Re-initiate sync transfer negotiation now, and if1538 * we got a REJECT in response to SDTR, then it'll be set to DONE.1539 */1540 if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST)1541 host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE;1542#endif1543 1544 /*1545 * If we have any messages waiting to go out, then assert ATN now1546 */1547 if (msgqueue_msglength(&host->scsi.msgs))1548 acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);1549 1550 switch (host->scsi.last_message) {1551 case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8):1552 /*1553 * Target can't handle synchronous transfers1554 */1555 printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n",1556 host->host->host_no, acornscsi_target(host));1557 host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA;1558 host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS;1559 sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);1560 break;1561 1562 default:1563 break;1564 }1565 break;1566 1567 case SIMPLE_QUEUE_TAG:1568 /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */1569 printk("scsi%d.%c: reconnect queue tag %02X\n",1570 host->host->host_no, acornscsi_target(host),1571 message[1]);1572 break;1573 1574 case EXTENDED_MESSAGE:1575 switch (message[2]) {1576#ifdef CONFIG_SCSI_ACORNSCSI_SYNC1577 case EXTENDED_SDTR:1578 if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) {1579 /*1580 * We requested synchronous transfers. This isn't quite right...1581 * We can only say if this succeeded if we proceed on to execute the1582 * command from this message. If we get a MESSAGE PARITY ERROR,1583 * and the target retries fail, then we fallback to asynchronous mode1584 */1585 host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED;1586 printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n",1587 host->host->host_no, acornscsi_target(host),1588 message[4], message[3] * 4);1589 host->device[host->SCpnt->device->id].sync_xfer =1590 calc_sync_xfer(message[3] * 4, message[4]);1591 } else {1592 unsigned char period, length;1593 /*1594 * Target requested synchronous transfers. The agreement is only1595 * to be in operation AFTER the target leaves message out phase.1596 */1597 acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);1598 period = max_t(unsigned int, message[3], sdtr_period / 4);1599 length = min_t(unsigned int, message[4], sdtr_size);1600 msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3,1601 EXTENDED_SDTR, period, length);1602 host->device[host->SCpnt->device->id].sync_xfer =1603 calc_sync_xfer(period * 4, length);1604 }1605 sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);1606 break;1607#else1608 /* We do not accept synchronous transfers. Respond with a1609 * MESSAGE_REJECT.1610 */1611#endif1612 1613 case EXTENDED_WDTR:1614 /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT1615 * to a wide data transfer request.1616 */1617 default:1618 acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);1619 msgqueue_flush(&host->scsi.msgs);1620 msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);1621 break;1622 }1623 break;1624 1625 default: /* reject message */1626 printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n",1627 host->host->host_no, acornscsi_target(host),1628 message[0]);1629 acornscsi_sbic_issuecmd(host, CMND_ASSERTATN);1630 msgqueue_flush(&host->scsi.msgs);1631 msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT);1632 host->scsi.phase = PHASE_MSGIN;1633 break;1634 }1635 acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);1636}1637 1638/*1639 * Function: int acornscsi_buildmessages(AS_Host *host)1640 * Purpose : build the connection messages for a host1641 * Params : host - host to add messages to1642 */1643static1644void acornscsi_buildmessages(AS_Host *host)1645{1646#if 01647 /* does the device need resetting? */1648 if (cmd_reset) {1649 msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET);1650 return;1651 }1652#endif1653 1654 msgqueue_addmsg(&host->scsi.msgs, 1,1655 IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok,1656 host->SCpnt->device->lun));1657 1658#if 01659 /* does the device need the current command aborted */1660 if (cmd_aborted) {1661 acornscsi_abortcmd(host);1662 return;1663 }1664#endif1665 1666 1667#ifdef CONFIG_SCSI_ACORNSCSI_SYNC1668 if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) {1669 host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST;1670 msgqueue_addmsg(&host->scsi.msgs, 5,1671 EXTENDED_MESSAGE, 3, EXTENDED_SDTR,1672 sdtr_period / 4, sdtr_size);1673 }1674#endif1675}1676 1677/*1678 * Function: int acornscsi_starttransfer(AS_Host *host)1679 * Purpose : transfer data to/from connected target1680 * Params : host - host to which target is connected1681 * Returns : 0 if failure1682 */1683static1684int acornscsi_starttransfer(AS_Host *host)1685{1686 int residual;1687 1688 if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) {1689 printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n",1690 host->host->host_no, acornscsi_target(host));1691 return 0;1692 }1693 1694 residual = scsi_bufflen(host->SCpnt) - host->scsi.SCp.scsi_xferred;1695 1696 sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer);1697 sbic_arm_writenext(host, residual >> 16);1698 sbic_arm_writenext(host, residual >> 8);1699 sbic_arm_writenext(host, residual);1700 acornscsi_sbic_issuecmd(host, CMND_XFERINFO);1701 return 1;1702}1703 1704/* =========================================================================================1705 * Connection & Disconnection1706 */1707/*1708 * Function : acornscsi_reconnect(AS_Host *host)1709 * Purpose : reconnect a previously disconnected command1710 * Params : host - host specific data1711 * Remarks : SCSI spec says:1712 * 'The set of active pointers is restored from the set1713 * of saved pointers upon reconnection of the I/O process'1714 */1715static1716int acornscsi_reconnect(AS_Host *host)1717{1718 unsigned int target, lun, ok = 0;1719 1720 target = sbic_arm_read(host, SBIC_SOURCEID);1721 1722 if (!(target & 8))1723 printk(KERN_ERR "scsi%d: invalid source id after reselection "1724 "- device fault?\n",1725 host->host->host_no);1726 1727 target &= 7;1728 1729 if (host->SCpnt && !host->scsi.disconnectable) {1730 printk(KERN_ERR "scsi%d.%d: reconnected while command in "1731 "progress to target %d?\n",1732 host->host->host_no, target, host->SCpnt->device->id);1733 host->SCpnt = NULL;1734 }1735 1736 lun = sbic_arm_read(host, SBIC_DATA) & 7;1737 1738 host->scsi.reconnected.target = target;1739 host->scsi.reconnected.lun = lun;1740 host->scsi.reconnected.tag = 0;1741 1742 if (host->scsi.disconnectable && host->SCpnt &&1743 host->SCpnt->device->id == target && host->SCpnt->device->lun == lun)1744 ok = 1;1745 1746 if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun))1747 ok = 1;1748 1749 ADD_STATUS(target, 0x81, host->scsi.phase, 0);1750 1751 if (ok) {1752 host->scsi.phase = PHASE_RECONNECTED;1753 } else {1754 /* this doesn't seem to work */1755 printk(KERN_ERR "scsi%d.%c: reselected with no command "1756 "to reconnect with\n",1757 host->host->host_no, '0' + target);1758 acornscsi_dumplog(host, target);1759 acornscsi_abortcmd(host);1760 if (host->SCpnt) {1761 queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);1762 host->SCpnt = NULL;1763 }1764 }1765 acornscsi_sbic_issuecmd(host, CMND_NEGATEACK);1766 return !ok;1767}1768 1769/*1770 * Function: int acornscsi_reconnect_finish(AS_Host *host)1771 * Purpose : finish reconnecting a command1772 * Params : host - host to complete1773 * Returns : 0 if failed1774 */1775static1776int acornscsi_reconnect_finish(AS_Host *host)1777{1778 if (host->scsi.disconnectable && host->SCpnt) {1779 host->scsi.disconnectable = 0;1780 if (host->SCpnt->device->id == host->scsi.reconnected.target &&1781 host->SCpnt->device->lun == host->scsi.reconnected.lun &&1782 scsi_cmd_to_rq(host->SCpnt)->tag == host->scsi.reconnected.tag) {1783#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))1784 DBG(host->SCpnt, printk("scsi%d.%c: reconnected",1785 host->host->host_no, acornscsi_target(host)));1786#endif1787 } else {1788 queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt);1789#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))1790 DBG(host->SCpnt, printk("scsi%d.%c: had to move command "1791 "to disconnected queue\n",1792 host->host->host_no, acornscsi_target(host)));1793#endif1794 host->SCpnt = NULL;1795 }1796 }1797 if (!host->SCpnt) {1798 host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected,1799 host->scsi.reconnected.target,1800 host->scsi.reconnected.lun,1801 host->scsi.reconnected.tag);1802#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))1803 DBG(host->SCpnt, printk("scsi%d.%c: had to get command",1804 host->host->host_no, acornscsi_target(host)));1805#endif1806 }1807 1808 if (!host->SCpnt)1809 acornscsi_abortcmd(host);1810 else {1811 /*1812 * Restore data pointer from SAVED pointers.1813 */1814 host->scsi.SCp = *arm_scsi_pointer(host->SCpnt);1815#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))1816 printk(", data pointers: [%p, %X]",1817 host->scsi.SCp.ptr, host->scsi.SCp.this_residual);1818#endif1819 }1820#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON))1821 printk("\n");1822#endif1823 1824 host->dma.transferred = host->scsi.SCp.scsi_xferred;1825 1826 return host->SCpnt != NULL;1827}1828 1829/*1830 * Function: void acornscsi_disconnect_unexpected(AS_Host *host)1831 * Purpose : handle an unexpected disconnect1832 * Params : host - host on which disconnect occurred1833 */1834static1835void acornscsi_disconnect_unexpected(AS_Host *host)1836{1837 printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n",1838 host->host->host_no, acornscsi_target(host));1839#if (DEBUG & DEBUG_ABORT)1840 acornscsi_dumplog(host, 8);1841#endif1842 1843 acornscsi_done(host, &host->SCpnt, DID_ERROR);1844}1845 1846/*1847 * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag)1848 * Purpose : abort a currently executing command1849 * Params : host - host with connected command to abort1850 */1851static1852void acornscsi_abortcmd(AS_Host *host)1853{1854 host->scsi.phase = PHASE_ABORTED;1855 sbic_arm_write(host, SBIC_CMND, CMND_ASSERTATN);1856 1857 msgqueue_flush(&host->scsi.msgs);1858 msgqueue_addmsg(&host->scsi.msgs, 1, ABORT);1859}1860 1861/* ==========================================================================================1862 * Interrupt routines.1863 */1864/*1865 * Function: int acornscsi_sbicintr(AS_Host *host)1866 * Purpose : handle interrupts from SCSI device1867 * Params : host - host to process1868 * Returns : INTR_PROCESS if expecting another SBIC interrupt1869 * INTR_IDLE if no interrupt1870 * INTR_NEXT_COMMAND if we have finished processing the command1871 */1872static1873intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq)1874{1875 unsigned int asr, ssr;1876 1877 asr = sbic_arm_read(host, SBIC_ASR);1878 if (!(asr & ASR_INT))1879 return INTR_IDLE;1880 1881 ssr = sbic_arm_read(host, SBIC_SSR);1882 1883#if (DEBUG & DEBUG_PHASES)1884 print_sbic_status(asr, ssr, host->scsi.phase);1885#endif1886 1887 ADD_STATUS(8, ssr, host->scsi.phase, in_irq);1888 1889 if (host->SCpnt && !host->scsi.disconnectable)1890 ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);1891 1892 switch (ssr) {1893 case 0x00: /* reset state - not advanced */1894 printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n",1895 host->host->host_no);1896 /* setup sbic - WD33C93A */1897 sbic_arm_write(host, SBIC_OWNID, OWNID_EAF | host->host->this_id);1898 sbic_arm_write(host, SBIC_CMND, CMND_RESET);1899 return INTR_IDLE;1900 1901 case 0x01: /* reset state - advanced */1902 sbic_arm_write(host, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI);1903 sbic_arm_write(host, SBIC_TIMEOUT, TIMEOUT_TIME);1904 sbic_arm_write(host, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA);1905 sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP);1906 msgqueue_flush(&host->scsi.msgs);1907 return INTR_IDLE;1908 1909 case 0x41: /* unexpected disconnect aborted command */1910 acornscsi_disconnect_unexpected(host);1911 return INTR_NEXT_COMMAND;1912 }1913 1914 switch (host->scsi.phase) {1915 case PHASE_CONNECTING: /* STATE: command removed from issue queue */1916 switch (ssr) {1917 case 0x11: /* -> PHASE_CONNECTED */1918 /* BUS FREE -> SELECTION */1919 host->scsi.phase = PHASE_CONNECTED;1920 msgqueue_flush(&host->scsi.msgs);1921 host->dma.transferred = host->scsi.SCp.scsi_xferred;1922 /* 33C93 gives next interrupt indicating bus phase */1923 asr = sbic_arm_read(host, SBIC_ASR);1924 if (!(asr & ASR_INT))1925 break;1926 ssr = sbic_arm_read(host, SBIC_SSR);1927 ADD_STATUS(8, ssr, host->scsi.phase, 1);1928 ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1);1929 goto connected;1930 1931 case 0x42: /* select timed out */1932 /* -> PHASE_IDLE */1933 acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT);1934 return INTR_NEXT_COMMAND;1935 1936 case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */1937 /* BUS FREE -> RESELECTION */1938 host->origSCpnt = host->SCpnt;1939 host->SCpnt = NULL;1940 msgqueue_flush(&host->scsi.msgs);1941 acornscsi_reconnect(host);1942 break;1943 1944 default:1945 printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n",1946 host->host->host_no, acornscsi_target(host), ssr);1947 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);1948 acornscsi_abortcmd(host);1949 }1950 return INTR_PROCESSING;1951 1952 connected:1953 case PHASE_CONNECTED: /* STATE: device selected ok */1954 switch (ssr) {1955#ifdef NONSTANDARD1956 case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */1957 /* SELECTION -> COMMAND */1958 acornscsi_sendcommand(host);1959 break;1960 1961 case 0x8b: /* -> PHASE_STATUS */1962 /* SELECTION -> STATUS */1963 acornscsi_readstatusbyte(host);1964 host->scsi.phase = PHASE_STATUSIN;1965 break;1966#endif1967 1968 case 0x8e: /* -> PHASE_MSGOUT */1969 /* SELECTION ->MESSAGE OUT */1970 host->scsi.phase = PHASE_MSGOUT;1971 acornscsi_buildmessages(host);1972 acornscsi_sendmessage(host);1973 break;1974 1975 /* these should not happen */1976 case 0x85: /* target disconnected */1977 acornscsi_done(host, &host->SCpnt, DID_ERROR);1978 break;1979 1980 default:1981 printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n",1982 host->host->host_no, acornscsi_target(host), ssr);1983 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);1984 acornscsi_abortcmd(host);1985 }1986 return INTR_PROCESSING;1987 1988 case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */1989 /*1990 * SCSI standard says that MESSAGE OUT phases can be followed by a1991 * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase1992 */1993 switch (ssr) {1994 case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */1995 case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */1996 /* MESSAGE OUT -> COMMAND */1997 acornscsi_sendcommand(host);1998 break;1999 2000 case 0x8b: /* -> PHASE_STATUS */2001 case 0x1b: /* -> PHASE_STATUS */2002 /* MESSAGE OUT -> STATUS */2003 acornscsi_readstatusbyte(host);2004 host->scsi.phase = PHASE_STATUSIN;2005 break;2006 2007 case 0x8e: /* -> PHASE_MSGOUT */2008 /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */2009 acornscsi_sendmessage(host);2010 break;2011 2012 case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */2013 case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */2014 /* MESSAGE OUT -> MESSAGE IN */2015 acornscsi_message(host);2016 break;2017 2018 default:2019 printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n",2020 host->host->host_no, acornscsi_target(host), ssr);2021 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2022 }2023 return INTR_PROCESSING;2024 2025 case PHASE_COMMAND: /* STATE: connected & command sent */2026 switch (ssr) {2027 case 0x18: /* -> PHASE_DATAOUT */2028 /* COMMAND -> DATA OUT */2029 if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)2030 acornscsi_abortcmd(host);2031 acornscsi_dma_setup(host, DMA_OUT);2032 if (!acornscsi_starttransfer(host))2033 acornscsi_abortcmd(host);2034 host->scsi.phase = PHASE_DATAOUT;2035 return INTR_IDLE;2036 2037 case 0x19: /* -> PHASE_DATAIN */2038 /* COMMAND -> DATA IN */2039 if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len)2040 acornscsi_abortcmd(host);2041 acornscsi_dma_setup(host, DMA_IN);2042 if (!acornscsi_starttransfer(host))2043 acornscsi_abortcmd(host);2044 host->scsi.phase = PHASE_DATAIN;2045 return INTR_IDLE;2046 2047 case 0x1b: /* -> PHASE_STATUS */2048 /* COMMAND -> STATUS */2049 acornscsi_readstatusbyte(host);2050 host->scsi.phase = PHASE_STATUSIN;2051 break;2052 2053 case 0x1e: /* -> PHASE_MSGOUT */2054 /* COMMAND -> MESSAGE OUT */2055 acornscsi_sendmessage(host);2056 break;2057 2058 case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */2059 /* COMMAND -> MESSAGE IN */2060 acornscsi_message(host);2061 break;2062 2063 default:2064 printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n",2065 host->host->host_no, acornscsi_target(host), ssr);2066 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2067 }2068 return INTR_PROCESSING;2069 2070 case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */2071 if (ssr == 0x85) { /* -> PHASE_IDLE */2072 host->scsi.disconnectable = 1;2073 host->scsi.reconnected.tag = 0;2074 host->scsi.phase = PHASE_IDLE;2075 host->stats.disconnects += 1;2076 } else {2077 printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n",2078 host->host->host_no, acornscsi_target(host), ssr);2079 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2080 }2081 return INTR_NEXT_COMMAND;2082 2083 case PHASE_IDLE: /* STATE: disconnected */2084 if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */2085 acornscsi_reconnect(host);2086 else {2087 printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n",2088 host->host->host_no, acornscsi_target(host), ssr);2089 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2090 }2091 return INTR_PROCESSING;2092 2093 case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */2094 /*2095 * Command reconnected - if MESGIN, get message - it may be2096 * the tag. If not, get command out of disconnected queue2097 */2098 /*2099 * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY,2100 * reconnect I_T_L command2101 */2102 if (ssr != 0x8f && !acornscsi_reconnect_finish(host))2103 return INTR_IDLE;2104 ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq);2105 switch (ssr) {2106 case 0x88: /* data out phase */2107 /* -> PHASE_DATAOUT */2108 /* MESSAGE IN -> DATA OUT */2109 acornscsi_dma_setup(host, DMA_OUT);2110 if (!acornscsi_starttransfer(host))2111 acornscsi_abortcmd(host);2112 host->scsi.phase = PHASE_DATAOUT;2113 return INTR_IDLE;2114 2115 case 0x89: /* data in phase */2116 /* -> PHASE_DATAIN */2117 /* MESSAGE IN -> DATA IN */2118 acornscsi_dma_setup(host, DMA_IN);2119 if (!acornscsi_starttransfer(host))2120 acornscsi_abortcmd(host);2121 host->scsi.phase = PHASE_DATAIN;2122 return INTR_IDLE;2123 2124 case 0x8a: /* command out */2125 /* MESSAGE IN -> COMMAND */2126 acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */2127 break;2128 2129 case 0x8b: /* status in */2130 /* -> PHASE_STATUSIN */2131 /* MESSAGE IN -> STATUS */2132 acornscsi_readstatusbyte(host);2133 host->scsi.phase = PHASE_STATUSIN;2134 break;2135 2136 case 0x8e: /* message out */2137 /* -> PHASE_MSGOUT */2138 /* MESSAGE IN -> MESSAGE OUT */2139 acornscsi_sendmessage(host);2140 break;2141 2142 case 0x8f: /* message in */2143 acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */2144 break;2145 2146 default:2147 printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n",2148 host->host->host_no, acornscsi_target(host), ssr);2149 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2150 }2151 return INTR_PROCESSING;2152 2153 case PHASE_DATAIN: /* STATE: transferred data in */2154 /*2155 * This is simple - if we disconnect then the DMA address & count is2156 * correct.2157 */2158 switch (ssr) {2159 case 0x19: /* -> PHASE_DATAIN */2160 case 0x89: /* -> PHASE_DATAIN */2161 acornscsi_abortcmd(host);2162 return INTR_IDLE;2163 2164 case 0x1b: /* -> PHASE_STATUSIN */2165 case 0x4b: /* -> PHASE_STATUSIN */2166 case 0x8b: /* -> PHASE_STATUSIN */2167 /* DATA IN -> STATUS */2168 host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -2169 acornscsi_sbic_xfcount(host);2170 acornscsi_dma_stop(host);2171 acornscsi_readstatusbyte(host);2172 host->scsi.phase = PHASE_STATUSIN;2173 break;2174 2175 case 0x1e: /* -> PHASE_MSGOUT */2176 case 0x4e: /* -> PHASE_MSGOUT */2177 case 0x8e: /* -> PHASE_MSGOUT */2178 /* DATA IN -> MESSAGE OUT */2179 host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -2180 acornscsi_sbic_xfcount(host);2181 acornscsi_dma_stop(host);2182 acornscsi_sendmessage(host);2183 break;2184 2185 case 0x1f: /* message in */2186 case 0x4f: /* message in */2187 case 0x8f: /* message in */2188 /* DATA IN -> MESSAGE IN */2189 host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -2190 acornscsi_sbic_xfcount(host);2191 acornscsi_dma_stop(host);2192 acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */2193 break;2194 2195 default:2196 printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n",2197 host->host->host_no, acornscsi_target(host), ssr);2198 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2199 }2200 return INTR_PROCESSING;2201 2202 case PHASE_DATAOUT: /* STATE: transferred data out */2203 /*2204 * This is more complicated - if we disconnect, the DMA could be 122205 * bytes ahead of us. We need to correct this.2206 */2207 switch (ssr) {2208 case 0x18: /* -> PHASE_DATAOUT */2209 case 0x88: /* -> PHASE_DATAOUT */2210 acornscsi_abortcmd(host);2211 return INTR_IDLE;2212 2213 case 0x1b: /* -> PHASE_STATUSIN */2214 case 0x4b: /* -> PHASE_STATUSIN */2215 case 0x8b: /* -> PHASE_STATUSIN */2216 /* DATA OUT -> STATUS */2217 host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -2218 acornscsi_sbic_xfcount(host);2219 acornscsi_dma_stop(host);2220 acornscsi_dma_adjust(host);2221 acornscsi_readstatusbyte(host);2222 host->scsi.phase = PHASE_STATUSIN;2223 break;2224 2225 case 0x1e: /* -> PHASE_MSGOUT */2226 case 0x4e: /* -> PHASE_MSGOUT */2227 case 0x8e: /* -> PHASE_MSGOUT */2228 /* DATA OUT -> MESSAGE OUT */2229 host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -2230 acornscsi_sbic_xfcount(host);2231 acornscsi_dma_stop(host);2232 acornscsi_dma_adjust(host);2233 acornscsi_sendmessage(host);2234 break;2235 2236 case 0x1f: /* message in */2237 case 0x4f: /* message in */2238 case 0x8f: /* message in */2239 /* DATA OUT -> MESSAGE IN */2240 host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) -2241 acornscsi_sbic_xfcount(host);2242 acornscsi_dma_stop(host);2243 acornscsi_dma_adjust(host);2244 acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */2245 break;2246 2247 default:2248 printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n",2249 host->host->host_no, acornscsi_target(host), ssr);2250 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2251 }2252 return INTR_PROCESSING;2253 2254 case PHASE_STATUSIN: /* STATE: status in complete */2255 switch (ssr) {2256 case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */2257 case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */2258 /* STATUS -> MESSAGE IN */2259 acornscsi_message(host);2260 break;2261 2262 case 0x1e: /* -> PHASE_MSGOUT */2263 case 0x8e: /* -> PHASE_MSGOUT */2264 /* STATUS -> MESSAGE OUT */2265 acornscsi_sendmessage(host);2266 break;2267 2268 default:2269 printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n",2270 host->host->host_no, acornscsi_target(host), ssr);2271 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2272 }2273 return INTR_PROCESSING;2274 2275 case PHASE_MSGIN: /* STATE: message in */2276 switch (ssr) {2277 case 0x1e: /* -> PHASE_MSGOUT */2278 case 0x4e: /* -> PHASE_MSGOUT */2279 case 0x8e: /* -> PHASE_MSGOUT */2280 /* MESSAGE IN -> MESSAGE OUT */2281 acornscsi_sendmessage(host);2282 break;2283 2284 case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */2285 case 0x2f:2286 case 0x4f:2287 case 0x8f:2288 acornscsi_message(host);2289 break;2290 2291 case 0x85:2292 printk("scsi%d.%c: strange message in disconnection\n",2293 host->host->host_no, acornscsi_target(host));2294 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2295 acornscsi_done(host, &host->SCpnt, DID_ERROR);2296 break;2297 2298 default:2299 printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n",2300 host->host->host_no, acornscsi_target(host), ssr);2301 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2302 }2303 return INTR_PROCESSING;2304 2305 case PHASE_DONE: /* STATE: received status & message */2306 switch (ssr) {2307 case 0x85: /* -> PHASE_IDLE */2308 acornscsi_done(host, &host->SCpnt, DID_OK);2309 return INTR_NEXT_COMMAND;2310 2311 case 0x1e:2312 case 0x8e:2313 acornscsi_sendmessage(host);2314 break;2315 2316 default:2317 printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n",2318 host->host->host_no, acornscsi_target(host), ssr);2319 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2320 }2321 return INTR_PROCESSING;2322 2323 case PHASE_ABORTED:2324 switch (ssr) {2325 case 0x85:2326 if (host->SCpnt)2327 acornscsi_done(host, &host->SCpnt, DID_ABORT);2328 else {2329 clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun,2330 host->busyluns);2331 host->scsi.phase = PHASE_IDLE;2332 }2333 return INTR_NEXT_COMMAND;2334 2335 case 0x1e:2336 case 0x2e:2337 case 0x4e:2338 case 0x8e:2339 acornscsi_sendmessage(host);2340 break;2341 2342 default:2343 printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n",2344 host->host->host_no, acornscsi_target(host), ssr);2345 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2346 }2347 return INTR_PROCESSING;2348 2349 default:2350 printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n",2351 host->host->host_no, acornscsi_target(host), ssr);2352 acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8);2353 }2354 return INTR_PROCESSING;2355}2356 2357/*2358 * Prototype: void acornscsi_intr(int irq, void *dev_id)2359 * Purpose : handle interrupts from Acorn SCSI card2360 * Params : irq - interrupt number2361 * dev_id - device specific data (AS_Host structure)2362 */2363static irqreturn_t2364acornscsi_intr(int irq, void *dev_id)2365{2366 AS_Host *host = (AS_Host *)dev_id;2367 intr_ret_t ret;2368 int iostatus;2369 int in_irq = 0;2370 2371 do {2372 ret = INTR_IDLE;2373 2374 iostatus = readb(host->fast + INT_REG);2375 2376 if (iostatus & 2) {2377 acornscsi_dma_intr(host);2378 iostatus = readb(host->fast + INT_REG);2379 }2380 2381 if (iostatus & 8)2382 ret = acornscsi_sbicintr(host, in_irq);2383 2384 /*2385 * If we have a transfer pending, start it.2386 * Only start it if the interface has already started transferring2387 * it's data2388 */2389 if (host->dma.xfer_required)2390 acornscsi_dma_xfer(host);2391 2392 if (ret == INTR_NEXT_COMMAND)2393 ret = acornscsi_kick(host);2394 2395 in_irq = 1;2396 } while (ret != INTR_IDLE);2397 2398 return IRQ_HANDLED;2399}2400 2401/*=============================================================================================2402 * Interfaces between interrupt handler and rest of scsi code2403 */2404 2405/*2406 * Function : acornscsi_queuecmd(struct scsi_cmnd *cmd)2407 * Purpose : queues a SCSI command2408 * Params : cmd - SCSI command2409 * Returns : 0, or < 0 on error.2410 */2411static int acornscsi_queuecmd_lck(struct scsi_cmnd *SCpnt)2412{2413 struct scsi_pointer *scsi_pointer = arm_scsi_pointer(SCpnt);2414 void (*done)(struct scsi_cmnd *) = scsi_done;2415 AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;2416 2417#if (DEBUG & DEBUG_NO_WRITE)2418 if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) {2419 printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n",2420 host->host->host_no, '0' + SCpnt->device->id);2421 set_host_byte(SCpnt, DID_NO_CONNECT);2422 done(SCpnt);2423 return 0;2424 }2425#endif2426 2427 SCpnt->host_scribble = NULL;2428 SCpnt->result = 0;2429 scsi_pointer->phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]);2430 scsi_pointer->sent_command = 0;2431 scsi_pointer->scsi_xferred = 0;2432 2433 init_SCp(SCpnt);2434 2435 host->stats.queues += 1;2436 2437 {2438 unsigned long flags;2439 2440 if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) {2441 set_host_byte(SCpnt, DID_ERROR);2442 done(SCpnt);2443 return 0;2444 }2445 local_irq_save(flags);2446 if (host->scsi.phase == PHASE_IDLE)2447 acornscsi_kick(host);2448 local_irq_restore(flags);2449 }2450 return 0;2451}2452 2453static DEF_SCSI_QCMD(acornscsi_queuecmd)2454 2455enum res_abort { res_not_running, res_success, res_success_clear, res_snooze };2456 2457/*2458 * Prototype: enum res acornscsi_do_abort(struct scsi_cmnd *SCpnt)2459 * Purpose : abort a command on this host2460 * Params : SCpnt - command to abort2461 * Returns : our abort status2462 */2463static enum res_abort acornscsi_do_abort(AS_Host *host, struct scsi_cmnd *SCpnt)2464{2465 enum res_abort res = res_not_running;2466 2467 if (queue_remove_cmd(&host->queues.issue, SCpnt)) {2468 /*2469 * The command was on the issue queue, and has not been2470 * issued yet. We can remove the command from the queue,2471 * and acknowledge the abort. Neither the devices nor the2472 * interface know about the command.2473 */2474//#if (DEBUG & DEBUG_ABORT)2475 printk("on issue queue ");2476//#endif2477 res = res_success;2478 } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) {2479 /*2480 * The command was on the disconnected queue. Simply2481 * acknowledge the abort condition, and when the target2482 * reconnects, we will give it an ABORT message. The2483 * target should then disconnect, and we will clear2484 * the busylun bit.2485 */2486//#if (DEBUG & DEBUG_ABORT)2487 printk("on disconnected queue ");2488//#endif2489 res = res_success;2490 } else if (host->SCpnt == SCpnt) {2491 unsigned long flags;2492 2493//#if (DEBUG & DEBUG_ABORT)2494 printk("executing ");2495//#endif2496 2497 local_irq_save(flags);2498 switch (host->scsi.phase) {2499 /*2500 * If the interface is idle, and the command is 'disconnectable',2501 * then it is the same as on the disconnected queue. We simply2502 * remove all traces of the command. When the target reconnects,2503 * we will give it an ABORT message since the command could not2504 * be found. When the target finally disconnects, we will clear2505 * the busylun bit.2506 */2507 case PHASE_IDLE:2508 if (host->scsi.disconnectable) {2509 host->scsi.disconnectable = 0;2510 host->SCpnt = NULL;2511 res = res_success;2512 }2513 break;2514 2515 /*2516 * If the command has connected and done nothing further,2517 * simply force a disconnect. We also need to clear the2518 * busylun bit.2519 */2520 case PHASE_CONNECTED:2521 sbic_arm_write(host, SBIC_CMND, CMND_DISCONNECT);2522 host->SCpnt = NULL;2523 res = res_success_clear;2524 break;2525 2526 default:2527 acornscsi_abortcmd(host);2528 res = res_snooze;2529 }2530 local_irq_restore(flags);2531 } else if (host->origSCpnt == SCpnt) {2532 /*2533 * The command will be executed next, but a command2534 * is currently using the interface. This is similar to2535 * being on the issue queue, except the busylun bit has2536 * been set.2537 */2538 host->origSCpnt = NULL;2539//#if (DEBUG & DEBUG_ABORT)2540 printk("waiting for execution ");2541//#endif2542 res = res_success_clear;2543 } else2544 printk("unknown ");2545 2546 return res;2547}2548 2549/*2550 * Prototype: int acornscsi_abort(struct scsi_cmnd *SCpnt)2551 * Purpose : abort a command on this host2552 * Params : SCpnt - command to abort2553 * Returns : one of SCSI_ABORT_ macros2554 */2555static int acornscsi_abort(struct scsi_cmnd *SCpnt)2556{2557 AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata;2558 int result;2559 2560 host->stats.aborts += 1;2561 2562#if (DEBUG & DEBUG_ABORT)2563 {2564 int asr, ssr;2565 asr = sbic_arm_read(host, SBIC_ASR);2566 ssr = sbic_arm_read(host, SBIC_SSR);2567 2568 printk(KERN_WARNING "acornscsi_abort: ");2569 print_sbic_status(asr, ssr, host->scsi.phase);2570 acornscsi_dumplog(host, SCpnt->device->id);2571 }2572#endif2573 2574 printk("scsi%d: ", host->host->host_no);2575 2576 switch (acornscsi_do_abort(host, SCpnt)) {2577 /*2578 * We managed to find the command and cleared it out.2579 * We do not expect the command to be executing on the2580 * target, but we have set the busylun bit.2581 */2582 case res_success_clear:2583//#if (DEBUG & DEBUG_ABORT)2584 printk("clear ");2585//#endif2586 clear_bit(SCpnt->device->id * 8 +2587 (u8)(SCpnt->device->lun & 0x7), host->busyluns);2588 fallthrough;2589 2590 /*2591 * We found the command, and cleared it out. Either2592 * the command is still known to be executing on the2593 * target, or the busylun bit is not set.2594 */2595 case res_success:2596//#if (DEBUG & DEBUG_ABORT)2597 printk("success\n");2598//#endif2599 result = SUCCESS;2600 break;2601 2602 /*2603 * We did find the command, but unfortunately we couldn't2604 * unhook it from ourselves. Wait some more, and if it2605 * still doesn't complete, reset the interface.2606 */2607 case res_snooze:2608//#if (DEBUG & DEBUG_ABORT)2609 printk("snooze\n");2610//#endif2611 result = FAILED;2612 break;2613 2614 /*2615 * The command could not be found (either because it completed,2616 * or it got dropped.2617 */2618 default:2619 case res_not_running:2620 acornscsi_dumplog(host, SCpnt->device->id);2621 result = FAILED;2622//#if (DEBUG & DEBUG_ABORT)2623 printk("not running\n");2624//#endif2625 break;2626 }2627 2628 return result;2629}2630 2631/*2632 * Prototype: int acornscsi_reset(struct scsi_cmnd *SCpnt)2633 * Purpose : reset a command on this host/reset this host2634 * Params : SCpnt - command causing reset2635 * Returns : one of SCSI_RESET_ macros2636 */2637static int acornscsi_host_reset(struct scsi_cmnd *SCpnt)2638{2639 AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata;2640 struct scsi_cmnd *SCptr;2641 2642 host->stats.resets += 1;2643 2644#if (DEBUG & DEBUG_RESET)2645 {2646 int asr, ssr, devidx;2647 2648 asr = sbic_arm_read(host, SBIC_ASR);2649 ssr = sbic_arm_read(host, SBIC_SSR);2650 2651 printk(KERN_WARNING "acornscsi_reset: ");2652 print_sbic_status(asr, ssr, host->scsi.phase);2653 for (devidx = 0; devidx < 9; devidx++)2654 acornscsi_dumplog(host, devidx);2655 }2656#endif2657 2658 acornscsi_dma_stop(host);2659 2660 /*2661 * do hard reset. This resets all devices on this host, and so we2662 * must set the reset status on all commands.2663 */2664 acornscsi_resetcard(host);2665 2666 while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL)2667 ;2668 2669 return SUCCESS;2670}2671 2672/*==============================================================================================2673 * initialisation & miscellaneous support2674 */2675 2676/*2677 * Function: char *acornscsi_info(struct Scsi_Host *host)2678 * Purpose : return a string describing this interface2679 * Params : host - host to give information on2680 * Returns : a constant string2681 */2682static const char *acornscsi_info(struct Scsi_Host *host)2683{2684 static char string[100], *p;2685 2686 p = string;2687 2688 p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d"2689#ifdef CONFIG_SCSI_ACORNSCSI_SYNC2690 " SYNC"2691#endif2692#if (DEBUG & DEBUG_NO_WRITE)2693 " NOWRITE (" __stringify(NO_WRITE) ")"2694#endif2695 , host->hostt->name, host->io_port, host->irq,2696 VER_MAJOR, VER_MINOR, VER_PATCH);2697 return string;2698}2699 2700static int acornscsi_show_info(struct seq_file *m, struct Scsi_Host *instance)2701{2702 int devidx;2703 struct scsi_device *scd;2704 AS_Host *host;2705 2706 host = (AS_Host *)instance->hostdata;2707 2708 seq_printf(m, "AcornSCSI driver v%d.%d.%d"2709#ifdef CONFIG_SCSI_ACORNSCSI_SYNC2710 " SYNC"2711#endif2712#if (DEBUG & DEBUG_NO_WRITE)2713 " NOWRITE (" __stringify(NO_WRITE) ")"2714#endif2715 "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH);2716 2717 seq_printf(m, "SBIC: WD33C93A Address: %p IRQ : %d\n",2718 host->base + SBIC_REGIDX, host->scsi.irq);2719#ifdef USE_DMAC2720 seq_printf(m, "DMAC: uPC71071 Address: %p IRQ : %d\n\n",2721 host->base + DMAC_OFFSET, host->scsi.irq);2722#endif2723 2724 seq_printf(m, "Statistics:\n"2725 "Queued commands: %-10u Issued commands: %-10u\n"2726 "Done commands : %-10u Reads : %-10u\n"2727 "Writes : %-10u Others : %-10u\n"2728 "Disconnects : %-10u Aborts : %-10u\n"2729 "Resets : %-10u\n\nLast phases:",2730 host->stats.queues, host->stats.removes,2731 host->stats.fins, host->stats.reads,2732 host->stats.writes, host->stats.miscs,2733 host->stats.disconnects, host->stats.aborts,2734 host->stats.resets);2735 2736 for (devidx = 0; devidx < 9; devidx ++) {2737 unsigned int statptr, prev;2738 2739 seq_printf(m, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx));2740 statptr = host->status_ptr[devidx] - 10;2741 2742 if ((signed int)statptr < 0)2743 statptr += STATUS_BUFFER_SIZE;2744 2745 prev = host->status[devidx][statptr].when;2746 2747 for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) {2748 if (host->status[devidx][statptr].when) {2749 seq_printf(m, "%c%02X:%02X+%2ld",2750 host->status[devidx][statptr].irq ? '-' : ' ',2751 host->status[devidx][statptr].ph,2752 host->status[devidx][statptr].ssr,2753 (host->status[devidx][statptr].when - prev) < 100 ?2754 (host->status[devidx][statptr].when - prev) : 99);2755 prev = host->status[devidx][statptr].when;2756 }2757 }2758 }2759 2760 seq_printf(m, "\nAttached devices:\n");2761 2762 shost_for_each_device(scd, instance) {2763 seq_printf(m, "Device/Lun TaggedQ Sync\n");2764 seq_printf(m, " %d/%llu ", scd->id, scd->lun);2765 if (scd->tagged_supported)2766 seq_printf(m, "%3sabled ",2767 scd->simple_tags ? "en" : "dis");2768 else2769 seq_printf(m, "unsupported ");2770 2771 if (host->device[scd->id].sync_xfer & 15)2772 seq_printf(m, "offset %d, %d ns\n",2773 host->device[scd->id].sync_xfer & 15,2774 acornscsi_getperiod(host->device[scd->id].sync_xfer));2775 else2776 seq_printf(m, "async\n");2777 2778 }2779 return 0;2780}2781 2782static const struct scsi_host_template acornscsi_template = {2783 .module = THIS_MODULE,2784 .show_info = acornscsi_show_info,2785 .name = "AcornSCSI",2786 .info = acornscsi_info,2787 .queuecommand = acornscsi_queuecmd,2788 .eh_abort_handler = acornscsi_abort,2789 .eh_host_reset_handler = acornscsi_host_reset,2790 .can_queue = 16,2791 .this_id = 7,2792 .sg_tablesize = SG_ALL,2793 .cmd_per_lun = 2,2794 .dma_boundary = PAGE_SIZE - 1,2795 .proc_name = "acornscsi",2796 .cmd_size = sizeof(struct arm_cmd_priv),2797};2798 2799static int acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id)2800{2801 struct Scsi_Host *host;2802 AS_Host *ashost;2803 int ret;2804 2805 ret = ecard_request_resources(ec);2806 if (ret)2807 goto out;2808 2809 host = scsi_host_alloc(&acornscsi_template, sizeof(AS_Host));2810 if (!host) {2811 ret = -ENOMEM;2812 goto out_release;2813 }2814 2815 ashost = (AS_Host *)host->hostdata;2816 2817 ashost->base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);2818 ashost->fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);2819 if (!ashost->base || !ashost->fast) {2820 ret = -ENOMEM;2821 goto out_put;2822 }2823 2824 host->irq = ec->irq;2825 ashost->host = host;2826 ashost->scsi.irq = host->irq;2827 2828 ec->irqaddr = ashost->fast + INT_REG;2829 ec->irqmask = 0x0a;2830 2831 ret = request_irq(host->irq, acornscsi_intr, 0, "acornscsi", ashost);2832 if (ret) {2833 printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n",2834 host->host_no, ashost->scsi.irq, ret);2835 goto out_put;2836 }2837 2838 memset(&ashost->stats, 0, sizeof (ashost->stats));2839 queue_initialise(&ashost->queues.issue);2840 queue_initialise(&ashost->queues.disconnected);2841 msgqueue_initialise(&ashost->scsi.msgs);2842 2843 acornscsi_resetcard(ashost);2844 2845 ret = scsi_add_host(host, &ec->dev);2846 if (ret)2847 goto out_irq;2848 2849 scsi_scan_host(host);2850 goto out;2851 2852 out_irq:2853 free_irq(host->irq, ashost);2854 msgqueue_free(&ashost->scsi.msgs);2855 queue_free(&ashost->queues.disconnected);2856 queue_free(&ashost->queues.issue);2857 out_put:2858 ecardm_iounmap(ec, ashost->fast);2859 ecardm_iounmap(ec, ashost->base);2860 scsi_host_put(host);2861 out_release:2862 ecard_release_resources(ec);2863 out:2864 return ret;2865}2866 2867static void acornscsi_remove(struct expansion_card *ec)2868{2869 struct Scsi_Host *host = ecard_get_drvdata(ec);2870 AS_Host *ashost = (AS_Host *)host->hostdata;2871 2872 ecard_set_drvdata(ec, NULL);2873 scsi_remove_host(host);2874 2875 /*2876 * Put card into RESET state2877 */2878 writeb(0x80, ashost->fast + PAGE_REG);2879 2880 free_irq(host->irq, ashost);2881 2882 msgqueue_free(&ashost->scsi.msgs);2883 queue_free(&ashost->queues.disconnected);2884 queue_free(&ashost->queues.issue);2885 ecardm_iounmap(ec, ashost->fast);2886 ecardm_iounmap(ec, ashost->base);2887 scsi_host_put(host);2888 ecard_release_resources(ec);2889}2890 2891static const struct ecard_id acornscsi_cids[] = {2892 { MANU_ACORN, PROD_ACORN_SCSI },2893 { 0xffff, 0xffff },2894};2895 2896static struct ecard_driver acornscsi_driver = {2897 .probe = acornscsi_probe,2898 .remove = acornscsi_remove,2899 .id_table = acornscsi_cids,2900 .drv = {2901 .name = "acornscsi",2902 },2903};2904 2905static int __init acornscsi_init(void)2906{2907 return ecard_register_driver(&acornscsi_driver);2908}2909 2910static void __exit acornscsi_exit(void)2911{2912 ecard_remove_driver(&acornscsi_driver);2913}2914 2915module_init(acornscsi_init);2916module_exit(acornscsi_exit);2917 2918MODULE_AUTHOR("Russell King");2919MODULE_DESCRIPTION("AcornSCSI driver");2920MODULE_LICENSE("GPL");2921