18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/acorn/scsi/acornscsi.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Acorn SCSI 3 driver 68c2ecf20Sopenharmony_ci * By R.M.King. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Abandoned using the Select and Transfer command since there were 98c2ecf20Sopenharmony_ci * some nasty races between our software and the target devices that 108c2ecf20Sopenharmony_ci * were not easy to solve, and the device errata had a lot of entries 118c2ecf20Sopenharmony_ci * for this command, some of them quite nasty... 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Changelog: 148c2ecf20Sopenharmony_ci * 26-Sep-1997 RMK Re-jigged to use the queue module. 158c2ecf20Sopenharmony_ci * Re-coded state machine to be based on driver 168c2ecf20Sopenharmony_ci * state not scsi state. Should be easier to debug. 178c2ecf20Sopenharmony_ci * Added acornscsi_release to clean up properly. 188c2ecf20Sopenharmony_ci * Updated proc/scsi reporting. 198c2ecf20Sopenharmony_ci * 05-Oct-1997 RMK Implemented writing to SCSI devices. 208c2ecf20Sopenharmony_ci * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/ 218c2ecf20Sopenharmony_ci * reconnect race condition causing a warning message. 228c2ecf20Sopenharmony_ci * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. 238c2ecf20Sopenharmony_ci * 15-Oct-1997 RMK Improved handling of commands. 248c2ecf20Sopenharmony_ci * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. 258c2ecf20Sopenharmony_ci * 13-Dec-1998 RMK Better abort code and command handling. Extra state 268c2ecf20Sopenharmony_ci * transitions added to allow dodgy devices to work. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#define DEBUG_NO_WRITE 1 298c2ecf20Sopenharmony_ci#define DEBUG_QUEUES 2 308c2ecf20Sopenharmony_ci#define DEBUG_DMA 4 318c2ecf20Sopenharmony_ci#define DEBUG_ABORT 8 328c2ecf20Sopenharmony_ci#define DEBUG_DISCON 16 338c2ecf20Sopenharmony_ci#define DEBUG_CONNECT 32 348c2ecf20Sopenharmony_ci#define DEBUG_PHASES 64 358c2ecf20Sopenharmony_ci#define DEBUG_WRITE 128 368c2ecf20Sopenharmony_ci#define DEBUG_LINK 256 378c2ecf20Sopenharmony_ci#define DEBUG_MESSAGES 512 388c2ecf20Sopenharmony_ci#define DEBUG_RESET 1024 398c2ecf20Sopenharmony_ci#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ 408c2ecf20Sopenharmony_ci DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ 418c2ecf20Sopenharmony_ci DEBUG_DMA|DEBUG_QUEUES) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* DRIVER CONFIGURATION 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * SCSI-II Tagged queue support. 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * I don't have any SCSI devices that support it, so it is totally untested 488c2ecf20Sopenharmony_ci * (except to make sure that it doesn't interfere with any non-tagging 498c2ecf20Sopenharmony_ci * devices). It is not fully implemented either - what happens when a 508c2ecf20Sopenharmony_ci * tagging device reconnects??? 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * You can tell if you have a device that supports tagged queueing my 538c2ecf20Sopenharmony_ci * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported 548c2ecf20Sopenharmony_ci * as '2 TAG'. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config 578c2ecf20Sopenharmony_ci * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug, 588c2ecf20Sopenharmony_ci * comment out the undef. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * SCSI-II Synchronous transfer support. 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * Tried and tested... 658c2ecf20Sopenharmony_ci * 668c2ecf20Sopenharmony_ci * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max) 678c2ecf20Sopenharmony_ci * SDTR_PERIOD - period of REQ signal (min=125, max=1020) 688c2ecf20Sopenharmony_ci * DEFAULT_PERIOD - default REQ period. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci#define SDTR_SIZE 12 718c2ecf20Sopenharmony_ci#define SDTR_PERIOD 125 728c2ecf20Sopenharmony_ci#define DEFAULT_PERIOD 500 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * Debugging information 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * DEBUG - bit mask from list above 788c2ecf20Sopenharmony_ci * DEBUG_TARGET - is defined to the target number if you want to debug 798c2ecf20Sopenharmony_ci * a specific target. [only recon/write/dma]. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE) 828c2ecf20Sopenharmony_ci/* only allow writing to SCSI device 0 */ 838c2ecf20Sopenharmony_ci#define NO_WRITE 0xFE 848c2ecf20Sopenharmony_ci/*#define DEBUG_TARGET 2*/ 858c2ecf20Sopenharmony_ci/* 868c2ecf20Sopenharmony_ci * Select timeout time (in 10ms units) 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * This is the timeout used between the start of selection and the WD33C93 898c2ecf20Sopenharmony_ci * chip deciding that the device isn't responding. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci#define TIMEOUT_TIME 10 928c2ecf20Sopenharmony_ci/* 938c2ecf20Sopenharmony_ci * Define this if you want to have verbose explanation of SCSI 948c2ecf20Sopenharmony_ci * status/messages. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci#undef CONFIG_ACORNSCSI_CONSTANTS 978c2ecf20Sopenharmony_ci/* 988c2ecf20Sopenharmony_ci * Define this if you want to use the on board DMAC [don't remove this option] 998c2ecf20Sopenharmony_ci * If not set, then use PIO mode (not currently supported). 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci#define USE_DMAC 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * ==================================================================================== 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#ifdef DEBUG_TARGET 1088c2ecf20Sopenharmony_ci#define DBG(cmd,xxx...) \ 1098c2ecf20Sopenharmony_ci if (cmd->device->id == DEBUG_TARGET) { \ 1108c2ecf20Sopenharmony_ci xxx; \ 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci#else 1138c2ecf20Sopenharmony_ci#define DBG(cmd,xxx...) xxx 1148c2ecf20Sopenharmony_ci#endif 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#include <linux/module.h> 1178c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1188c2ecf20Sopenharmony_ci#include <linux/string.h> 1198c2ecf20Sopenharmony_ci#include <linux/signal.h> 1208c2ecf20Sopenharmony_ci#include <linux/errno.h> 1218c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 1228c2ecf20Sopenharmony_ci#include <linux/ioport.h> 1238c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 1248c2ecf20Sopenharmony_ci#include <linux/delay.h> 1258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 1268c2ecf20Sopenharmony_ci#include <linux/init.h> 1278c2ecf20Sopenharmony_ci#include <linux/bitops.h> 1288c2ecf20Sopenharmony_ci#include <linux/stringify.h> 1298c2ecf20Sopenharmony_ci#include <linux/io.h> 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#include <asm/ecard.h> 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#include "../scsi.h" 1348c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h> 1358c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 1368c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_spi.h> 1378c2ecf20Sopenharmony_ci#include "acornscsi.h" 1388c2ecf20Sopenharmony_ci#include "msgqueue.h" 1398c2ecf20Sopenharmony_ci#include "scsi.h" 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#include <scsi/scsicam.h> 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define VER_MAJOR 2 1448c2ecf20Sopenharmony_ci#define VER_MINOR 0 1458c2ecf20Sopenharmony_ci#define VER_PATCH 6 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#ifndef ABORT_TAG 1488c2ecf20Sopenharmony_ci#define ABORT_TAG 0xd 1498c2ecf20Sopenharmony_ci#else 1508c2ecf20Sopenharmony_ci#error "Yippee! ABORT TAG is now defined! Remove this error!" 1518c2ecf20Sopenharmony_ci#endif 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#ifdef USE_DMAC 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * DMAC setup parameters 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP) 1588c2ecf20Sopenharmony_ci#define INIT_DEVCON1 (DEVCON1_BHLD) 1598c2ecf20Sopenharmony_ci#define DMAC_READ (MODECON_READ) 1608c2ecf20Sopenharmony_ci#define DMAC_WRITE (MODECON_WRITE) 1618c2ecf20Sopenharmony_ci#define INIT_SBICDMA (CTRL_DMABURST) 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#define scsi_xferred have_data_in 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * Size of on-board DMA buffer 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci#define DMAC_BUFFER_SIZE 65536 1698c2ecf20Sopenharmony_ci#endif 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define STATUS_BUFFER_TO_PRINT 24 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciunsigned int sdtr_period = SDTR_PERIOD; 1748c2ecf20Sopenharmony_ciunsigned int sdtr_size = SDTR_SIZE; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, 1778c2ecf20Sopenharmony_ci unsigned int result); 1788c2ecf20Sopenharmony_cistatic int acornscsi_reconnect_finish(AS_Host *host); 1798c2ecf20Sopenharmony_cistatic void acornscsi_dma_cleanup(AS_Host *host); 1808c2ecf20Sopenharmony_cistatic void acornscsi_abortcmd(AS_Host *host, unsigned char tag); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* ==================================================================================== 1838c2ecf20Sopenharmony_ci * Miscellaneous 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* Offsets from MEMC base */ 1878c2ecf20Sopenharmony_ci#define SBIC_REGIDX 0x2000 1888c2ecf20Sopenharmony_ci#define SBIC_REGVAL 0x2004 1898c2ecf20Sopenharmony_ci#define DMAC_OFFSET 0x3000 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci/* Offsets from FAST IOC base */ 1928c2ecf20Sopenharmony_ci#define INT_REG 0x2000 1938c2ecf20Sopenharmony_ci#define PAGE_REG 0x3000 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic inline void sbic_arm_write(AS_Host *host, unsigned int reg, unsigned int value) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci writeb(reg, host->base + SBIC_REGIDX); 1988c2ecf20Sopenharmony_ci writeb(value, host->base + SBIC_REGVAL); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline int sbic_arm_read(AS_Host *host, unsigned int reg) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci if(reg == SBIC_ASR) 2048c2ecf20Sopenharmony_ci return readl(host->base + SBIC_REGIDX) & 255; 2058c2ecf20Sopenharmony_ci writeb(reg, host->base + SBIC_REGIDX); 2068c2ecf20Sopenharmony_ci return readl(host->base + SBIC_REGVAL) & 255; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci#define sbic_arm_writenext(host, val) writeb((val), (host)->base + SBIC_REGVAL) 2108c2ecf20Sopenharmony_ci#define sbic_arm_readnext(host) readb((host)->base + SBIC_REGVAL) 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci#ifdef USE_DMAC 2138c2ecf20Sopenharmony_ci#define dmac_read(host,reg) \ 2148c2ecf20Sopenharmony_ci readb((host)->base + DMAC_OFFSET + ((reg) << 2)) 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci#define dmac_write(host,reg,value) \ 2178c2ecf20Sopenharmony_ci ({ writeb((value), (host)->base + DMAC_OFFSET + ((reg) << 2)); }) 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci#define dmac_clearintr(host) writeb(0, (host)->fast + INT_REG) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic inline unsigned int dmac_address(AS_Host *host) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci return dmac_read(host, DMAC_TXADRHI) << 16 | 2248c2ecf20Sopenharmony_ci dmac_read(host, DMAC_TXADRMD) << 8 | 2258c2ecf20Sopenharmony_ci dmac_read(host, DMAC_TXADRLO); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic 2298c2ecf20Sopenharmony_civoid acornscsi_dumpdma(AS_Host *host, char *where) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci unsigned int mode, addr, len; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci mode = dmac_read(host, DMAC_MODECON); 2348c2ecf20Sopenharmony_ci addr = dmac_address(host); 2358c2ecf20Sopenharmony_ci len = dmac_read(host, DMAC_TXCNTHI) << 8 | 2368c2ecf20Sopenharmony_ci dmac_read(host, DMAC_TXCNTLO); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", 2398c2ecf20Sopenharmony_ci host->host->host_no, where, 2408c2ecf20Sopenharmony_ci mode, addr, (len + 1) & 0xffff, 2418c2ecf20Sopenharmony_ci dmac_read(host, DMAC_MASKREG)); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci printk("DMA @%06x, ", host->dma.start_addr); 2448c2ecf20Sopenharmony_ci printk("BH @%p +%04x, ", host->scsi.SCp.ptr, 2458c2ecf20Sopenharmony_ci host->scsi.SCp.this_residual); 2468c2ecf20Sopenharmony_ci printk("DT @+%04x ST @+%04x", host->dma.transferred, 2478c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred); 2488c2ecf20Sopenharmony_ci printk("\n"); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci#endif 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic 2538c2ecf20Sopenharmony_ciunsigned long acornscsi_sbic_xfcount(AS_Host *host) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci unsigned long length; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci length = sbic_arm_read(host, SBIC_TRANSCNTH) << 16; 2588c2ecf20Sopenharmony_ci length |= sbic_arm_readnext(host) << 8; 2598c2ecf20Sopenharmony_ci length |= sbic_arm_readnext(host); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return length; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic int 2658c2ecf20Sopenharmony_ciacornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci int asr; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci do { 2708c2ecf20Sopenharmony_ci asr = sbic_arm_read(host, SBIC_ASR); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if ((asr & stat_mask) == stat) 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci udelay(1); 2768c2ecf20Sopenharmony_ci } while (--timeout); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci printk("scsi%d: timeout while %s\n", host->host->host_no, msg); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return -1; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic 2848c2ecf20Sopenharmony_ciint acornscsi_sbic_issuecmd(AS_Host *host, int command) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) 2878c2ecf20Sopenharmony_ci return -1; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CMND, command); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void 2958c2ecf20Sopenharmony_ciacornscsi_csdelay(unsigned int cs) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci unsigned long target_jiffies, flags; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci target_jiffies = jiffies + 1 + cs * HZ / 100; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci local_save_flags(flags); 3028c2ecf20Sopenharmony_ci local_irq_enable(); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci while (time_before(jiffies, target_jiffies)) barrier(); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci local_irq_restore(flags); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic 3108c2ecf20Sopenharmony_civoid acornscsi_resetcard(AS_Host *host) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci unsigned int i, timeout; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* assert reset line */ 3158c2ecf20Sopenharmony_ci host->card.page_reg = 0x80; 3168c2ecf20Sopenharmony_ci writeb(host->card.page_reg, host->fast + PAGE_REG); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* wait 3 cs. SCSI standard says 25ms. */ 3198c2ecf20Sopenharmony_ci acornscsi_csdelay(3); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci host->card.page_reg = 0; 3228c2ecf20Sopenharmony_ci writeb(host->card.page_reg, host->fast + PAGE_REG); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* 3258c2ecf20Sopenharmony_ci * Should get a reset from the card 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci timeout = 1000; 3288c2ecf20Sopenharmony_ci do { 3298c2ecf20Sopenharmony_ci if (readb(host->fast + INT_REG) & 8) 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci udelay(1); 3328c2ecf20Sopenharmony_ci } while (--timeout); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (timeout == 0) 3358c2ecf20Sopenharmony_ci printk("scsi%d: timeout while resetting card\n", 3368c2ecf20Sopenharmony_ci host->host->host_no); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci sbic_arm_read(host, SBIC_ASR); 3398c2ecf20Sopenharmony_ci sbic_arm_read(host, SBIC_SSR); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* setup sbic - WD33C93A */ 3428c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_OWNID, OWNID_EAF | host->host->this_id); 3438c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CMND, CMND_RESET); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* 3468c2ecf20Sopenharmony_ci * Command should cause a reset interrupt 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci timeout = 1000; 3498c2ecf20Sopenharmony_ci do { 3508c2ecf20Sopenharmony_ci if (readb(host->fast + INT_REG) & 8) 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci udelay(1); 3538c2ecf20Sopenharmony_ci } while (--timeout); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (timeout == 0) 3568c2ecf20Sopenharmony_ci printk("scsi%d: timeout while resetting card\n", 3578c2ecf20Sopenharmony_ci host->host->host_no); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci sbic_arm_read(host, SBIC_ASR); 3608c2ecf20Sopenharmony_ci if (sbic_arm_read(host, SBIC_SSR) != 0x01) 3618c2ecf20Sopenharmony_ci printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", 3628c2ecf20Sopenharmony_ci host->host->host_no); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI); 3658c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_TIMEOUT, TIMEOUT_TIME); 3668c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA); 3678c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci host->card.page_reg = 0x40; 3708c2ecf20Sopenharmony_ci writeb(host->card.page_reg, host->fast + PAGE_REG); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* setup dmac - uPC71071 */ 3738c2ecf20Sopenharmony_ci dmac_write(host, DMAC_INIT, 0); 3748c2ecf20Sopenharmony_ci#ifdef USE_DMAC 3758c2ecf20Sopenharmony_ci dmac_write(host, DMAC_INIT, INIT_8BIT); 3768c2ecf20Sopenharmony_ci dmac_write(host, DMAC_CHANNEL, CHANNEL_0); 3778c2ecf20Sopenharmony_ci dmac_write(host, DMAC_DEVCON0, INIT_DEVCON0); 3788c2ecf20Sopenharmony_ci dmac_write(host, DMAC_DEVCON1, INIT_DEVCON1); 3798c2ecf20Sopenharmony_ci#endif 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci host->SCpnt = NULL; 3828c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_IDLE; 3838c2ecf20Sopenharmony_ci host->scsi.disconnectable = 0; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci memset(host->busyluns, 0, sizeof(host->busyluns)); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 3888c2ecf20Sopenharmony_ci host->device[i].sync_state = SYNC_NEGOCIATE; 3898c2ecf20Sopenharmony_ci host->device[i].disconnect_ok = 1; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* wait 25 cs. SCSI standard says 250ms. */ 3938c2ecf20Sopenharmony_ci acornscsi_csdelay(25); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/*============================================================================================= 3978c2ecf20Sopenharmony_ci * Utility routines (eg. debug) 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci#ifdef CONFIG_ACORNSCSI_CONSTANTS 4008c2ecf20Sopenharmony_cistatic char *acornscsi_interrupttype[] = { 4018c2ecf20Sopenharmony_ci "rst", "suc", "p/a", "3", 4028c2ecf20Sopenharmony_ci "term", "5", "6", "7", 4038c2ecf20Sopenharmony_ci "serv", "9", "a", "b", 4048c2ecf20Sopenharmony_ci "c", "d", "e", "f" 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic signed char acornscsi_map[] = { 4088c2ecf20Sopenharmony_ci 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4098c2ecf20Sopenharmony_ci -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, 4108c2ecf20Sopenharmony_ci 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, 4118c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4128c2ecf20Sopenharmony_ci 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11, 4138c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4148c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4158c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4168c2ecf20Sopenharmony_ci 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, 4178c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4188c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4198c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4208c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4218c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4228c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4238c2ecf20Sopenharmony_ci -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic char *acornscsi_interruptcode[] = { 4278c2ecf20Sopenharmony_ci /* 0 */ 4288c2ecf20Sopenharmony_ci "reset - normal mode", /* 00 */ 4298c2ecf20Sopenharmony_ci "reset - advanced mode", /* 01 */ 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* 2 */ 4328c2ecf20Sopenharmony_ci "sel", /* 11 */ 4338c2ecf20Sopenharmony_ci "sel+xfer", /* 16 */ 4348c2ecf20Sopenharmony_ci "data-out", /* 18 */ 4358c2ecf20Sopenharmony_ci "data-in", /* 19 */ 4368c2ecf20Sopenharmony_ci "cmd", /* 1A */ 4378c2ecf20Sopenharmony_ci "stat", /* 1B */ 4388c2ecf20Sopenharmony_ci "??-out", /* 1C */ 4398c2ecf20Sopenharmony_ci "??-in", /* 1D */ 4408c2ecf20Sopenharmony_ci "msg-out", /* 1E */ 4418c2ecf20Sopenharmony_ci "msg-in", /* 1F */ 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* 12 */ 4448c2ecf20Sopenharmony_ci "/ACK asserted", /* 20 */ 4458c2ecf20Sopenharmony_ci "save-data-ptr", /* 21 */ 4468c2ecf20Sopenharmony_ci "{re}sel", /* 22 */ 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 15 */ 4498c2ecf20Sopenharmony_ci "inv cmd", /* 40 */ 4508c2ecf20Sopenharmony_ci "unexpected disconnect", /* 41 */ 4518c2ecf20Sopenharmony_ci "sel timeout", /* 42 */ 4528c2ecf20Sopenharmony_ci "P err", /* 43 */ 4538c2ecf20Sopenharmony_ci "P err+ATN", /* 44 */ 4548c2ecf20Sopenharmony_ci "bad status byte", /* 47 */ 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 21 */ 4578c2ecf20Sopenharmony_ci "resel, no id", /* 80 */ 4588c2ecf20Sopenharmony_ci "resel", /* 81 */ 4598c2ecf20Sopenharmony_ci "discon", /* 85 */ 4608c2ecf20Sopenharmony_ci}; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic 4638c2ecf20Sopenharmony_civoid print_scsi_status(unsigned int ssr) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci if (acornscsi_map[ssr] != -1) 4668c2ecf20Sopenharmony_ci printk("%s:%s", 4678c2ecf20Sopenharmony_ci acornscsi_interrupttype[(ssr >> 4)], 4688c2ecf20Sopenharmony_ci acornscsi_interruptcode[acornscsi_map[ssr]]); 4698c2ecf20Sopenharmony_ci else 4708c2ecf20Sopenharmony_ci printk("%X:%X", ssr >> 4, ssr & 0x0f); 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci#endif 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic 4758c2ecf20Sopenharmony_civoid print_sbic_status(int asr, int ssr, int cmdphase) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci#ifdef CONFIG_ACORNSCSI_CONSTANTS 4788c2ecf20Sopenharmony_ci printk("sbic: %c%c%c%c%c%c ", 4798c2ecf20Sopenharmony_ci asr & ASR_INT ? 'I' : 'i', 4808c2ecf20Sopenharmony_ci asr & ASR_LCI ? 'L' : 'l', 4818c2ecf20Sopenharmony_ci asr & ASR_BSY ? 'B' : 'b', 4828c2ecf20Sopenharmony_ci asr & ASR_CIP ? 'C' : 'c', 4838c2ecf20Sopenharmony_ci asr & ASR_PE ? 'P' : 'p', 4848c2ecf20Sopenharmony_ci asr & ASR_DBR ? 'D' : 'd'); 4858c2ecf20Sopenharmony_ci printk("scsi: "); 4868c2ecf20Sopenharmony_ci print_scsi_status(ssr); 4878c2ecf20Sopenharmony_ci printk(" ph %02X\n", cmdphase); 4888c2ecf20Sopenharmony_ci#else 4898c2ecf20Sopenharmony_ci printk("sbic: %02X scsi: %X:%X ph: %02X\n", 4908c2ecf20Sopenharmony_ci asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase); 4918c2ecf20Sopenharmony_ci#endif 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void 4958c2ecf20Sopenharmony_ciacornscsi_dumplogline(AS_Host *host, int target, int line) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci unsigned long prev; 4988c2ecf20Sopenharmony_ci signed int ptr; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; 5018c2ecf20Sopenharmony_ci if (ptr < 0) 5028c2ecf20Sopenharmony_ci ptr += STATUS_BUFFER_SIZE; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci printk("%c: %3s:", target == 8 ? 'H' : '0' + target, 5058c2ecf20Sopenharmony_ci line == 0 ? "ph" : line == 1 ? "ssr" : "int"); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci prev = host->status[target][ptr].when; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { 5108c2ecf20Sopenharmony_ci unsigned long time_diff; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (!host->status[target][ptr].when) 5138c2ecf20Sopenharmony_ci continue; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci switch (line) { 5168c2ecf20Sopenharmony_ci case 0: 5178c2ecf20Sopenharmony_ci printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', 5188c2ecf20Sopenharmony_ci host->status[target][ptr].ph); 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci case 1: 5228c2ecf20Sopenharmony_ci printk(" %02X", host->status[target][ptr].ssr); 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci case 2: 5268c2ecf20Sopenharmony_ci time_diff = host->status[target][ptr].when - prev; 5278c2ecf20Sopenharmony_ci prev = host->status[target][ptr].when; 5288c2ecf20Sopenharmony_ci if (time_diff == 0) 5298c2ecf20Sopenharmony_ci printk("==^"); 5308c2ecf20Sopenharmony_ci else if (time_diff >= 100) 5318c2ecf20Sopenharmony_ci printk(" "); 5328c2ecf20Sopenharmony_ci else 5338c2ecf20Sopenharmony_ci printk(" %02ld", time_diff); 5348c2ecf20Sopenharmony_ci break; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci printk("\n"); 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic 5428c2ecf20Sopenharmony_civoid acornscsi_dumplog(AS_Host *host, int target) 5438c2ecf20Sopenharmony_ci{ 5448c2ecf20Sopenharmony_ci do { 5458c2ecf20Sopenharmony_ci acornscsi_dumplogline(host, target, 0); 5468c2ecf20Sopenharmony_ci acornscsi_dumplogline(host, target, 1); 5478c2ecf20Sopenharmony_ci acornscsi_dumplogline(host, target, 2); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (target == 8) 5508c2ecf20Sopenharmony_ci break; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci target = 8; 5538c2ecf20Sopenharmony_ci } while (1); 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic 5578c2ecf20Sopenharmony_cichar acornscsi_target(AS_Host *host) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci if (host->SCpnt) 5608c2ecf20Sopenharmony_ci return '0' + host->SCpnt->device->id; 5618c2ecf20Sopenharmony_ci return 'H'; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/* 5658c2ecf20Sopenharmony_ci * Prototype: cmdtype_t acornscsi_cmdtype(int command) 5668c2ecf20Sopenharmony_ci * Purpose : differentiate READ from WRITE from other commands 5678c2ecf20Sopenharmony_ci * Params : command - command to interpret 5688c2ecf20Sopenharmony_ci * Returns : CMD_READ - command reads data, 5698c2ecf20Sopenharmony_ci * CMD_WRITE - command writes data, 5708c2ecf20Sopenharmony_ci * CMD_MISC - everything else 5718c2ecf20Sopenharmony_ci */ 5728c2ecf20Sopenharmony_cistatic inline 5738c2ecf20Sopenharmony_cicmdtype_t acornscsi_cmdtype(int command) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci switch (command) { 5768c2ecf20Sopenharmony_ci case WRITE_6: case WRITE_10: case WRITE_12: 5778c2ecf20Sopenharmony_ci return CMD_WRITE; 5788c2ecf20Sopenharmony_ci case READ_6: case READ_10: case READ_12: 5798c2ecf20Sopenharmony_ci return CMD_READ; 5808c2ecf20Sopenharmony_ci default: 5818c2ecf20Sopenharmony_ci return CMD_MISC; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/* 5868c2ecf20Sopenharmony_ci * Prototype: int acornscsi_datadirection(int command) 5878c2ecf20Sopenharmony_ci * Purpose : differentiate between commands that have a DATA IN phase 5888c2ecf20Sopenharmony_ci * and a DATA OUT phase 5898c2ecf20Sopenharmony_ci * Params : command - command to interpret 5908c2ecf20Sopenharmony_ci * Returns : DATADIR_OUT - data out phase expected 5918c2ecf20Sopenharmony_ci * DATADIR_IN - data in phase expected 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_cistatic 5948c2ecf20Sopenharmony_cidatadir_t acornscsi_datadirection(int command) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci switch (command) { 5978c2ecf20Sopenharmony_ci case CHANGE_DEFINITION: case COMPARE: case COPY: 5988c2ecf20Sopenharmony_ci case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: 5998c2ecf20Sopenharmony_ci case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: 6008c2ecf20Sopenharmony_ci case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: 6018c2ecf20Sopenharmony_ci case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: 6028c2ecf20Sopenharmony_ci case WRITE_6: case WRITE_10: case WRITE_VERIFY: 6038c2ecf20Sopenharmony_ci case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME: 6048c2ecf20Sopenharmony_ci case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: 6058c2ecf20Sopenharmony_ci case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW: 6068c2ecf20Sopenharmony_ci case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea: 6078c2ecf20Sopenharmony_ci return DATADIR_OUT; 6088c2ecf20Sopenharmony_ci default: 6098c2ecf20Sopenharmony_ci return DATADIR_IN; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/* 6148c2ecf20Sopenharmony_ci * Purpose : provide values for synchronous transfers with 33C93. 6158c2ecf20Sopenharmony_ci * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting 6168c2ecf20Sopenharmony_ci * Modified by Russell King for 8MHz WD33C93A 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_cistatic struct sync_xfer_tbl { 6198c2ecf20Sopenharmony_ci unsigned int period_ns; 6208c2ecf20Sopenharmony_ci unsigned char reg_value; 6218c2ecf20Sopenharmony_ci} sync_xfer_table[] = { 6228c2ecf20Sopenharmony_ci { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 }, 6238c2ecf20Sopenharmony_ci { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 }, 6248c2ecf20Sopenharmony_ci { 874, 0x70 }, { 999, 0x00 }, { 0, 0 } 6258c2ecf20Sopenharmony_ci}; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci/* 6288c2ecf20Sopenharmony_ci * Prototype: int acornscsi_getperiod(unsigned char syncxfer) 6298c2ecf20Sopenharmony_ci * Purpose : period for the synchronous transfer setting 6308c2ecf20Sopenharmony_ci * Params : syncxfer SYNCXFER register value 6318c2ecf20Sopenharmony_ci * Returns : period in ns. 6328c2ecf20Sopenharmony_ci */ 6338c2ecf20Sopenharmony_cistatic 6348c2ecf20Sopenharmony_ciint acornscsi_getperiod(unsigned char syncxfer) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci int i; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci syncxfer &= 0xf0; 6398c2ecf20Sopenharmony_ci if (syncxfer == 0x10) 6408c2ecf20Sopenharmony_ci syncxfer = 0; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci for (i = 1; sync_xfer_table[i].period_ns; i++) 6438c2ecf20Sopenharmony_ci if (syncxfer == sync_xfer_table[i].reg_value) 6448c2ecf20Sopenharmony_ci return sync_xfer_table[i].period_ns; 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci} 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci/* 6498c2ecf20Sopenharmony_ci * Prototype: int round_period(unsigned int period) 6508c2ecf20Sopenharmony_ci * Purpose : return index into above table for a required REQ period 6518c2ecf20Sopenharmony_ci * Params : period - time (ns) for REQ 6528c2ecf20Sopenharmony_ci * Returns : table index 6538c2ecf20Sopenharmony_ci * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_cistatic inline 6568c2ecf20Sopenharmony_ciint round_period(unsigned int period) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci int i; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci for (i = 1; sync_xfer_table[i].period_ns; i++) { 6618c2ecf20Sopenharmony_ci if ((period <= sync_xfer_table[i].period_ns) && 6628c2ecf20Sopenharmony_ci (period > sync_xfer_table[i - 1].period_ns)) 6638c2ecf20Sopenharmony_ci return i; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci return 7; 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci/* 6698c2ecf20Sopenharmony_ci * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) 6708c2ecf20Sopenharmony_ci * Purpose : calculate value for 33c93s SYNC register 6718c2ecf20Sopenharmony_ci * Params : period - time (ns) for REQ 6728c2ecf20Sopenharmony_ci * offset - offset in bytes between REQ/ACK 6738c2ecf20Sopenharmony_ci * Returns : value for SYNC register 6748c2ecf20Sopenharmony_ci * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_cistatic 6778c2ecf20Sopenharmony_ciunsigned char __maybe_unused calc_sync_xfer(unsigned int period, 6788c2ecf20Sopenharmony_ci unsigned int offset) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci return sync_xfer_table[round_period(period)].reg_value | 6818c2ecf20Sopenharmony_ci ((offset < SDTR_SIZE) ? offset : SDTR_SIZE); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci/* ==================================================================================== 6858c2ecf20Sopenharmony_ci * Command functions 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ci/* 6888c2ecf20Sopenharmony_ci * Function: acornscsi_kick(AS_Host *host) 6898c2ecf20Sopenharmony_ci * Purpose : kick next command to interface 6908c2ecf20Sopenharmony_ci * Params : host - host to send command to 6918c2ecf20Sopenharmony_ci * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING 6928c2ecf20Sopenharmony_ci * Notes : interrupts are always disabled! 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_cistatic 6958c2ecf20Sopenharmony_ciintr_ret_t acornscsi_kick(AS_Host *host) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci int from_queue = 0; 6988c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* first check to see if a command is waiting to be executed */ 7018c2ecf20Sopenharmony_ci SCpnt = host->origSCpnt; 7028c2ecf20Sopenharmony_ci host->origSCpnt = NULL; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* retrieve next command */ 7058c2ecf20Sopenharmony_ci if (!SCpnt) { 7068c2ecf20Sopenharmony_ci SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); 7078c2ecf20Sopenharmony_ci if (!SCpnt) 7088c2ecf20Sopenharmony_ci return INTR_IDLE; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci from_queue = 1; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (host->scsi.disconnectable && host->SCpnt) { 7148c2ecf20Sopenharmony_ci queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); 7158c2ecf20Sopenharmony_ci host->scsi.disconnectable = 0; 7168c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) 7178c2ecf20Sopenharmony_ci DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", 7188c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host))); 7198c2ecf20Sopenharmony_ci#endif 7208c2ecf20Sopenharmony_ci host->SCpnt = NULL; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* 7248c2ecf20Sopenharmony_ci * If we have an interrupt pending, then we may have been reselected. 7258c2ecf20Sopenharmony_ci * In this case, we don't want to write to the registers 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ci if (!(sbic_arm_read(host, SBIC_ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { 7288c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_DESTID, SCpnt->device->id); 7298c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CMND, CMND_SELWITHATN); 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci /* 7338c2ecf20Sopenharmony_ci * claim host busy - all of these must happen atomically wrt 7348c2ecf20Sopenharmony_ci * our interrupt routine. Failure means command loss. 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_CONNECTING; 7378c2ecf20Sopenharmony_ci host->SCpnt = SCpnt; 7388c2ecf20Sopenharmony_ci host->scsi.SCp = SCpnt->SCp; 7398c2ecf20Sopenharmony_ci host->dma.xfer_setup = 0; 7408c2ecf20Sopenharmony_ci host->dma.xfer_required = 0; 7418c2ecf20Sopenharmony_ci host->dma.xfer_done = 0; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) 7448c2ecf20Sopenharmony_ci DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", 7458c2ecf20Sopenharmony_ci host->host->host_no, '0' + SCpnt->device->id, 7468c2ecf20Sopenharmony_ci SCpnt->cmnd[0])); 7478c2ecf20Sopenharmony_ci#endif 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (from_queue) { 7508c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE 7518c2ecf20Sopenharmony_ci /* 7528c2ecf20Sopenharmony_ci * tagged queueing - allocate a new tag to this command 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_ci if (SCpnt->device->simple_tags) { 7558c2ecf20Sopenharmony_ci SCpnt->device->current_tag += 1; 7568c2ecf20Sopenharmony_ci if (SCpnt->device->current_tag == 0) 7578c2ecf20Sopenharmony_ci SCpnt->device->current_tag = 1; 7588c2ecf20Sopenharmony_ci SCpnt->tag = SCpnt->device->current_tag; 7598c2ecf20Sopenharmony_ci } else 7608c2ecf20Sopenharmony_ci#endif 7618c2ecf20Sopenharmony_ci set_bit(SCpnt->device->id * 8 + 7628c2ecf20Sopenharmony_ci (u8)(SCpnt->device->lun & 0x07), host->busyluns); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci host->stats.removes += 1; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { 7678c2ecf20Sopenharmony_ci case CMD_WRITE: 7688c2ecf20Sopenharmony_ci host->stats.writes += 1; 7698c2ecf20Sopenharmony_ci break; 7708c2ecf20Sopenharmony_ci case CMD_READ: 7718c2ecf20Sopenharmony_ci host->stats.reads += 1; 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci case CMD_MISC: 7748c2ecf20Sopenharmony_ci host->stats.miscs += 1; 7758c2ecf20Sopenharmony_ci break; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci return INTR_PROCESSING; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci/* 7838c2ecf20Sopenharmony_ci * Function: void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, unsigned int result) 7848c2ecf20Sopenharmony_ci * Purpose : complete processing for command 7858c2ecf20Sopenharmony_ci * Params : host - interface that completed 7868c2ecf20Sopenharmony_ci * result - driver byte of result 7878c2ecf20Sopenharmony_ci */ 7888c2ecf20Sopenharmony_cistatic void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, 7898c2ecf20Sopenharmony_ci unsigned int result) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt = *SCpntp; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* clean up */ 7948c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci host->stats.fins += 1; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (SCpnt) { 7998c2ecf20Sopenharmony_ci *SCpntp = NULL; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci acornscsi_dma_cleanup(host); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* 8068c2ecf20Sopenharmony_ci * In theory, this should not happen. In practice, it seems to. 8078c2ecf20Sopenharmony_ci * Only trigger an error if the device attempts to report all happy 8088c2ecf20Sopenharmony_ci * but with untransferred buffers... If we don't do something, then 8098c2ecf20Sopenharmony_ci * data loss will occur. Should we check SCpnt->underflow here? 8108c2ecf20Sopenharmony_ci * It doesn't appear to be set to something meaningful by the higher 8118c2ecf20Sopenharmony_ci * levels all the time. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_ci if (result == DID_OK) { 8148c2ecf20Sopenharmony_ci int xfer_warn = 0; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (SCpnt->underflow == 0) { 8178c2ecf20Sopenharmony_ci if (host->scsi.SCp.ptr && 8188c2ecf20Sopenharmony_ci acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) 8198c2ecf20Sopenharmony_ci xfer_warn = 1; 8208c2ecf20Sopenharmony_ci } else { 8218c2ecf20Sopenharmony_ci if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || 8228c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred != host->dma.transferred) 8238c2ecf20Sopenharmony_ci xfer_warn = 1; 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) 8278c2ecf20Sopenharmony_ci * Targets which break data transfers into multiple 8288c2ecf20Sopenharmony_ci * connections shall end each successful connection 8298c2ecf20Sopenharmony_ci * (except possibly the last) with a SAVE DATA 8308c2ecf20Sopenharmony_ci * POINTER - DISCONNECT message sequence. 8318c2ecf20Sopenharmony_ci * 8328c2ecf20Sopenharmony_ci * This makes it difficult to ensure that a transfer has 8338c2ecf20Sopenharmony_ci * completed. If we reach the end of a transfer during 8348c2ecf20Sopenharmony_ci * the command, then we can only have finished the transfer. 8358c2ecf20Sopenharmony_ci * therefore, if we seem to have some data remaining, this 8368c2ecf20Sopenharmony_ci * is not a problem. 8378c2ecf20Sopenharmony_ci */ 8388c2ecf20Sopenharmony_ci if (host->dma.xfer_done) 8398c2ecf20Sopenharmony_ci xfer_warn = 0; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (xfer_warn) { 8428c2ecf20Sopenharmony_ci switch (status_byte(SCpnt->result)) { 8438c2ecf20Sopenharmony_ci case CHECK_CONDITION: 8448c2ecf20Sopenharmony_ci case COMMAND_TERMINATED: 8458c2ecf20Sopenharmony_ci case BUSY: 8468c2ecf20Sopenharmony_ci case QUEUE_FULL: 8478c2ecf20Sopenharmony_ci case RESERVATION_CONFLICT: 8488c2ecf20Sopenharmony_ci break; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci default: 8518c2ecf20Sopenharmony_ci scmd_printk(KERN_ERR, SCpnt, 8528c2ecf20Sopenharmony_ci "incomplete data transfer detected: " 8538c2ecf20Sopenharmony_ci "result=%08X", SCpnt->result); 8548c2ecf20Sopenharmony_ci scsi_print_command(SCpnt); 8558c2ecf20Sopenharmony_ci acornscsi_dumpdma(host, "done"); 8568c2ecf20Sopenharmony_ci acornscsi_dumplog(host, SCpnt->device->id); 8578c2ecf20Sopenharmony_ci set_host_byte(SCpnt, DID_ERROR); 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if (!SCpnt->scsi_done) 8638c2ecf20Sopenharmony_ci panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci clear_bit(SCpnt->device->id * 8 + 8668c2ecf20Sopenharmony_ci (u8)(SCpnt->device->lun & 0x7), host->busyluns); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci SCpnt->scsi_done(SCpnt); 8698c2ecf20Sopenharmony_ci } else 8708c2ecf20Sopenharmony_ci printk("scsi%d: null command in acornscsi_done", host->host->host_no); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_IDLE; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci/* ==================================================================================== 8768c2ecf20Sopenharmony_ci * DMA routines 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_ci/* 8798c2ecf20Sopenharmony_ci * Purpose : update SCSI Data Pointer 8808c2ecf20Sopenharmony_ci * Notes : this will only be one SG entry or less 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_cistatic 8838c2ecf20Sopenharmony_civoid acornscsi_data_updateptr(AS_Host *host, struct scsi_pointer *SCp, unsigned int length) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci SCp->ptr += length; 8868c2ecf20Sopenharmony_ci SCp->this_residual -= length; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (SCp->this_residual == 0 && next_SCp(SCp) == 0) 8898c2ecf20Sopenharmony_ci host->dma.xfer_done = 1; 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci/* 8938c2ecf20Sopenharmony_ci * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, 8948c2ecf20Sopenharmony_ci * unsigned int start_addr, unsigned int length) 8958c2ecf20Sopenharmony_ci * Purpose : read data from DMA RAM 8968c2ecf20Sopenharmony_ci * Params : host - host to transfer from 8978c2ecf20Sopenharmony_ci * ptr - DRAM address 8988c2ecf20Sopenharmony_ci * start_addr - host mem address 8998c2ecf20Sopenharmony_ci * length - number of bytes to transfer 9008c2ecf20Sopenharmony_ci * Notes : this will only be one SG entry or less 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_cistatic 9038c2ecf20Sopenharmony_civoid acornscsi_data_read(AS_Host *host, char *ptr, 9048c2ecf20Sopenharmony_ci unsigned int start_addr, unsigned int length) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci extern void __acornscsi_in(void __iomem *, char *buf, int len); 9078c2ecf20Sopenharmony_ci unsigned int page, offset, len = length; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci page = (start_addr >> 12); 9108c2ecf20Sopenharmony_ci offset = start_addr & ((1 << 12) - 1); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci while (len > 0) { 9158c2ecf20Sopenharmony_ci unsigned int this_len; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (len + offset > (1 << 12)) 9188c2ecf20Sopenharmony_ci this_len = (1 << 12) - offset; 9198c2ecf20Sopenharmony_ci else 9208c2ecf20Sopenharmony_ci this_len = len; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci __acornscsi_in(host->base + (offset << 1), ptr, this_len); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci offset += this_len; 9258c2ecf20Sopenharmony_ci ptr += this_len; 9268c2ecf20Sopenharmony_ci len -= this_len; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci if (offset == (1 << 12)) { 9298c2ecf20Sopenharmony_ci offset = 0; 9308c2ecf20Sopenharmony_ci page ++; 9318c2ecf20Sopenharmony_ci writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci writeb(host->card.page_reg, host->fast + PAGE_REG); 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci/* 9388c2ecf20Sopenharmony_ci * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, 9398c2ecf20Sopenharmony_ci * unsigned int start_addr, unsigned int length) 9408c2ecf20Sopenharmony_ci * Purpose : write data to DMA RAM 9418c2ecf20Sopenharmony_ci * Params : host - host to transfer from 9428c2ecf20Sopenharmony_ci * ptr - DRAM address 9438c2ecf20Sopenharmony_ci * start_addr - host mem address 9448c2ecf20Sopenharmony_ci * length - number of bytes to transfer 9458c2ecf20Sopenharmony_ci * Notes : this will only be one SG entry or less 9468c2ecf20Sopenharmony_ci */ 9478c2ecf20Sopenharmony_cistatic 9488c2ecf20Sopenharmony_civoid acornscsi_data_write(AS_Host *host, char *ptr, 9498c2ecf20Sopenharmony_ci unsigned int start_addr, unsigned int length) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci extern void __acornscsi_out(void __iomem *, char *buf, int len); 9528c2ecf20Sopenharmony_ci unsigned int page, offset, len = length; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci page = (start_addr >> 12); 9558c2ecf20Sopenharmony_ci offset = start_addr & ((1 << 12) - 1); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci while (len > 0) { 9608c2ecf20Sopenharmony_ci unsigned int this_len; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (len + offset > (1 << 12)) 9638c2ecf20Sopenharmony_ci this_len = (1 << 12) - offset; 9648c2ecf20Sopenharmony_ci else 9658c2ecf20Sopenharmony_ci this_len = len; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci __acornscsi_out(host->base + (offset << 1), ptr, this_len); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci offset += this_len; 9708c2ecf20Sopenharmony_ci ptr += this_len; 9718c2ecf20Sopenharmony_ci len -= this_len; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (offset == (1 << 12)) { 9748c2ecf20Sopenharmony_ci offset = 0; 9758c2ecf20Sopenharmony_ci page ++; 9768c2ecf20Sopenharmony_ci writeb((page & 0x3f) | host->card.page_reg, host->fast + PAGE_REG); 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci writeb(host->card.page_reg, host->fast + PAGE_REG); 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci/* ========================================================================================= 9838c2ecf20Sopenharmony_ci * On-board DMA routines 9848c2ecf20Sopenharmony_ci */ 9858c2ecf20Sopenharmony_ci#ifdef USE_DMAC 9868c2ecf20Sopenharmony_ci/* 9878c2ecf20Sopenharmony_ci * Prototype: void acornscsi_dmastop(AS_Host *host) 9888c2ecf20Sopenharmony_ci * Purpose : stop all DMA 9898c2ecf20Sopenharmony_ci * Params : host - host on which to stop DMA 9908c2ecf20Sopenharmony_ci * Notes : This is called when leaving DATA IN/OUT phase, 9918c2ecf20Sopenharmony_ci * or when interface is RESET 9928c2ecf20Sopenharmony_ci */ 9938c2ecf20Sopenharmony_cistatic inline 9948c2ecf20Sopenharmony_civoid acornscsi_dma_stop(AS_Host *host) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MASKREG, MASK_ON); 9978c2ecf20Sopenharmony_ci dmac_clearintr(host); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA) 10008c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "stop")); 10018c2ecf20Sopenharmony_ci#endif 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci/* 10058c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) 10068c2ecf20Sopenharmony_ci * Purpose : setup DMA controller for data transfer 10078c2ecf20Sopenharmony_ci * Params : host - host to setup 10088c2ecf20Sopenharmony_ci * direction - data transfer direction 10098c2ecf20Sopenharmony_ci * Notes : This is called when entering DATA I/O phase, not 10108c2ecf20Sopenharmony_ci * while we're in a DATA I/O phase 10118c2ecf20Sopenharmony_ci */ 10128c2ecf20Sopenharmony_cistatic 10138c2ecf20Sopenharmony_civoid acornscsi_dma_setup(AS_Host *host, dmadir_t direction) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci unsigned int address, length, mode; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci host->dma.direction = direction; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MASKREG, MASK_ON); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (direction == DMA_OUT) { 10228c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE) 10238c2ecf20Sopenharmony_ci if (NO_WRITE & (1 << host->SCpnt->device->id)) { 10248c2ecf20Sopenharmony_ci printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", 10258c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 10268c2ecf20Sopenharmony_ci return; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci#endif 10298c2ecf20Sopenharmony_ci mode = DMAC_WRITE; 10308c2ecf20Sopenharmony_ci } else 10318c2ecf20Sopenharmony_ci mode = DMAC_READ; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* 10348c2ecf20Sopenharmony_ci * Allocate some buffer space, limited to half the buffer size 10358c2ecf20Sopenharmony_ci */ 10368c2ecf20Sopenharmony_ci length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); 10378c2ecf20Sopenharmony_ci if (length) { 10388c2ecf20Sopenharmony_ci host->dma.start_addr = address = host->dma.free_addr; 10398c2ecf20Sopenharmony_ci host->dma.free_addr = (host->dma.free_addr + length) & 10408c2ecf20Sopenharmony_ci (DMAC_BUFFER_SIZE - 1); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci /* 10438c2ecf20Sopenharmony_ci * Transfer data to DMA memory 10448c2ecf20Sopenharmony_ci */ 10458c2ecf20Sopenharmony_ci if (direction == DMA_OUT) 10468c2ecf20Sopenharmony_ci acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, 10478c2ecf20Sopenharmony_ci length); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci length -= 1; 10508c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXCNTLO, length); 10518c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXCNTHI, length >> 8); 10528c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRLO, address); 10538c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRMD, address >> 8); 10548c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRHI, 0); 10558c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MODECON, mode); 10568c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MASKREG, MASK_OFF); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA) 10598c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "strt")); 10608c2ecf20Sopenharmony_ci#endif 10618c2ecf20Sopenharmony_ci host->dma.xfer_setup = 1; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci/* 10668c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_cleanup(AS_Host *host) 10678c2ecf20Sopenharmony_ci * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct 10688c2ecf20Sopenharmony_ci * Params : host - host to finish 10698c2ecf20Sopenharmony_ci * Notes : This is called when a command is: 10708c2ecf20Sopenharmony_ci * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONNECT 10718c2ecf20Sopenharmony_ci * : This must not return until all transfers are completed. 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_cistatic 10748c2ecf20Sopenharmony_civoid acornscsi_dma_cleanup(AS_Host *host) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MASKREG, MASK_ON); 10778c2ecf20Sopenharmony_ci dmac_clearintr(host); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci /* 10808c2ecf20Sopenharmony_ci * Check for a pending transfer 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_ci if (host->dma.xfer_required) { 10838c2ecf20Sopenharmony_ci host->dma.xfer_required = 0; 10848c2ecf20Sopenharmony_ci if (host->dma.direction == DMA_IN) 10858c2ecf20Sopenharmony_ci acornscsi_data_read(host, host->dma.xfer_ptr, 10868c2ecf20Sopenharmony_ci host->dma.xfer_start, host->dma.xfer_length); 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* 10908c2ecf20Sopenharmony_ci * Has a transfer been setup? 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_ci if (host->dma.xfer_setup) { 10938c2ecf20Sopenharmony_ci unsigned int transferred; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci host->dma.xfer_setup = 0; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA) 10988c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi")); 10998c2ecf20Sopenharmony_ci#endif 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci /* 11028c2ecf20Sopenharmony_ci * Calculate number of bytes transferred from DMA. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_ci transferred = dmac_address(host) - host->dma.start_addr; 11058c2ecf20Sopenharmony_ci host->dma.transferred += transferred; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (host->dma.direction == DMA_IN) 11088c2ecf20Sopenharmony_ci acornscsi_data_read(host, host->scsi.SCp.ptr, 11098c2ecf20Sopenharmony_ci host->dma.start_addr, transferred); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci /* 11128c2ecf20Sopenharmony_ci * Update SCSI pointers 11138c2ecf20Sopenharmony_ci */ 11148c2ecf20Sopenharmony_ci acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); 11158c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA) 11168c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo")); 11178c2ecf20Sopenharmony_ci#endif 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci/* 11228c2ecf20Sopenharmony_ci * Function: void acornscsi_dmacintr(AS_Host *host) 11238c2ecf20Sopenharmony_ci * Purpose : handle interrupts from DMAC device 11248c2ecf20Sopenharmony_ci * Params : host - host to process 11258c2ecf20Sopenharmony_ci * Notes : If reading, we schedule the read to main memory & 11268c2ecf20Sopenharmony_ci * allow the transfer to continue. 11278c2ecf20Sopenharmony_ci * : If writing, we fill the onboard DMA memory from main 11288c2ecf20Sopenharmony_ci * memory. 11298c2ecf20Sopenharmony_ci * : Called whenever DMAC finished it's current transfer. 11308c2ecf20Sopenharmony_ci */ 11318c2ecf20Sopenharmony_cistatic 11328c2ecf20Sopenharmony_civoid acornscsi_dma_intr(AS_Host *host) 11338c2ecf20Sopenharmony_ci{ 11348c2ecf20Sopenharmony_ci unsigned int address, length, transferred; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA) 11378c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "inti")); 11388c2ecf20Sopenharmony_ci#endif 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MASKREG, MASK_ON); 11418c2ecf20Sopenharmony_ci dmac_clearintr(host); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci /* 11448c2ecf20Sopenharmony_ci * Calculate amount transferred via DMA 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_ci transferred = dmac_address(host) - host->dma.start_addr; 11478c2ecf20Sopenharmony_ci host->dma.transferred += transferred; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* 11508c2ecf20Sopenharmony_ci * Schedule DMA transfer off board 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_ci if (host->dma.direction == DMA_IN) { 11538c2ecf20Sopenharmony_ci host->dma.xfer_start = host->dma.start_addr; 11548c2ecf20Sopenharmony_ci host->dma.xfer_length = transferred; 11558c2ecf20Sopenharmony_ci host->dma.xfer_ptr = host->scsi.SCp.ptr; 11568c2ecf20Sopenharmony_ci host->dma.xfer_required = 1; 11578c2ecf20Sopenharmony_ci } 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* 11628c2ecf20Sopenharmony_ci * Allocate some buffer space, limited to half the on-board RAM size 11638c2ecf20Sopenharmony_ci */ 11648c2ecf20Sopenharmony_ci length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); 11658c2ecf20Sopenharmony_ci if (length) { 11668c2ecf20Sopenharmony_ci host->dma.start_addr = address = host->dma.free_addr; 11678c2ecf20Sopenharmony_ci host->dma.free_addr = (host->dma.free_addr + length) & 11688c2ecf20Sopenharmony_ci (DMAC_BUFFER_SIZE - 1); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci /* 11718c2ecf20Sopenharmony_ci * Transfer data to DMA memory 11728c2ecf20Sopenharmony_ci */ 11738c2ecf20Sopenharmony_ci if (host->dma.direction == DMA_OUT) 11748c2ecf20Sopenharmony_ci acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, 11758c2ecf20Sopenharmony_ci length); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci length -= 1; 11788c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXCNTLO, length); 11798c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXCNTHI, length >> 8); 11808c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRLO, address); 11818c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRMD, address >> 8); 11828c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRHI, 0); 11838c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MASKREG, MASK_OFF); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_DMA) 11868c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "into")); 11878c2ecf20Sopenharmony_ci#endif 11888c2ecf20Sopenharmony_ci } else { 11898c2ecf20Sopenharmony_ci host->dma.xfer_setup = 0; 11908c2ecf20Sopenharmony_ci#if 0 11918c2ecf20Sopenharmony_ci /* 11928c2ecf20Sopenharmony_ci * If the interface still wants more, then this is an error. 11938c2ecf20Sopenharmony_ci * We give it another byte, but we also attempt to raise an 11948c2ecf20Sopenharmony_ci * attention condition. We continue giving one byte until 11958c2ecf20Sopenharmony_ci * the device recognises the attention. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci if (dmac_read(host, DMAC_STATUS) & STATUS_RQ0) { 11988c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXCNTLO, 0); 12018c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXCNTHI, 0); 12028c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRLO, 0); 12038c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRMD, 0); 12048c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRHI, 0); 12058c2ecf20Sopenharmony_ci dmac_write(host, DMAC_MASKREG, MASK_OFF); 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci#endif 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci/* 12128c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_xfer(AS_Host *host) 12138c2ecf20Sopenharmony_ci * Purpose : transfer data between AcornSCSI and memory 12148c2ecf20Sopenharmony_ci * Params : host - host to process 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_cistatic 12178c2ecf20Sopenharmony_civoid acornscsi_dma_xfer(AS_Host *host) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci host->dma.xfer_required = 0; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci if (host->dma.direction == DMA_IN) 12228c2ecf20Sopenharmony_ci acornscsi_data_read(host, host->dma.xfer_ptr, 12238c2ecf20Sopenharmony_ci host->dma.xfer_start, host->dma.xfer_length); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci/* 12278c2ecf20Sopenharmony_ci * Function: void acornscsi_dma_adjust(AS_Host *host) 12288c2ecf20Sopenharmony_ci * Purpose : adjust DMA pointers & count for bytes transferred to 12298c2ecf20Sopenharmony_ci * SBIC but not SCSI bus. 12308c2ecf20Sopenharmony_ci * Params : host - host to adjust DMA count for 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_cistatic 12338c2ecf20Sopenharmony_civoid acornscsi_dma_adjust(AS_Host *host) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci if (host->dma.xfer_setup) { 12368c2ecf20Sopenharmony_ci signed long transferred; 12378c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) 12388c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "adji")); 12398c2ecf20Sopenharmony_ci#endif 12408c2ecf20Sopenharmony_ci /* 12418c2ecf20Sopenharmony_ci * Calculate correct DMA address - DMA is ahead of SCSI bus while 12428c2ecf20Sopenharmony_ci * writing. 12438c2ecf20Sopenharmony_ci * host->scsi.SCp.scsi_xferred is the number of bytes 12448c2ecf20Sopenharmony_ci * actually transferred to/from the SCSI bus. 12458c2ecf20Sopenharmony_ci * host->dma.transferred is the number of bytes transferred 12468c2ecf20Sopenharmony_ci * over DMA since host->dma.start_addr was last set. 12478c2ecf20Sopenharmony_ci * 12488c2ecf20Sopenharmony_ci * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred 12498c2ecf20Sopenharmony_ci * - host->dma.transferred 12508c2ecf20Sopenharmony_ci */ 12518c2ecf20Sopenharmony_ci transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; 12528c2ecf20Sopenharmony_ci if (transferred < 0) 12538c2ecf20Sopenharmony_ci printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", 12548c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), transferred); 12558c2ecf20Sopenharmony_ci else if (transferred == 0) 12568c2ecf20Sopenharmony_ci host->dma.xfer_setup = 0; 12578c2ecf20Sopenharmony_ci else { 12588c2ecf20Sopenharmony_ci transferred += host->dma.start_addr; 12598c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRLO, transferred); 12608c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRMD, transferred >> 8); 12618c2ecf20Sopenharmony_ci dmac_write(host, DMAC_TXADRHI, transferred >> 16); 12628c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) 12638c2ecf20Sopenharmony_ci DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo")); 12648c2ecf20Sopenharmony_ci#endif 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci#endif 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci/* ========================================================================================= 12718c2ecf20Sopenharmony_ci * Data I/O 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_cistatic int 12748c2ecf20Sopenharmony_ciacornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout) 12758c2ecf20Sopenharmony_ci{ 12768c2ecf20Sopenharmony_ci unsigned int asr, timeout = max_timeout; 12778c2ecf20Sopenharmony_ci int my_ptr = *ptr; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci while (my_ptr < len) { 12808c2ecf20Sopenharmony_ci asr = sbic_arm_read(host, SBIC_ASR); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (asr & ASR_DBR) { 12838c2ecf20Sopenharmony_ci timeout = max_timeout; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_DATA, bytes[my_ptr++]); 12868c2ecf20Sopenharmony_ci } else if (asr & ASR_INT) 12878c2ecf20Sopenharmony_ci break; 12888c2ecf20Sopenharmony_ci else if (--timeout == 0) 12898c2ecf20Sopenharmony_ci break; 12908c2ecf20Sopenharmony_ci udelay(1); 12918c2ecf20Sopenharmony_ci } 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci *ptr = my_ptr; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci return (timeout == 0) ? -1 : 0; 12968c2ecf20Sopenharmony_ci} 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci/* 12998c2ecf20Sopenharmony_ci * Function: void acornscsi_sendcommand(AS_Host *host) 13008c2ecf20Sopenharmony_ci * Purpose : send a command to a target 13018c2ecf20Sopenharmony_ci * Params : host - host which is connected to target 13028c2ecf20Sopenharmony_ci */ 13038c2ecf20Sopenharmony_cistatic void 13048c2ecf20Sopenharmony_ciacornscsi_sendcommand(AS_Host *host) 13058c2ecf20Sopenharmony_ci{ 13068c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt = host->SCpnt; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_TRANSCNTH, 0); 13098c2ecf20Sopenharmony_ci sbic_arm_writenext(host, 0); 13108c2ecf20Sopenharmony_ci sbic_arm_writenext(host, SCpnt->cmd_len - host->scsi.SCp.sent_command); 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_XFERINFO); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci if (acornscsi_write_pio(host, SCpnt->cmnd, 13158c2ecf20Sopenharmony_ci (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) 13168c2ecf20Sopenharmony_ci printk("scsi%d: timeout while sending command\n", host->host->host_no); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_COMMAND; 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_cistatic 13228c2ecf20Sopenharmony_civoid acornscsi_sendmessage(AS_Host *host) 13238c2ecf20Sopenharmony_ci{ 13248c2ecf20Sopenharmony_ci unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); 13258c2ecf20Sopenharmony_ci unsigned int msgnr; 13268c2ecf20Sopenharmony_ci struct message *msg; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES) 13298c2ecf20Sopenharmony_ci printk("scsi%d.%c: sending message ", 13308c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 13318c2ecf20Sopenharmony_ci#endif 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci switch (message_length) { 13348c2ecf20Sopenharmony_ci case 0: 13358c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1"); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_DATA, NOP); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci host->scsi.last_message = NOP; 13428c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES) 13438c2ecf20Sopenharmony_ci printk("NOP"); 13448c2ecf20Sopenharmony_ci#endif 13458c2ecf20Sopenharmony_ci break; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci case 1: 13488c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); 13498c2ecf20Sopenharmony_ci msg = msgqueue_getmsg(&host->scsi.msgs, 0); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2"); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_DATA, msg->msg[0]); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci host->scsi.last_message = msg->msg[0]; 13568c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES) 13578c2ecf20Sopenharmony_ci spi_print_msg(msg->msg); 13588c2ecf20Sopenharmony_ci#endif 13598c2ecf20Sopenharmony_ci break; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci default: 13628c2ecf20Sopenharmony_ci /* 13638c2ecf20Sopenharmony_ci * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14) 13648c2ecf20Sopenharmony_ci * 'When a target sends this (MESSAGE_REJECT) message, it 13658c2ecf20Sopenharmony_ci * shall change to MESSAGE IN phase and send this message 13668c2ecf20Sopenharmony_ci * prior to requesting additional message bytes from the 13678c2ecf20Sopenharmony_ci * initiator. This provides an interlock so that the 13688c2ecf20Sopenharmony_ci * initiator can determine which message byte is rejected. 13698c2ecf20Sopenharmony_ci */ 13708c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_TRANSCNTH, 0); 13718c2ecf20Sopenharmony_ci sbic_arm_writenext(host, 0); 13728c2ecf20Sopenharmony_ci sbic_arm_writenext(host, message_length); 13738c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_XFERINFO); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci msgnr = 0; 13768c2ecf20Sopenharmony_ci while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) { 13778c2ecf20Sopenharmony_ci unsigned int i; 13788c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES) 13798c2ecf20Sopenharmony_ci spi_print_msg(msg); 13808c2ecf20Sopenharmony_ci#endif 13818c2ecf20Sopenharmony_ci i = 0; 13828c2ecf20Sopenharmony_ci if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000)) 13838c2ecf20Sopenharmony_ci printk("scsi%d: timeout while sending message\n", host->host->host_no); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci host->scsi.last_message = msg->msg[0]; 13868c2ecf20Sopenharmony_ci if (msg->msg[0] == EXTENDED_MESSAGE) 13878c2ecf20Sopenharmony_ci host->scsi.last_message |= msg->msg[2] << 8; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (i != msg->length) 13908c2ecf20Sopenharmony_ci break; 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci break; 13938c2ecf20Sopenharmony_ci } 13948c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES) 13958c2ecf20Sopenharmony_ci printk("\n"); 13968c2ecf20Sopenharmony_ci#endif 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci/* 14008c2ecf20Sopenharmony_ci * Function: void acornscsi_readstatusbyte(AS_Host *host) 14018c2ecf20Sopenharmony_ci * Purpose : Read status byte from connected target 14028c2ecf20Sopenharmony_ci * Params : host - host connected to target 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_cistatic 14058c2ecf20Sopenharmony_civoid acornscsi_readstatusbyte(AS_Host *host) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT); 14088c2ecf20Sopenharmony_ci acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte"); 14098c2ecf20Sopenharmony_ci host->scsi.SCp.Status = sbic_arm_read(host, SBIC_DATA); 14108c2ecf20Sopenharmony_ci} 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci/* 14138c2ecf20Sopenharmony_ci * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host) 14148c2ecf20Sopenharmony_ci * Purpose : Read one message byte from connected target 14158c2ecf20Sopenharmony_ci * Params : host - host connected to target 14168c2ecf20Sopenharmony_ci */ 14178c2ecf20Sopenharmony_cistatic 14188c2ecf20Sopenharmony_ciunsigned char acornscsi_readmessagebyte(AS_Host *host) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci unsigned char message; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte"); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci message = sbic_arm_read(host, SBIC_DATA); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* wait for MSGIN-XFER-PAUSED */ 14298c2ecf20Sopenharmony_ci acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte"); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci sbic_arm_read(host, SBIC_SSR); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci return message; 14348c2ecf20Sopenharmony_ci} 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci/* 14378c2ecf20Sopenharmony_ci * Function: void acornscsi_message(AS_Host *host) 14388c2ecf20Sopenharmony_ci * Purpose : Read complete message from connected target & action message 14398c2ecf20Sopenharmony_ci * Params : host - host connected to target 14408c2ecf20Sopenharmony_ci */ 14418c2ecf20Sopenharmony_cistatic 14428c2ecf20Sopenharmony_civoid acornscsi_message(AS_Host *host) 14438c2ecf20Sopenharmony_ci{ 14448c2ecf20Sopenharmony_ci unsigned char message[16]; 14458c2ecf20Sopenharmony_ci unsigned int msgidx = 0, msglen = 1; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci do { 14488c2ecf20Sopenharmony_ci message[msgidx] = acornscsi_readmessagebyte(host); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci switch (msgidx) { 14518c2ecf20Sopenharmony_ci case 0: 14528c2ecf20Sopenharmony_ci if (message[0] == EXTENDED_MESSAGE || 14538c2ecf20Sopenharmony_ci (message[0] >= 0x20 && message[0] <= 0x2f)) 14548c2ecf20Sopenharmony_ci msglen = 2; 14558c2ecf20Sopenharmony_ci break; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci case 1: 14588c2ecf20Sopenharmony_ci if (message[0] == EXTENDED_MESSAGE) 14598c2ecf20Sopenharmony_ci msglen += message[msgidx]; 14608c2ecf20Sopenharmony_ci break; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci msgidx += 1; 14638c2ecf20Sopenharmony_ci if (msgidx < msglen) { 14648c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci /* wait for next msg-in */ 14678c2ecf20Sopenharmony_ci acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack"); 14688c2ecf20Sopenharmony_ci sbic_arm_read(host, SBIC_SSR); 14698c2ecf20Sopenharmony_ci } 14708c2ecf20Sopenharmony_ci } while (msgidx < msglen); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_MESSAGES) 14738c2ecf20Sopenharmony_ci printk("scsi%d.%c: message in: ", 14748c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 14758c2ecf20Sopenharmony_ci spi_print_msg(message); 14768c2ecf20Sopenharmony_ci printk("\n"); 14778c2ecf20Sopenharmony_ci#endif 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci if (host->scsi.phase == PHASE_RECONNECTED) { 14808c2ecf20Sopenharmony_ci /* 14818c2ecf20Sopenharmony_ci * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) 14828c2ecf20Sopenharmony_ci * 'Whenever a target reconnects to an initiator to continue 14838c2ecf20Sopenharmony_ci * a tagged I/O process, the SIMPLE QUEUE TAG message shall 14848c2ecf20Sopenharmony_ci * be sent immediately following the IDENTIFY message...' 14858c2ecf20Sopenharmony_ci */ 14868c2ecf20Sopenharmony_ci if (message[0] == SIMPLE_QUEUE_TAG) 14878c2ecf20Sopenharmony_ci host->scsi.reconnected.tag = message[1]; 14888c2ecf20Sopenharmony_ci if (acornscsi_reconnect_finish(host)) 14898c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_MSGIN; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci switch (message[0]) { 14938c2ecf20Sopenharmony_ci case ABORT: 14948c2ecf20Sopenharmony_ci case ABORT_TAG: 14958c2ecf20Sopenharmony_ci case COMMAND_COMPLETE: 14968c2ecf20Sopenharmony_ci if (host->scsi.phase != PHASE_STATUSIN) { 14978c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n", 14988c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 14998c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt->device->id); 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_DONE; 15028c2ecf20Sopenharmony_ci host->scsi.SCp.Message = message[0]; 15038c2ecf20Sopenharmony_ci break; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci case SAVE_POINTERS: 15068c2ecf20Sopenharmony_ci /* 15078c2ecf20Sopenharmony_ci * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20) 15088c2ecf20Sopenharmony_ci * 'The SAVE DATA POINTER message is sent from a target to 15098c2ecf20Sopenharmony_ci * direct the initiator to copy the active data pointer to 15108c2ecf20Sopenharmony_ci * the saved data pointer for the current I/O process. 15118c2ecf20Sopenharmony_ci */ 15128c2ecf20Sopenharmony_ci acornscsi_dma_cleanup(host); 15138c2ecf20Sopenharmony_ci host->SCpnt->SCp = host->scsi.SCp; 15148c2ecf20Sopenharmony_ci host->SCpnt->SCp.sent_command = 0; 15158c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_MSGIN; 15168c2ecf20Sopenharmony_ci break; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci case RESTORE_POINTERS: 15198c2ecf20Sopenharmony_ci /* 15208c2ecf20Sopenharmony_ci * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19) 15218c2ecf20Sopenharmony_ci * 'The RESTORE POINTERS message is sent from a target to 15228c2ecf20Sopenharmony_ci * direct the initiator to copy the most recently saved 15238c2ecf20Sopenharmony_ci * command, data, and status pointers for the I/O process 15248c2ecf20Sopenharmony_ci * to the corresponding active pointers. The command and 15258c2ecf20Sopenharmony_ci * status pointers shall be restored to the beginning of 15268c2ecf20Sopenharmony_ci * the present command and status areas.' 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_ci acornscsi_dma_cleanup(host); 15298c2ecf20Sopenharmony_ci host->scsi.SCp = host->SCpnt->SCp; 15308c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_MSGIN; 15318c2ecf20Sopenharmony_ci break; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci case DISCONNECT: 15348c2ecf20Sopenharmony_ci /* 15358c2ecf20Sopenharmony_ci * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2) 15368c2ecf20Sopenharmony_ci * 'On those occasions when an error or exception condition occurs 15378c2ecf20Sopenharmony_ci * and the target elects to repeat the information transfer, the 15388c2ecf20Sopenharmony_ci * target may repeat the transfer either issuing a RESTORE POINTERS 15398c2ecf20Sopenharmony_ci * message or by disconnecting without issuing a SAVE POINTERS 15408c2ecf20Sopenharmony_ci * message. When reconnection is completed, the most recent 15418c2ecf20Sopenharmony_ci * saved pointer values are restored.' 15428c2ecf20Sopenharmony_ci */ 15438c2ecf20Sopenharmony_ci acornscsi_dma_cleanup(host); 15448c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_DISCONNECT; 15458c2ecf20Sopenharmony_ci break; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci case MESSAGE_REJECT: 15488c2ecf20Sopenharmony_ci#if 0 /* this isn't needed any more */ 15498c2ecf20Sopenharmony_ci /* 15508c2ecf20Sopenharmony_ci * If we were negociating sync transfer, we don't yet know if 15518c2ecf20Sopenharmony_ci * this REJECT is for the sync transfer or for the tagged queue/wide 15528c2ecf20Sopenharmony_ci * transfer. Re-initiate sync transfer negotiation now, and if 15538c2ecf20Sopenharmony_ci * we got a REJECT in response to SDTR, then it'll be set to DONE. 15548c2ecf20Sopenharmony_ci */ 15558c2ecf20Sopenharmony_ci if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) 15568c2ecf20Sopenharmony_ci host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE; 15578c2ecf20Sopenharmony_ci#endif 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci /* 15608c2ecf20Sopenharmony_ci * If we have any messages waiting to go out, then assert ATN now 15618c2ecf20Sopenharmony_ci */ 15628c2ecf20Sopenharmony_ci if (msgqueue_msglength(&host->scsi.msgs)) 15638c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci switch (host->scsi.last_message) { 15668c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE 15678c2ecf20Sopenharmony_ci case HEAD_OF_QUEUE_TAG: 15688c2ecf20Sopenharmony_ci case ORDERED_QUEUE_TAG: 15698c2ecf20Sopenharmony_ci case SIMPLE_QUEUE_TAG: 15708c2ecf20Sopenharmony_ci /* 15718c2ecf20Sopenharmony_ci * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) 15728c2ecf20Sopenharmony_ci * If a target does not implement tagged queuing and a queue tag 15738c2ecf20Sopenharmony_ci * message is received, it shall respond with a MESSAGE REJECT 15748c2ecf20Sopenharmony_ci * message and accept the I/O process as if it were untagged. 15758c2ecf20Sopenharmony_ci */ 15768c2ecf20Sopenharmony_ci printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", 15778c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 15788c2ecf20Sopenharmony_ci host->SCpnt->device->simple_tags = 0; 15798c2ecf20Sopenharmony_ci set_bit(host->SCpnt->device->id * 8 + 15808c2ecf20Sopenharmony_ci (u8)(host->SCpnt->device->lun & 0x7), host->busyluns); 15818c2ecf20Sopenharmony_ci break; 15828c2ecf20Sopenharmony_ci#endif 15838c2ecf20Sopenharmony_ci case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): 15848c2ecf20Sopenharmony_ci /* 15858c2ecf20Sopenharmony_ci * Target can't handle synchronous transfers 15868c2ecf20Sopenharmony_ci */ 15878c2ecf20Sopenharmony_ci printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", 15888c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 15898c2ecf20Sopenharmony_ci host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA; 15908c2ecf20Sopenharmony_ci host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS; 15918c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); 15928c2ecf20Sopenharmony_ci break; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci default: 15958c2ecf20Sopenharmony_ci break; 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci break; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci case QUEUE_FULL: 16008c2ecf20Sopenharmony_ci /* TODO: target queue is full */ 16018c2ecf20Sopenharmony_ci break; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci case SIMPLE_QUEUE_TAG: 16048c2ecf20Sopenharmony_ci /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */ 16058c2ecf20Sopenharmony_ci printk("scsi%d.%c: reconnect queue tag %02X\n", 16068c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), 16078c2ecf20Sopenharmony_ci message[1]); 16088c2ecf20Sopenharmony_ci break; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci case EXTENDED_MESSAGE: 16118c2ecf20Sopenharmony_ci switch (message[2]) { 16128c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC 16138c2ecf20Sopenharmony_ci case EXTENDED_SDTR: 16148c2ecf20Sopenharmony_ci if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) { 16158c2ecf20Sopenharmony_ci /* 16168c2ecf20Sopenharmony_ci * We requested synchronous transfers. This isn't quite right... 16178c2ecf20Sopenharmony_ci * We can only say if this succeeded if we proceed on to execute the 16188c2ecf20Sopenharmony_ci * command from this message. If we get a MESSAGE PARITY ERROR, 16198c2ecf20Sopenharmony_ci * and the target retries fail, then we fallback to asynchronous mode 16208c2ecf20Sopenharmony_ci */ 16218c2ecf20Sopenharmony_ci host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED; 16228c2ecf20Sopenharmony_ci printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", 16238c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), 16248c2ecf20Sopenharmony_ci message[4], message[3] * 4); 16258c2ecf20Sopenharmony_ci host->device[host->SCpnt->device->id].sync_xfer = 16268c2ecf20Sopenharmony_ci calc_sync_xfer(message[3] * 4, message[4]); 16278c2ecf20Sopenharmony_ci } else { 16288c2ecf20Sopenharmony_ci unsigned char period, length; 16298c2ecf20Sopenharmony_ci /* 16308c2ecf20Sopenharmony_ci * Target requested synchronous transfers. The agreement is only 16318c2ecf20Sopenharmony_ci * to be in operation AFTER the target leaves message out phase. 16328c2ecf20Sopenharmony_ci */ 16338c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); 16348c2ecf20Sopenharmony_ci period = max_t(unsigned int, message[3], sdtr_period / 4); 16358c2ecf20Sopenharmony_ci length = min_t(unsigned int, message[4], sdtr_size); 16368c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, 16378c2ecf20Sopenharmony_ci EXTENDED_SDTR, period, length); 16388c2ecf20Sopenharmony_ci host->device[host->SCpnt->device->id].sync_xfer = 16398c2ecf20Sopenharmony_ci calc_sync_xfer(period * 4, length); 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); 16428c2ecf20Sopenharmony_ci break; 16438c2ecf20Sopenharmony_ci#else 16448c2ecf20Sopenharmony_ci /* We do not accept synchronous transfers. Respond with a 16458c2ecf20Sopenharmony_ci * MESSAGE_REJECT. 16468c2ecf20Sopenharmony_ci */ 16478c2ecf20Sopenharmony_ci#endif 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci case EXTENDED_WDTR: 16508c2ecf20Sopenharmony_ci /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT 16518c2ecf20Sopenharmony_ci * to a wide data transfer request. 16528c2ecf20Sopenharmony_ci */ 16538c2ecf20Sopenharmony_ci default: 16548c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); 16558c2ecf20Sopenharmony_ci msgqueue_flush(&host->scsi.msgs); 16568c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); 16578c2ecf20Sopenharmony_ci break; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci break; 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci default: /* reject message */ 16628c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", 16638c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), 16648c2ecf20Sopenharmony_ci message[0]); 16658c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); 16668c2ecf20Sopenharmony_ci msgqueue_flush(&host->scsi.msgs); 16678c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); 16688c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_MSGIN; 16698c2ecf20Sopenharmony_ci break; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); 16728c2ecf20Sopenharmony_ci} 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci/* 16758c2ecf20Sopenharmony_ci * Function: int acornscsi_buildmessages(AS_Host *host) 16768c2ecf20Sopenharmony_ci * Purpose : build the connection messages for a host 16778c2ecf20Sopenharmony_ci * Params : host - host to add messages to 16788c2ecf20Sopenharmony_ci */ 16798c2ecf20Sopenharmony_cistatic 16808c2ecf20Sopenharmony_civoid acornscsi_buildmessages(AS_Host *host) 16818c2ecf20Sopenharmony_ci{ 16828c2ecf20Sopenharmony_ci#if 0 16838c2ecf20Sopenharmony_ci /* does the device need resetting? */ 16848c2ecf20Sopenharmony_ci if (cmd_reset) { 16858c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET); 16868c2ecf20Sopenharmony_ci return; 16878c2ecf20Sopenharmony_ci } 16888c2ecf20Sopenharmony_ci#endif 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 1, 16918c2ecf20Sopenharmony_ci IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok, 16928c2ecf20Sopenharmony_ci host->SCpnt->device->lun)); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci#if 0 16958c2ecf20Sopenharmony_ci /* does the device need the current command aborted */ 16968c2ecf20Sopenharmony_ci if (cmd_aborted) { 16978c2ecf20Sopenharmony_ci acornscsi_abortcmd(host->SCpnt->tag); 16988c2ecf20Sopenharmony_ci return; 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci#endif 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE 17038c2ecf20Sopenharmony_ci if (host->SCpnt->tag) { 17048c2ecf20Sopenharmony_ci unsigned int tag_type; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (host->SCpnt->cmnd[0] == REQUEST_SENSE || 17078c2ecf20Sopenharmony_ci host->SCpnt->cmnd[0] == TEST_UNIT_READY || 17088c2ecf20Sopenharmony_ci host->SCpnt->cmnd[0] == INQUIRY) 17098c2ecf20Sopenharmony_ci tag_type = HEAD_OF_QUEUE_TAG; 17108c2ecf20Sopenharmony_ci else 17118c2ecf20Sopenharmony_ci tag_type = SIMPLE_QUEUE_TAG; 17128c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); 17138c2ecf20Sopenharmony_ci } 17148c2ecf20Sopenharmony_ci#endif 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC 17178c2ecf20Sopenharmony_ci if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) { 17188c2ecf20Sopenharmony_ci host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST; 17198c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 5, 17208c2ecf20Sopenharmony_ci EXTENDED_MESSAGE, 3, EXTENDED_SDTR, 17218c2ecf20Sopenharmony_ci sdtr_period / 4, sdtr_size); 17228c2ecf20Sopenharmony_ci } 17238c2ecf20Sopenharmony_ci#endif 17248c2ecf20Sopenharmony_ci} 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci/* 17278c2ecf20Sopenharmony_ci * Function: int acornscsi_starttransfer(AS_Host *host) 17288c2ecf20Sopenharmony_ci * Purpose : transfer data to/from connected target 17298c2ecf20Sopenharmony_ci * Params : host - host to which target is connected 17308c2ecf20Sopenharmony_ci * Returns : 0 if failure 17318c2ecf20Sopenharmony_ci */ 17328c2ecf20Sopenharmony_cistatic 17338c2ecf20Sopenharmony_ciint acornscsi_starttransfer(AS_Host *host) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci int residual; 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) { 17388c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", 17398c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 17408c2ecf20Sopenharmony_ci return 0; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci residual = scsi_bufflen(host->SCpnt) - host->scsi.SCp.scsi_xferred; 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); 17468c2ecf20Sopenharmony_ci sbic_arm_writenext(host, residual >> 16); 17478c2ecf20Sopenharmony_ci sbic_arm_writenext(host, residual >> 8); 17488c2ecf20Sopenharmony_ci sbic_arm_writenext(host, residual); 17498c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_XFERINFO); 17508c2ecf20Sopenharmony_ci return 1; 17518c2ecf20Sopenharmony_ci} 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci/* ========================================================================================= 17548c2ecf20Sopenharmony_ci * Connection & Disconnection 17558c2ecf20Sopenharmony_ci */ 17568c2ecf20Sopenharmony_ci/* 17578c2ecf20Sopenharmony_ci * Function : acornscsi_reconnect(AS_Host *host) 17588c2ecf20Sopenharmony_ci * Purpose : reconnect a previously disconnected command 17598c2ecf20Sopenharmony_ci * Params : host - host specific data 17608c2ecf20Sopenharmony_ci * Remarks : SCSI spec says: 17618c2ecf20Sopenharmony_ci * 'The set of active pointers is restored from the set 17628c2ecf20Sopenharmony_ci * of saved pointers upon reconnection of the I/O process' 17638c2ecf20Sopenharmony_ci */ 17648c2ecf20Sopenharmony_cistatic 17658c2ecf20Sopenharmony_ciint acornscsi_reconnect(AS_Host *host) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci unsigned int target, lun, ok = 0; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci target = sbic_arm_read(host, SBIC_SOURCEID); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if (!(target & 8)) 17728c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d: invalid source id after reselection " 17738c2ecf20Sopenharmony_ci "- device fault?\n", 17748c2ecf20Sopenharmony_ci host->host->host_no); 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci target &= 7; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (host->SCpnt && !host->scsi.disconnectable) { 17798c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%d: reconnected while command in " 17808c2ecf20Sopenharmony_ci "progress to target %d?\n", 17818c2ecf20Sopenharmony_ci host->host->host_no, target, host->SCpnt->device->id); 17828c2ecf20Sopenharmony_ci host->SCpnt = NULL; 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci lun = sbic_arm_read(host, SBIC_DATA) & 7; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci host->scsi.reconnected.target = target; 17888c2ecf20Sopenharmony_ci host->scsi.reconnected.lun = lun; 17898c2ecf20Sopenharmony_ci host->scsi.reconnected.tag = 0; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (host->scsi.disconnectable && host->SCpnt && 17928c2ecf20Sopenharmony_ci host->SCpnt->device->id == target && host->SCpnt->device->lun == lun) 17938c2ecf20Sopenharmony_ci ok = 1; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) 17968c2ecf20Sopenharmony_ci ok = 1; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci ADD_STATUS(target, 0x81, host->scsi.phase, 0); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (ok) { 18018c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_RECONNECTED; 18028c2ecf20Sopenharmony_ci } else { 18038c2ecf20Sopenharmony_ci /* this doesn't seem to work */ 18048c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: reselected with no command " 18058c2ecf20Sopenharmony_ci "to reconnect with\n", 18068c2ecf20Sopenharmony_ci host->host->host_no, '0' + target); 18078c2ecf20Sopenharmony_ci acornscsi_dumplog(host, target); 18088c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, 0); 18098c2ecf20Sopenharmony_ci if (host->SCpnt) { 18108c2ecf20Sopenharmony_ci queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); 18118c2ecf20Sopenharmony_ci host->SCpnt = NULL; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); 18158c2ecf20Sopenharmony_ci return !ok; 18168c2ecf20Sopenharmony_ci} 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci/* 18198c2ecf20Sopenharmony_ci * Function: int acornscsi_reconnect_finish(AS_Host *host) 18208c2ecf20Sopenharmony_ci * Purpose : finish reconnecting a command 18218c2ecf20Sopenharmony_ci * Params : host - host to complete 18228c2ecf20Sopenharmony_ci * Returns : 0 if failed 18238c2ecf20Sopenharmony_ci */ 18248c2ecf20Sopenharmony_cistatic 18258c2ecf20Sopenharmony_ciint acornscsi_reconnect_finish(AS_Host *host) 18268c2ecf20Sopenharmony_ci{ 18278c2ecf20Sopenharmony_ci if (host->scsi.disconnectable && host->SCpnt) { 18288c2ecf20Sopenharmony_ci host->scsi.disconnectable = 0; 18298c2ecf20Sopenharmony_ci if (host->SCpnt->device->id == host->scsi.reconnected.target && 18308c2ecf20Sopenharmony_ci host->SCpnt->device->lun == host->scsi.reconnected.lun && 18318c2ecf20Sopenharmony_ci host->SCpnt->tag == host->scsi.reconnected.tag) { 18328c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) 18338c2ecf20Sopenharmony_ci DBG(host->SCpnt, printk("scsi%d.%c: reconnected", 18348c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host))); 18358c2ecf20Sopenharmony_ci#endif 18368c2ecf20Sopenharmony_ci } else { 18378c2ecf20Sopenharmony_ci queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); 18388c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) 18398c2ecf20Sopenharmony_ci DBG(host->SCpnt, printk("scsi%d.%c: had to move command " 18408c2ecf20Sopenharmony_ci "to disconnected queue\n", 18418c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host))); 18428c2ecf20Sopenharmony_ci#endif 18438c2ecf20Sopenharmony_ci host->SCpnt = NULL; 18448c2ecf20Sopenharmony_ci } 18458c2ecf20Sopenharmony_ci } 18468c2ecf20Sopenharmony_ci if (!host->SCpnt) { 18478c2ecf20Sopenharmony_ci host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, 18488c2ecf20Sopenharmony_ci host->scsi.reconnected.target, 18498c2ecf20Sopenharmony_ci host->scsi.reconnected.lun, 18508c2ecf20Sopenharmony_ci host->scsi.reconnected.tag); 18518c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) 18528c2ecf20Sopenharmony_ci DBG(host->SCpnt, printk("scsi%d.%c: had to get command", 18538c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host))); 18548c2ecf20Sopenharmony_ci#endif 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci if (!host->SCpnt) 18588c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->scsi.reconnected.tag); 18598c2ecf20Sopenharmony_ci else { 18608c2ecf20Sopenharmony_ci /* 18618c2ecf20Sopenharmony_ci * Restore data pointer from SAVED pointers. 18628c2ecf20Sopenharmony_ci */ 18638c2ecf20Sopenharmony_ci host->scsi.SCp = host->SCpnt->SCp; 18648c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) 18658c2ecf20Sopenharmony_ci printk(", data pointers: [%p, %X]", 18668c2ecf20Sopenharmony_ci host->scsi.SCp.ptr, host->scsi.SCp.this_residual); 18678c2ecf20Sopenharmony_ci#endif 18688c2ecf20Sopenharmony_ci } 18698c2ecf20Sopenharmony_ci#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) 18708c2ecf20Sopenharmony_ci printk("\n"); 18718c2ecf20Sopenharmony_ci#endif 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci host->dma.transferred = host->scsi.SCp.scsi_xferred; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci return host->SCpnt != NULL; 18768c2ecf20Sopenharmony_ci} 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci/* 18798c2ecf20Sopenharmony_ci * Function: void acornscsi_disconnect_unexpected(AS_Host *host) 18808c2ecf20Sopenharmony_ci * Purpose : handle an unexpected disconnect 18818c2ecf20Sopenharmony_ci * Params : host - host on which disconnect occurred 18828c2ecf20Sopenharmony_ci */ 18838c2ecf20Sopenharmony_cistatic 18848c2ecf20Sopenharmony_civoid acornscsi_disconnect_unexpected(AS_Host *host) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", 18878c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 18888c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_ABORT) 18898c2ecf20Sopenharmony_ci acornscsi_dumplog(host, 8); 18908c2ecf20Sopenharmony_ci#endif 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci acornscsi_done(host, &host->SCpnt, DID_ERROR); 18938c2ecf20Sopenharmony_ci} 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci/* 18968c2ecf20Sopenharmony_ci * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) 18978c2ecf20Sopenharmony_ci * Purpose : abort a currently executing command 18988c2ecf20Sopenharmony_ci * Params : host - host with connected command to abort 18998c2ecf20Sopenharmony_ci * tag - tag to abort 19008c2ecf20Sopenharmony_ci */ 19018c2ecf20Sopenharmony_cistatic 19028c2ecf20Sopenharmony_civoid acornscsi_abortcmd(AS_Host *host, unsigned char tag) 19038c2ecf20Sopenharmony_ci{ 19048c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_ABORTED; 19058c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CMND, CMND_ASSERTATN); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci msgqueue_flush(&host->scsi.msgs); 19088c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE 19098c2ecf20Sopenharmony_ci if (tag) 19108c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); 19118c2ecf20Sopenharmony_ci else 19128c2ecf20Sopenharmony_ci#endif 19138c2ecf20Sopenharmony_ci msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); 19148c2ecf20Sopenharmony_ci} 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci/* ========================================================================================== 19178c2ecf20Sopenharmony_ci * Interrupt routines. 19188c2ecf20Sopenharmony_ci */ 19198c2ecf20Sopenharmony_ci/* 19208c2ecf20Sopenharmony_ci * Function: int acornscsi_sbicintr(AS_Host *host) 19218c2ecf20Sopenharmony_ci * Purpose : handle interrupts from SCSI device 19228c2ecf20Sopenharmony_ci * Params : host - host to process 19238c2ecf20Sopenharmony_ci * Returns : INTR_PROCESS if expecting another SBIC interrupt 19248c2ecf20Sopenharmony_ci * INTR_IDLE if no interrupt 19258c2ecf20Sopenharmony_ci * INTR_NEXT_COMMAND if we have finished processing the command 19268c2ecf20Sopenharmony_ci */ 19278c2ecf20Sopenharmony_cistatic 19288c2ecf20Sopenharmony_ciintr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq) 19298c2ecf20Sopenharmony_ci{ 19308c2ecf20Sopenharmony_ci unsigned int asr, ssr; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci asr = sbic_arm_read(host, SBIC_ASR); 19338c2ecf20Sopenharmony_ci if (!(asr & ASR_INT)) 19348c2ecf20Sopenharmony_ci return INTR_IDLE; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci ssr = sbic_arm_read(host, SBIC_SSR); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_PHASES) 19398c2ecf20Sopenharmony_ci print_sbic_status(asr, ssr, host->scsi.phase); 19408c2ecf20Sopenharmony_ci#endif 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci ADD_STATUS(8, ssr, host->scsi.phase, in_irq); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_ci if (host->SCpnt && !host->scsi.disconnectable) 19458c2ecf20Sopenharmony_ci ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci switch (ssr) { 19488c2ecf20Sopenharmony_ci case 0x00: /* reset state - not advanced */ 19498c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", 19508c2ecf20Sopenharmony_ci host->host->host_no); 19518c2ecf20Sopenharmony_ci /* setup sbic - WD33C93A */ 19528c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_OWNID, OWNID_EAF | host->host->this_id); 19538c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CMND, CMND_RESET); 19548c2ecf20Sopenharmony_ci return INTR_IDLE; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci case 0x01: /* reset state - advanced */ 19578c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI); 19588c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_TIMEOUT, TIMEOUT_TIME); 19598c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA); 19608c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP); 19618c2ecf20Sopenharmony_ci msgqueue_flush(&host->scsi.msgs); 19628c2ecf20Sopenharmony_ci return INTR_IDLE; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci case 0x41: /* unexpected disconnect aborted command */ 19658c2ecf20Sopenharmony_ci acornscsi_disconnect_unexpected(host); 19668c2ecf20Sopenharmony_ci return INTR_NEXT_COMMAND; 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci switch (host->scsi.phase) { 19708c2ecf20Sopenharmony_ci case PHASE_CONNECTING: /* STATE: command removed from issue queue */ 19718c2ecf20Sopenharmony_ci switch (ssr) { 19728c2ecf20Sopenharmony_ci case 0x11: /* -> PHASE_CONNECTED */ 19738c2ecf20Sopenharmony_ci /* BUS FREE -> SELECTION */ 19748c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_CONNECTED; 19758c2ecf20Sopenharmony_ci msgqueue_flush(&host->scsi.msgs); 19768c2ecf20Sopenharmony_ci host->dma.transferred = host->scsi.SCp.scsi_xferred; 19778c2ecf20Sopenharmony_ci /* 33C93 gives next interrupt indicating bus phase */ 19788c2ecf20Sopenharmony_ci asr = sbic_arm_read(host, SBIC_ASR); 19798c2ecf20Sopenharmony_ci if (!(asr & ASR_INT)) 19808c2ecf20Sopenharmony_ci break; 19818c2ecf20Sopenharmony_ci ssr = sbic_arm_read(host, SBIC_SSR); 19828c2ecf20Sopenharmony_ci ADD_STATUS(8, ssr, host->scsi.phase, 1); 19838c2ecf20Sopenharmony_ci ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1); 19848c2ecf20Sopenharmony_ci goto connected; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci case 0x42: /* select timed out */ 19878c2ecf20Sopenharmony_ci /* -> PHASE_IDLE */ 19888c2ecf20Sopenharmony_ci acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); 19898c2ecf20Sopenharmony_ci return INTR_NEXT_COMMAND; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ 19928c2ecf20Sopenharmony_ci /* BUS FREE -> RESELECTION */ 19938c2ecf20Sopenharmony_ci host->origSCpnt = host->SCpnt; 19948c2ecf20Sopenharmony_ci host->SCpnt = NULL; 19958c2ecf20Sopenharmony_ci msgqueue_flush(&host->scsi.msgs); 19968c2ecf20Sopenharmony_ci acornscsi_reconnect(host); 19978c2ecf20Sopenharmony_ci break; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci default: 20008c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", 20018c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 20028c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 20038c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 20048c2ecf20Sopenharmony_ci } 20058c2ecf20Sopenharmony_ci return INTR_PROCESSING; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci connected: 20088c2ecf20Sopenharmony_ci case PHASE_CONNECTED: /* STATE: device selected ok */ 20098c2ecf20Sopenharmony_ci switch (ssr) { 20108c2ecf20Sopenharmony_ci#ifdef NONSTANDARD 20118c2ecf20Sopenharmony_ci case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ 20128c2ecf20Sopenharmony_ci /* SELECTION -> COMMAND */ 20138c2ecf20Sopenharmony_ci acornscsi_sendcommand(host); 20148c2ecf20Sopenharmony_ci break; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci case 0x8b: /* -> PHASE_STATUS */ 20178c2ecf20Sopenharmony_ci /* SELECTION -> STATUS */ 20188c2ecf20Sopenharmony_ci acornscsi_readstatusbyte(host); 20198c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_STATUSIN; 20208c2ecf20Sopenharmony_ci break; 20218c2ecf20Sopenharmony_ci#endif 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci case 0x8e: /* -> PHASE_MSGOUT */ 20248c2ecf20Sopenharmony_ci /* SELECTION ->MESSAGE OUT */ 20258c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_MSGOUT; 20268c2ecf20Sopenharmony_ci acornscsi_buildmessages(host); 20278c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 20288c2ecf20Sopenharmony_ci break; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci /* these should not happen */ 20318c2ecf20Sopenharmony_ci case 0x85: /* target disconnected */ 20328c2ecf20Sopenharmony_ci acornscsi_done(host, &host->SCpnt, DID_ERROR); 20338c2ecf20Sopenharmony_ci break; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci default: 20368c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", 20378c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 20388c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 20398c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci return INTR_PROCESSING; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ 20448c2ecf20Sopenharmony_ci /* 20458c2ecf20Sopenharmony_ci * SCSI standard says that MESSAGE OUT phases can be followed by a 20468c2ecf20Sopenharmony_ci * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase 20478c2ecf20Sopenharmony_ci */ 20488c2ecf20Sopenharmony_ci switch (ssr) { 20498c2ecf20Sopenharmony_ci case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ 20508c2ecf20Sopenharmony_ci case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ 20518c2ecf20Sopenharmony_ci /* MESSAGE OUT -> COMMAND */ 20528c2ecf20Sopenharmony_ci acornscsi_sendcommand(host); 20538c2ecf20Sopenharmony_ci break; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci case 0x8b: /* -> PHASE_STATUS */ 20568c2ecf20Sopenharmony_ci case 0x1b: /* -> PHASE_STATUS */ 20578c2ecf20Sopenharmony_ci /* MESSAGE OUT -> STATUS */ 20588c2ecf20Sopenharmony_ci acornscsi_readstatusbyte(host); 20598c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_STATUSIN; 20608c2ecf20Sopenharmony_ci break; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci case 0x8e: /* -> PHASE_MSGOUT */ 20638c2ecf20Sopenharmony_ci /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ 20648c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 20658c2ecf20Sopenharmony_ci break; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ 20688c2ecf20Sopenharmony_ci case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ 20698c2ecf20Sopenharmony_ci /* MESSAGE OUT -> MESSAGE IN */ 20708c2ecf20Sopenharmony_ci acornscsi_message(host); 20718c2ecf20Sopenharmony_ci break; 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci default: 20748c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", 20758c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 20768c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci return INTR_PROCESSING; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci case PHASE_COMMAND: /* STATE: connected & command sent */ 20818c2ecf20Sopenharmony_ci switch (ssr) { 20828c2ecf20Sopenharmony_ci case 0x18: /* -> PHASE_DATAOUT */ 20838c2ecf20Sopenharmony_ci /* COMMAND -> DATA OUT */ 20848c2ecf20Sopenharmony_ci if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) 20858c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 20868c2ecf20Sopenharmony_ci acornscsi_dma_setup(host, DMA_OUT); 20878c2ecf20Sopenharmony_ci if (!acornscsi_starttransfer(host)) 20888c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 20898c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_DATAOUT; 20908c2ecf20Sopenharmony_ci return INTR_IDLE; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci case 0x19: /* -> PHASE_DATAIN */ 20938c2ecf20Sopenharmony_ci /* COMMAND -> DATA IN */ 20948c2ecf20Sopenharmony_ci if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) 20958c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 20968c2ecf20Sopenharmony_ci acornscsi_dma_setup(host, DMA_IN); 20978c2ecf20Sopenharmony_ci if (!acornscsi_starttransfer(host)) 20988c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 20998c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_DATAIN; 21008c2ecf20Sopenharmony_ci return INTR_IDLE; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci case 0x1b: /* -> PHASE_STATUS */ 21038c2ecf20Sopenharmony_ci /* COMMAND -> STATUS */ 21048c2ecf20Sopenharmony_ci acornscsi_readstatusbyte(host); 21058c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_STATUSIN; 21068c2ecf20Sopenharmony_ci break; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci case 0x1e: /* -> PHASE_MSGOUT */ 21098c2ecf20Sopenharmony_ci /* COMMAND -> MESSAGE OUT */ 21108c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 21118c2ecf20Sopenharmony_ci break; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ 21148c2ecf20Sopenharmony_ci /* COMMAND -> MESSAGE IN */ 21158c2ecf20Sopenharmony_ci acornscsi_message(host); 21168c2ecf20Sopenharmony_ci break; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci default: 21198c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", 21208c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 21218c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 21228c2ecf20Sopenharmony_ci } 21238c2ecf20Sopenharmony_ci return INTR_PROCESSING; 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */ 21268c2ecf20Sopenharmony_ci if (ssr == 0x85) { /* -> PHASE_IDLE */ 21278c2ecf20Sopenharmony_ci host->scsi.disconnectable = 1; 21288c2ecf20Sopenharmony_ci host->scsi.reconnected.tag = 0; 21298c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_IDLE; 21308c2ecf20Sopenharmony_ci host->stats.disconnects += 1; 21318c2ecf20Sopenharmony_ci } else { 21328c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", 21338c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 21348c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci return INTR_NEXT_COMMAND; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci case PHASE_IDLE: /* STATE: disconnected */ 21398c2ecf20Sopenharmony_ci if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ 21408c2ecf20Sopenharmony_ci acornscsi_reconnect(host); 21418c2ecf20Sopenharmony_ci else { 21428c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", 21438c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 21448c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 21458c2ecf20Sopenharmony_ci } 21468c2ecf20Sopenharmony_ci return INTR_PROCESSING; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */ 21498c2ecf20Sopenharmony_ci /* 21508c2ecf20Sopenharmony_ci * Command reconnected - if MESGIN, get message - it may be 21518c2ecf20Sopenharmony_ci * the tag. If not, get command out of disconnected queue 21528c2ecf20Sopenharmony_ci */ 21538c2ecf20Sopenharmony_ci /* 21548c2ecf20Sopenharmony_ci * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, 21558c2ecf20Sopenharmony_ci * reconnect I_T_L command 21568c2ecf20Sopenharmony_ci */ 21578c2ecf20Sopenharmony_ci if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) 21588c2ecf20Sopenharmony_ci return INTR_IDLE; 21598c2ecf20Sopenharmony_ci ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); 21608c2ecf20Sopenharmony_ci switch (ssr) { 21618c2ecf20Sopenharmony_ci case 0x88: /* data out phase */ 21628c2ecf20Sopenharmony_ci /* -> PHASE_DATAOUT */ 21638c2ecf20Sopenharmony_ci /* MESSAGE IN -> DATA OUT */ 21648c2ecf20Sopenharmony_ci acornscsi_dma_setup(host, DMA_OUT); 21658c2ecf20Sopenharmony_ci if (!acornscsi_starttransfer(host)) 21668c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 21678c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_DATAOUT; 21688c2ecf20Sopenharmony_ci return INTR_IDLE; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci case 0x89: /* data in phase */ 21718c2ecf20Sopenharmony_ci /* -> PHASE_DATAIN */ 21728c2ecf20Sopenharmony_ci /* MESSAGE IN -> DATA IN */ 21738c2ecf20Sopenharmony_ci acornscsi_dma_setup(host, DMA_IN); 21748c2ecf20Sopenharmony_ci if (!acornscsi_starttransfer(host)) 21758c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 21768c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_DATAIN; 21778c2ecf20Sopenharmony_ci return INTR_IDLE; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci case 0x8a: /* command out */ 21808c2ecf20Sopenharmony_ci /* MESSAGE IN -> COMMAND */ 21818c2ecf20Sopenharmony_ci acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ 21828c2ecf20Sopenharmony_ci break; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci case 0x8b: /* status in */ 21858c2ecf20Sopenharmony_ci /* -> PHASE_STATUSIN */ 21868c2ecf20Sopenharmony_ci /* MESSAGE IN -> STATUS */ 21878c2ecf20Sopenharmony_ci acornscsi_readstatusbyte(host); 21888c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_STATUSIN; 21898c2ecf20Sopenharmony_ci break; 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci case 0x8e: /* message out */ 21928c2ecf20Sopenharmony_ci /* -> PHASE_MSGOUT */ 21938c2ecf20Sopenharmony_ci /* MESSAGE IN -> MESSAGE OUT */ 21948c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 21958c2ecf20Sopenharmony_ci break; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci case 0x8f: /* message in */ 21988c2ecf20Sopenharmony_ci acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ 21998c2ecf20Sopenharmony_ci break; 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci default: 22028c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", 22038c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 22048c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci return INTR_PROCESSING; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci case PHASE_DATAIN: /* STATE: transferred data in */ 22098c2ecf20Sopenharmony_ci /* 22108c2ecf20Sopenharmony_ci * This is simple - if we disconnect then the DMA address & count is 22118c2ecf20Sopenharmony_ci * correct. 22128c2ecf20Sopenharmony_ci */ 22138c2ecf20Sopenharmony_ci switch (ssr) { 22148c2ecf20Sopenharmony_ci case 0x19: /* -> PHASE_DATAIN */ 22158c2ecf20Sopenharmony_ci case 0x89: /* -> PHASE_DATAIN */ 22168c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 22178c2ecf20Sopenharmony_ci return INTR_IDLE; 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci case 0x1b: /* -> PHASE_STATUSIN */ 22208c2ecf20Sopenharmony_ci case 0x4b: /* -> PHASE_STATUSIN */ 22218c2ecf20Sopenharmony_ci case 0x8b: /* -> PHASE_STATUSIN */ 22228c2ecf20Sopenharmony_ci /* DATA IN -> STATUS */ 22238c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) - 22248c2ecf20Sopenharmony_ci acornscsi_sbic_xfcount(host); 22258c2ecf20Sopenharmony_ci acornscsi_dma_stop(host); 22268c2ecf20Sopenharmony_ci acornscsi_readstatusbyte(host); 22278c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_STATUSIN; 22288c2ecf20Sopenharmony_ci break; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci case 0x1e: /* -> PHASE_MSGOUT */ 22318c2ecf20Sopenharmony_ci case 0x4e: /* -> PHASE_MSGOUT */ 22328c2ecf20Sopenharmony_ci case 0x8e: /* -> PHASE_MSGOUT */ 22338c2ecf20Sopenharmony_ci /* DATA IN -> MESSAGE OUT */ 22348c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) - 22358c2ecf20Sopenharmony_ci acornscsi_sbic_xfcount(host); 22368c2ecf20Sopenharmony_ci acornscsi_dma_stop(host); 22378c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 22388c2ecf20Sopenharmony_ci break; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci case 0x1f: /* message in */ 22418c2ecf20Sopenharmony_ci case 0x4f: /* message in */ 22428c2ecf20Sopenharmony_ci case 0x8f: /* message in */ 22438c2ecf20Sopenharmony_ci /* DATA IN -> MESSAGE IN */ 22448c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) - 22458c2ecf20Sopenharmony_ci acornscsi_sbic_xfcount(host); 22468c2ecf20Sopenharmony_ci acornscsi_dma_stop(host); 22478c2ecf20Sopenharmony_ci acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ 22488c2ecf20Sopenharmony_ci break; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci default: 22518c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", 22528c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 22538c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 22548c2ecf20Sopenharmony_ci } 22558c2ecf20Sopenharmony_ci return INTR_PROCESSING; 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci case PHASE_DATAOUT: /* STATE: transferred data out */ 22588c2ecf20Sopenharmony_ci /* 22598c2ecf20Sopenharmony_ci * This is more complicated - if we disconnect, the DMA could be 12 22608c2ecf20Sopenharmony_ci * bytes ahead of us. We need to correct this. 22618c2ecf20Sopenharmony_ci */ 22628c2ecf20Sopenharmony_ci switch (ssr) { 22638c2ecf20Sopenharmony_ci case 0x18: /* -> PHASE_DATAOUT */ 22648c2ecf20Sopenharmony_ci case 0x88: /* -> PHASE_DATAOUT */ 22658c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 22668c2ecf20Sopenharmony_ci return INTR_IDLE; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci case 0x1b: /* -> PHASE_STATUSIN */ 22698c2ecf20Sopenharmony_ci case 0x4b: /* -> PHASE_STATUSIN */ 22708c2ecf20Sopenharmony_ci case 0x8b: /* -> PHASE_STATUSIN */ 22718c2ecf20Sopenharmony_ci /* DATA OUT -> STATUS */ 22728c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) - 22738c2ecf20Sopenharmony_ci acornscsi_sbic_xfcount(host); 22748c2ecf20Sopenharmony_ci acornscsi_dma_stop(host); 22758c2ecf20Sopenharmony_ci acornscsi_dma_adjust(host); 22768c2ecf20Sopenharmony_ci acornscsi_readstatusbyte(host); 22778c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_STATUSIN; 22788c2ecf20Sopenharmony_ci break; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci case 0x1e: /* -> PHASE_MSGOUT */ 22818c2ecf20Sopenharmony_ci case 0x4e: /* -> PHASE_MSGOUT */ 22828c2ecf20Sopenharmony_ci case 0x8e: /* -> PHASE_MSGOUT */ 22838c2ecf20Sopenharmony_ci /* DATA OUT -> MESSAGE OUT */ 22848c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) - 22858c2ecf20Sopenharmony_ci acornscsi_sbic_xfcount(host); 22868c2ecf20Sopenharmony_ci acornscsi_dma_stop(host); 22878c2ecf20Sopenharmony_ci acornscsi_dma_adjust(host); 22888c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 22898c2ecf20Sopenharmony_ci break; 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci case 0x1f: /* message in */ 22928c2ecf20Sopenharmony_ci case 0x4f: /* message in */ 22938c2ecf20Sopenharmony_ci case 0x8f: /* message in */ 22948c2ecf20Sopenharmony_ci /* DATA OUT -> MESSAGE IN */ 22958c2ecf20Sopenharmony_ci host->scsi.SCp.scsi_xferred = scsi_bufflen(host->SCpnt) - 22968c2ecf20Sopenharmony_ci acornscsi_sbic_xfcount(host); 22978c2ecf20Sopenharmony_ci acornscsi_dma_stop(host); 22988c2ecf20Sopenharmony_ci acornscsi_dma_adjust(host); 22998c2ecf20Sopenharmony_ci acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ 23008c2ecf20Sopenharmony_ci break; 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci default: 23038c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", 23048c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 23058c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 23068c2ecf20Sopenharmony_ci } 23078c2ecf20Sopenharmony_ci return INTR_PROCESSING; 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci case PHASE_STATUSIN: /* STATE: status in complete */ 23108c2ecf20Sopenharmony_ci switch (ssr) { 23118c2ecf20Sopenharmony_ci case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ 23128c2ecf20Sopenharmony_ci case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ 23138c2ecf20Sopenharmony_ci /* STATUS -> MESSAGE IN */ 23148c2ecf20Sopenharmony_ci acornscsi_message(host); 23158c2ecf20Sopenharmony_ci break; 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci case 0x1e: /* -> PHASE_MSGOUT */ 23188c2ecf20Sopenharmony_ci case 0x8e: /* -> PHASE_MSGOUT */ 23198c2ecf20Sopenharmony_ci /* STATUS -> MESSAGE OUT */ 23208c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 23218c2ecf20Sopenharmony_ci break; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci default: 23248c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", 23258c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 23268c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 23278c2ecf20Sopenharmony_ci } 23288c2ecf20Sopenharmony_ci return INTR_PROCESSING; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci case PHASE_MSGIN: /* STATE: message in */ 23318c2ecf20Sopenharmony_ci switch (ssr) { 23328c2ecf20Sopenharmony_ci case 0x1e: /* -> PHASE_MSGOUT */ 23338c2ecf20Sopenharmony_ci case 0x4e: /* -> PHASE_MSGOUT */ 23348c2ecf20Sopenharmony_ci case 0x8e: /* -> PHASE_MSGOUT */ 23358c2ecf20Sopenharmony_ci /* MESSAGE IN -> MESSAGE OUT */ 23368c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 23378c2ecf20Sopenharmony_ci break; 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ 23408c2ecf20Sopenharmony_ci case 0x2f: 23418c2ecf20Sopenharmony_ci case 0x4f: 23428c2ecf20Sopenharmony_ci case 0x8f: 23438c2ecf20Sopenharmony_ci acornscsi_message(host); 23448c2ecf20Sopenharmony_ci break; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci case 0x85: 23478c2ecf20Sopenharmony_ci printk("scsi%d.%c: strange message in disconnection\n", 23488c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host)); 23498c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 23508c2ecf20Sopenharmony_ci acornscsi_done(host, &host->SCpnt, DID_ERROR); 23518c2ecf20Sopenharmony_ci break; 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci default: 23548c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", 23558c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 23568c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 23578c2ecf20Sopenharmony_ci } 23588c2ecf20Sopenharmony_ci return INTR_PROCESSING; 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci case PHASE_DONE: /* STATE: received status & message */ 23618c2ecf20Sopenharmony_ci switch (ssr) { 23628c2ecf20Sopenharmony_ci case 0x85: /* -> PHASE_IDLE */ 23638c2ecf20Sopenharmony_ci acornscsi_done(host, &host->SCpnt, DID_OK); 23648c2ecf20Sopenharmony_ci return INTR_NEXT_COMMAND; 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci case 0x1e: 23678c2ecf20Sopenharmony_ci case 0x8e: 23688c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 23698c2ecf20Sopenharmony_ci break; 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci default: 23728c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", 23738c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 23748c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 23758c2ecf20Sopenharmony_ci } 23768c2ecf20Sopenharmony_ci return INTR_PROCESSING; 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci case PHASE_ABORTED: 23798c2ecf20Sopenharmony_ci switch (ssr) { 23808c2ecf20Sopenharmony_ci case 0x85: 23818c2ecf20Sopenharmony_ci if (host->SCpnt) 23828c2ecf20Sopenharmony_ci acornscsi_done(host, &host->SCpnt, DID_ABORT); 23838c2ecf20Sopenharmony_ci else { 23848c2ecf20Sopenharmony_ci clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun, 23858c2ecf20Sopenharmony_ci host->busyluns); 23868c2ecf20Sopenharmony_ci host->scsi.phase = PHASE_IDLE; 23878c2ecf20Sopenharmony_ci } 23888c2ecf20Sopenharmony_ci return INTR_NEXT_COMMAND; 23898c2ecf20Sopenharmony_ci 23908c2ecf20Sopenharmony_ci case 0x1e: 23918c2ecf20Sopenharmony_ci case 0x2e: 23928c2ecf20Sopenharmony_ci case 0x4e: 23938c2ecf20Sopenharmony_ci case 0x8e: 23948c2ecf20Sopenharmony_ci acornscsi_sendmessage(host); 23958c2ecf20Sopenharmony_ci break; 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci default: 23988c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", 23998c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 24008c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 24018c2ecf20Sopenharmony_ci } 24028c2ecf20Sopenharmony_ci return INTR_PROCESSING; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci default: 24058c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n", 24068c2ecf20Sopenharmony_ci host->host->host_no, acornscsi_target(host), ssr); 24078c2ecf20Sopenharmony_ci acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci return INTR_PROCESSING; 24108c2ecf20Sopenharmony_ci} 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci/* 24138c2ecf20Sopenharmony_ci * Prototype: void acornscsi_intr(int irq, void *dev_id) 24148c2ecf20Sopenharmony_ci * Purpose : handle interrupts from Acorn SCSI card 24158c2ecf20Sopenharmony_ci * Params : irq - interrupt number 24168c2ecf20Sopenharmony_ci * dev_id - device specific data (AS_Host structure) 24178c2ecf20Sopenharmony_ci */ 24188c2ecf20Sopenharmony_cistatic irqreturn_t 24198c2ecf20Sopenharmony_ciacornscsi_intr(int irq, void *dev_id) 24208c2ecf20Sopenharmony_ci{ 24218c2ecf20Sopenharmony_ci AS_Host *host = (AS_Host *)dev_id; 24228c2ecf20Sopenharmony_ci intr_ret_t ret; 24238c2ecf20Sopenharmony_ci int iostatus; 24248c2ecf20Sopenharmony_ci int in_irq = 0; 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci do { 24278c2ecf20Sopenharmony_ci ret = INTR_IDLE; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci iostatus = readb(host->fast + INT_REG); 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci if (iostatus & 2) { 24328c2ecf20Sopenharmony_ci acornscsi_dma_intr(host); 24338c2ecf20Sopenharmony_ci iostatus = readb(host->fast + INT_REG); 24348c2ecf20Sopenharmony_ci } 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci if (iostatus & 8) 24378c2ecf20Sopenharmony_ci ret = acornscsi_sbicintr(host, in_irq); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci /* 24408c2ecf20Sopenharmony_ci * If we have a transfer pending, start it. 24418c2ecf20Sopenharmony_ci * Only start it if the interface has already started transferring 24428c2ecf20Sopenharmony_ci * it's data 24438c2ecf20Sopenharmony_ci */ 24448c2ecf20Sopenharmony_ci if (host->dma.xfer_required) 24458c2ecf20Sopenharmony_ci acornscsi_dma_xfer(host); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci if (ret == INTR_NEXT_COMMAND) 24488c2ecf20Sopenharmony_ci ret = acornscsi_kick(host); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci in_irq = 1; 24518c2ecf20Sopenharmony_ci } while (ret != INTR_IDLE); 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci return IRQ_HANDLED; 24548c2ecf20Sopenharmony_ci} 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci/*============================================================================================= 24578c2ecf20Sopenharmony_ci * Interfaces between interrupt handler and rest of scsi code 24588c2ecf20Sopenharmony_ci */ 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci/* 24618c2ecf20Sopenharmony_ci * Function : acornscsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) 24628c2ecf20Sopenharmony_ci * Purpose : queues a SCSI command 24638c2ecf20Sopenharmony_ci * Params : cmd - SCSI command 24648c2ecf20Sopenharmony_ci * done - function called on completion, with pointer to command descriptor 24658c2ecf20Sopenharmony_ci * Returns : 0, or < 0 on error. 24668c2ecf20Sopenharmony_ci */ 24678c2ecf20Sopenharmony_cistatic int acornscsi_queuecmd_lck(struct scsi_cmnd *SCpnt, 24688c2ecf20Sopenharmony_ci void (*done)(struct scsi_cmnd *)) 24698c2ecf20Sopenharmony_ci{ 24708c2ecf20Sopenharmony_ci AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci if (!done) { 24738c2ecf20Sopenharmony_ci /* there should be some way of rejecting errors like this without panicing... */ 24748c2ecf20Sopenharmony_ci panic("scsi%d: queuecommand called with NULL done function [cmd=%p]", 24758c2ecf20Sopenharmony_ci host->host->host_no, SCpnt); 24768c2ecf20Sopenharmony_ci return -EINVAL; 24778c2ecf20Sopenharmony_ci } 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE) 24808c2ecf20Sopenharmony_ci if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) { 24818c2ecf20Sopenharmony_ci printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", 24828c2ecf20Sopenharmony_ci host->host->host_no, '0' + SCpnt->device->id); 24838c2ecf20Sopenharmony_ci SCpnt->result = DID_NO_CONNECT << 16; 24848c2ecf20Sopenharmony_ci done(SCpnt); 24858c2ecf20Sopenharmony_ci return 0; 24868c2ecf20Sopenharmony_ci } 24878c2ecf20Sopenharmony_ci#endif 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci SCpnt->scsi_done = done; 24908c2ecf20Sopenharmony_ci SCpnt->host_scribble = NULL; 24918c2ecf20Sopenharmony_ci SCpnt->result = 0; 24928c2ecf20Sopenharmony_ci SCpnt->tag = 0; 24938c2ecf20Sopenharmony_ci SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]); 24948c2ecf20Sopenharmony_ci SCpnt->SCp.sent_command = 0; 24958c2ecf20Sopenharmony_ci SCpnt->SCp.scsi_xferred = 0; 24968c2ecf20Sopenharmony_ci 24978c2ecf20Sopenharmony_ci init_SCp(SCpnt); 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci host->stats.queues += 1; 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci { 25028c2ecf20Sopenharmony_ci unsigned long flags; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) { 25058c2ecf20Sopenharmony_ci SCpnt->result = DID_ERROR << 16; 25068c2ecf20Sopenharmony_ci done(SCpnt); 25078c2ecf20Sopenharmony_ci return 0; 25088c2ecf20Sopenharmony_ci } 25098c2ecf20Sopenharmony_ci local_irq_save(flags); 25108c2ecf20Sopenharmony_ci if (host->scsi.phase == PHASE_IDLE) 25118c2ecf20Sopenharmony_ci acornscsi_kick(host); 25128c2ecf20Sopenharmony_ci local_irq_restore(flags); 25138c2ecf20Sopenharmony_ci } 25148c2ecf20Sopenharmony_ci return 0; 25158c2ecf20Sopenharmony_ci} 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ciDEF_SCSI_QCMD(acornscsi_queuecmd) 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci/* 25208c2ecf20Sopenharmony_ci * Prototype: void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1, struct scsi_cmnd **SCpntp2, int result) 25218c2ecf20Sopenharmony_ci * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 25228c2ecf20Sopenharmony_ci * Params : SCpntp1 - pointer to command to return 25238c2ecf20Sopenharmony_ci * SCpntp2 - pointer to command to check 25248c2ecf20Sopenharmony_ci * result - result to pass back to mid-level done function 25258c2ecf20Sopenharmony_ci * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. 25268c2ecf20Sopenharmony_ci */ 25278c2ecf20Sopenharmony_cistatic inline void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1, 25288c2ecf20Sopenharmony_ci struct scsi_cmnd **SCpntp2, 25298c2ecf20Sopenharmony_ci int result) 25308c2ecf20Sopenharmony_ci{ 25318c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt = *SCpntp1; 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci if (SCpnt) { 25348c2ecf20Sopenharmony_ci *SCpntp1 = NULL; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci SCpnt->result = result; 25378c2ecf20Sopenharmony_ci SCpnt->scsi_done(SCpnt); 25388c2ecf20Sopenharmony_ci } 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci if (SCpnt == *SCpntp2) 25418c2ecf20Sopenharmony_ci *SCpntp2 = NULL; 25428c2ecf20Sopenharmony_ci} 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_cienum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci/* 25478c2ecf20Sopenharmony_ci * Prototype: enum res acornscsi_do_abort(struct scsi_cmnd *SCpnt) 25488c2ecf20Sopenharmony_ci * Purpose : abort a command on this host 25498c2ecf20Sopenharmony_ci * Params : SCpnt - command to abort 25508c2ecf20Sopenharmony_ci * Returns : our abort status 25518c2ecf20Sopenharmony_ci */ 25528c2ecf20Sopenharmony_cistatic enum res_abort acornscsi_do_abort(AS_Host *host, struct scsi_cmnd *SCpnt) 25538c2ecf20Sopenharmony_ci{ 25548c2ecf20Sopenharmony_ci enum res_abort res = res_not_running; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci if (queue_remove_cmd(&host->queues.issue, SCpnt)) { 25578c2ecf20Sopenharmony_ci /* 25588c2ecf20Sopenharmony_ci * The command was on the issue queue, and has not been 25598c2ecf20Sopenharmony_ci * issued yet. We can remove the command from the queue, 25608c2ecf20Sopenharmony_ci * and acknowledge the abort. Neither the devices nor the 25618c2ecf20Sopenharmony_ci * interface know about the command. 25628c2ecf20Sopenharmony_ci */ 25638c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 25648c2ecf20Sopenharmony_ci printk("on issue queue "); 25658c2ecf20Sopenharmony_ci//#endif 25668c2ecf20Sopenharmony_ci res = res_success; 25678c2ecf20Sopenharmony_ci } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) { 25688c2ecf20Sopenharmony_ci /* 25698c2ecf20Sopenharmony_ci * The command was on the disconnected queue. Simply 25708c2ecf20Sopenharmony_ci * acknowledge the abort condition, and when the target 25718c2ecf20Sopenharmony_ci * reconnects, we will give it an ABORT message. The 25728c2ecf20Sopenharmony_ci * target should then disconnect, and we will clear 25738c2ecf20Sopenharmony_ci * the busylun bit. 25748c2ecf20Sopenharmony_ci */ 25758c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 25768c2ecf20Sopenharmony_ci printk("on disconnected queue "); 25778c2ecf20Sopenharmony_ci//#endif 25788c2ecf20Sopenharmony_ci res = res_success; 25798c2ecf20Sopenharmony_ci } else if (host->SCpnt == SCpnt) { 25808c2ecf20Sopenharmony_ci unsigned long flags; 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 25838c2ecf20Sopenharmony_ci printk("executing "); 25848c2ecf20Sopenharmony_ci//#endif 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci local_irq_save(flags); 25878c2ecf20Sopenharmony_ci switch (host->scsi.phase) { 25888c2ecf20Sopenharmony_ci /* 25898c2ecf20Sopenharmony_ci * If the interface is idle, and the command is 'disconnectable', 25908c2ecf20Sopenharmony_ci * then it is the same as on the disconnected queue. We simply 25918c2ecf20Sopenharmony_ci * remove all traces of the command. When the target reconnects, 25928c2ecf20Sopenharmony_ci * we will give it an ABORT message since the command could not 25938c2ecf20Sopenharmony_ci * be found. When the target finally disconnects, we will clear 25948c2ecf20Sopenharmony_ci * the busylun bit. 25958c2ecf20Sopenharmony_ci */ 25968c2ecf20Sopenharmony_ci case PHASE_IDLE: 25978c2ecf20Sopenharmony_ci if (host->scsi.disconnectable) { 25988c2ecf20Sopenharmony_ci host->scsi.disconnectable = 0; 25998c2ecf20Sopenharmony_ci host->SCpnt = NULL; 26008c2ecf20Sopenharmony_ci res = res_success; 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci break; 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci /* 26058c2ecf20Sopenharmony_ci * If the command has connected and done nothing further, 26068c2ecf20Sopenharmony_ci * simply force a disconnect. We also need to clear the 26078c2ecf20Sopenharmony_ci * busylun bit. 26088c2ecf20Sopenharmony_ci */ 26098c2ecf20Sopenharmony_ci case PHASE_CONNECTED: 26108c2ecf20Sopenharmony_ci sbic_arm_write(host, SBIC_CMND, CMND_DISCONNECT); 26118c2ecf20Sopenharmony_ci host->SCpnt = NULL; 26128c2ecf20Sopenharmony_ci res = res_success_clear; 26138c2ecf20Sopenharmony_ci break; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci default: 26168c2ecf20Sopenharmony_ci acornscsi_abortcmd(host, host->SCpnt->tag); 26178c2ecf20Sopenharmony_ci res = res_snooze; 26188c2ecf20Sopenharmony_ci } 26198c2ecf20Sopenharmony_ci local_irq_restore(flags); 26208c2ecf20Sopenharmony_ci } else if (host->origSCpnt == SCpnt) { 26218c2ecf20Sopenharmony_ci /* 26228c2ecf20Sopenharmony_ci * The command will be executed next, but a command 26238c2ecf20Sopenharmony_ci * is currently using the interface. This is similar to 26248c2ecf20Sopenharmony_ci * being on the issue queue, except the busylun bit has 26258c2ecf20Sopenharmony_ci * been set. 26268c2ecf20Sopenharmony_ci */ 26278c2ecf20Sopenharmony_ci host->origSCpnt = NULL; 26288c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 26298c2ecf20Sopenharmony_ci printk("waiting for execution "); 26308c2ecf20Sopenharmony_ci//#endif 26318c2ecf20Sopenharmony_ci res = res_success_clear; 26328c2ecf20Sopenharmony_ci } else 26338c2ecf20Sopenharmony_ci printk("unknown "); 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci return res; 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci/* 26398c2ecf20Sopenharmony_ci * Prototype: int acornscsi_abort(struct scsi_cmnd *SCpnt) 26408c2ecf20Sopenharmony_ci * Purpose : abort a command on this host 26418c2ecf20Sopenharmony_ci * Params : SCpnt - command to abort 26428c2ecf20Sopenharmony_ci * Returns : one of SCSI_ABORT_ macros 26438c2ecf20Sopenharmony_ci */ 26448c2ecf20Sopenharmony_ciint acornscsi_abort(struct scsi_cmnd *SCpnt) 26458c2ecf20Sopenharmony_ci{ 26468c2ecf20Sopenharmony_ci AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata; 26478c2ecf20Sopenharmony_ci int result; 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci host->stats.aborts += 1; 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_ABORT) 26528c2ecf20Sopenharmony_ci { 26538c2ecf20Sopenharmony_ci int asr, ssr; 26548c2ecf20Sopenharmony_ci asr = sbic_arm_read(host, SBIC_ASR); 26558c2ecf20Sopenharmony_ci ssr = sbic_arm_read(host, SBIC_SSR); 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci printk(KERN_WARNING "acornscsi_abort: "); 26588c2ecf20Sopenharmony_ci print_sbic_status(asr, ssr, host->scsi.phase); 26598c2ecf20Sopenharmony_ci acornscsi_dumplog(host, SCpnt->device->id); 26608c2ecf20Sopenharmony_ci } 26618c2ecf20Sopenharmony_ci#endif 26628c2ecf20Sopenharmony_ci 26638c2ecf20Sopenharmony_ci printk("scsi%d: ", host->host->host_no); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci switch (acornscsi_do_abort(host, SCpnt)) { 26668c2ecf20Sopenharmony_ci /* 26678c2ecf20Sopenharmony_ci * We managed to find the command and cleared it out. 26688c2ecf20Sopenharmony_ci * We do not expect the command to be executing on the 26698c2ecf20Sopenharmony_ci * target, but we have set the busylun bit. 26708c2ecf20Sopenharmony_ci */ 26718c2ecf20Sopenharmony_ci case res_success_clear: 26728c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 26738c2ecf20Sopenharmony_ci printk("clear "); 26748c2ecf20Sopenharmony_ci//#endif 26758c2ecf20Sopenharmony_ci clear_bit(SCpnt->device->id * 8 + 26768c2ecf20Sopenharmony_ci (u8)(SCpnt->device->lun & 0x7), host->busyluns); 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci /* 26798c2ecf20Sopenharmony_ci * We found the command, and cleared it out. Either 26808c2ecf20Sopenharmony_ci * the command is still known to be executing on the 26818c2ecf20Sopenharmony_ci * target, or the busylun bit is not set. 26828c2ecf20Sopenharmony_ci */ 26838c2ecf20Sopenharmony_ci case res_success: 26848c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 26858c2ecf20Sopenharmony_ci printk("success\n"); 26868c2ecf20Sopenharmony_ci//#endif 26878c2ecf20Sopenharmony_ci result = SUCCESS; 26888c2ecf20Sopenharmony_ci break; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci /* 26918c2ecf20Sopenharmony_ci * We did find the command, but unfortunately we couldn't 26928c2ecf20Sopenharmony_ci * unhook it from ourselves. Wait some more, and if it 26938c2ecf20Sopenharmony_ci * still doesn't complete, reset the interface. 26948c2ecf20Sopenharmony_ci */ 26958c2ecf20Sopenharmony_ci case res_snooze: 26968c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 26978c2ecf20Sopenharmony_ci printk("snooze\n"); 26988c2ecf20Sopenharmony_ci//#endif 26998c2ecf20Sopenharmony_ci result = FAILED; 27008c2ecf20Sopenharmony_ci break; 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ci /* 27038c2ecf20Sopenharmony_ci * The command could not be found (either because it completed, 27048c2ecf20Sopenharmony_ci * or it got dropped. 27058c2ecf20Sopenharmony_ci */ 27068c2ecf20Sopenharmony_ci default: 27078c2ecf20Sopenharmony_ci case res_not_running: 27088c2ecf20Sopenharmony_ci acornscsi_dumplog(host, SCpnt->device->id); 27098c2ecf20Sopenharmony_ci result = FAILED; 27108c2ecf20Sopenharmony_ci//#if (DEBUG & DEBUG_ABORT) 27118c2ecf20Sopenharmony_ci printk("not running\n"); 27128c2ecf20Sopenharmony_ci//#endif 27138c2ecf20Sopenharmony_ci break; 27148c2ecf20Sopenharmony_ci } 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci return result; 27178c2ecf20Sopenharmony_ci} 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci/* 27208c2ecf20Sopenharmony_ci * Prototype: int acornscsi_reset(struct scsi_cmnd *SCpnt) 27218c2ecf20Sopenharmony_ci * Purpose : reset a command on this host/reset this host 27228c2ecf20Sopenharmony_ci * Params : SCpnt - command causing reset 27238c2ecf20Sopenharmony_ci * Returns : one of SCSI_RESET_ macros 27248c2ecf20Sopenharmony_ci */ 27258c2ecf20Sopenharmony_ciint acornscsi_host_reset(struct scsi_cmnd *SCpnt) 27268c2ecf20Sopenharmony_ci{ 27278c2ecf20Sopenharmony_ci AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; 27288c2ecf20Sopenharmony_ci struct scsi_cmnd *SCptr; 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci host->stats.resets += 1; 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_RESET) 27338c2ecf20Sopenharmony_ci { 27348c2ecf20Sopenharmony_ci int asr, ssr, devidx; 27358c2ecf20Sopenharmony_ci 27368c2ecf20Sopenharmony_ci asr = sbic_arm_read(host, SBIC_ASR); 27378c2ecf20Sopenharmony_ci ssr = sbic_arm_read(host, SBIC_SSR); 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci printk(KERN_WARNING "acornscsi_reset: "); 27408c2ecf20Sopenharmony_ci print_sbic_status(asr, ssr, host->scsi.phase); 27418c2ecf20Sopenharmony_ci for (devidx = 0; devidx < 9; devidx++) 27428c2ecf20Sopenharmony_ci acornscsi_dumplog(host, devidx); 27438c2ecf20Sopenharmony_ci } 27448c2ecf20Sopenharmony_ci#endif 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci acornscsi_dma_stop(host); 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci /* 27498c2ecf20Sopenharmony_ci * do hard reset. This resets all devices on this host, and so we 27508c2ecf20Sopenharmony_ci * must set the reset status on all commands. 27518c2ecf20Sopenharmony_ci */ 27528c2ecf20Sopenharmony_ci acornscsi_resetcard(host); 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL) 27558c2ecf20Sopenharmony_ci ; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci return SUCCESS; 27588c2ecf20Sopenharmony_ci} 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci/*============================================================================================== 27618c2ecf20Sopenharmony_ci * initialisation & miscellaneous support 27628c2ecf20Sopenharmony_ci */ 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci/* 27658c2ecf20Sopenharmony_ci * Function: char *acornscsi_info(struct Scsi_Host *host) 27668c2ecf20Sopenharmony_ci * Purpose : return a string describing this interface 27678c2ecf20Sopenharmony_ci * Params : host - host to give information on 27688c2ecf20Sopenharmony_ci * Returns : a constant string 27698c2ecf20Sopenharmony_ci */ 27708c2ecf20Sopenharmony_ciconst 27718c2ecf20Sopenharmony_cichar *acornscsi_info(struct Scsi_Host *host) 27728c2ecf20Sopenharmony_ci{ 27738c2ecf20Sopenharmony_ci static char string[100], *p; 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci p = string; 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d" 27788c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC 27798c2ecf20Sopenharmony_ci " SYNC" 27808c2ecf20Sopenharmony_ci#endif 27818c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE 27828c2ecf20Sopenharmony_ci " TAG" 27838c2ecf20Sopenharmony_ci#endif 27848c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE) 27858c2ecf20Sopenharmony_ci " NOWRITE (" __stringify(NO_WRITE) ")" 27868c2ecf20Sopenharmony_ci#endif 27878c2ecf20Sopenharmony_ci , host->hostt->name, host->io_port, host->irq, 27888c2ecf20Sopenharmony_ci VER_MAJOR, VER_MINOR, VER_PATCH); 27898c2ecf20Sopenharmony_ci return string; 27908c2ecf20Sopenharmony_ci} 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_cistatic int acornscsi_show_info(struct seq_file *m, struct Scsi_Host *instance) 27938c2ecf20Sopenharmony_ci{ 27948c2ecf20Sopenharmony_ci int devidx; 27958c2ecf20Sopenharmony_ci struct scsi_device *scd; 27968c2ecf20Sopenharmony_ci AS_Host *host; 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci host = (AS_Host *)instance->hostdata; 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci seq_printf(m, "AcornSCSI driver v%d.%d.%d" 28018c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_SYNC 28028c2ecf20Sopenharmony_ci " SYNC" 28038c2ecf20Sopenharmony_ci#endif 28048c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE 28058c2ecf20Sopenharmony_ci " TAG" 28068c2ecf20Sopenharmony_ci#endif 28078c2ecf20Sopenharmony_ci#if (DEBUG & DEBUG_NO_WRITE) 28088c2ecf20Sopenharmony_ci " NOWRITE (" __stringify(NO_WRITE) ")" 28098c2ecf20Sopenharmony_ci#endif 28108c2ecf20Sopenharmony_ci "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH); 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci seq_printf(m, "SBIC: WD33C93A Address: %p IRQ : %d\n", 28138c2ecf20Sopenharmony_ci host->base + SBIC_REGIDX, host->scsi.irq); 28148c2ecf20Sopenharmony_ci#ifdef USE_DMAC 28158c2ecf20Sopenharmony_ci seq_printf(m, "DMAC: uPC71071 Address: %p IRQ : %d\n\n", 28168c2ecf20Sopenharmony_ci host->base + DMAC_OFFSET, host->scsi.irq); 28178c2ecf20Sopenharmony_ci#endif 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci seq_printf(m, "Statistics:\n" 28208c2ecf20Sopenharmony_ci "Queued commands: %-10u Issued commands: %-10u\n" 28218c2ecf20Sopenharmony_ci "Done commands : %-10u Reads : %-10u\n" 28228c2ecf20Sopenharmony_ci "Writes : %-10u Others : %-10u\n" 28238c2ecf20Sopenharmony_ci "Disconnects : %-10u Aborts : %-10u\n" 28248c2ecf20Sopenharmony_ci "Resets : %-10u\n\nLast phases:", 28258c2ecf20Sopenharmony_ci host->stats.queues, host->stats.removes, 28268c2ecf20Sopenharmony_ci host->stats.fins, host->stats.reads, 28278c2ecf20Sopenharmony_ci host->stats.writes, host->stats.miscs, 28288c2ecf20Sopenharmony_ci host->stats.disconnects, host->stats.aborts, 28298c2ecf20Sopenharmony_ci host->stats.resets); 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci for (devidx = 0; devidx < 9; devidx ++) { 28328c2ecf20Sopenharmony_ci unsigned int statptr, prev; 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci seq_printf(m, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); 28358c2ecf20Sopenharmony_ci statptr = host->status_ptr[devidx] - 10; 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci if ((signed int)statptr < 0) 28388c2ecf20Sopenharmony_ci statptr += STATUS_BUFFER_SIZE; 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci prev = host->status[devidx][statptr].when; 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) { 28438c2ecf20Sopenharmony_ci if (host->status[devidx][statptr].when) { 28448c2ecf20Sopenharmony_ci seq_printf(m, "%c%02X:%02X+%2ld", 28458c2ecf20Sopenharmony_ci host->status[devidx][statptr].irq ? '-' : ' ', 28468c2ecf20Sopenharmony_ci host->status[devidx][statptr].ph, 28478c2ecf20Sopenharmony_ci host->status[devidx][statptr].ssr, 28488c2ecf20Sopenharmony_ci (host->status[devidx][statptr].when - prev) < 100 ? 28498c2ecf20Sopenharmony_ci (host->status[devidx][statptr].when - prev) : 99); 28508c2ecf20Sopenharmony_ci prev = host->status[devidx][statptr].when; 28518c2ecf20Sopenharmony_ci } 28528c2ecf20Sopenharmony_ci } 28538c2ecf20Sopenharmony_ci } 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci seq_printf(m, "\nAttached devices:\n"); 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci shost_for_each_device(scd, instance) { 28588c2ecf20Sopenharmony_ci seq_printf(m, "Device/Lun TaggedQ Sync\n"); 28598c2ecf20Sopenharmony_ci seq_printf(m, " %d/%llu ", scd->id, scd->lun); 28608c2ecf20Sopenharmony_ci if (scd->tagged_supported) 28618c2ecf20Sopenharmony_ci seq_printf(m, "%3sabled(%3d) ", 28628c2ecf20Sopenharmony_ci scd->simple_tags ? "en" : "dis", 28638c2ecf20Sopenharmony_ci scd->current_tag); 28648c2ecf20Sopenharmony_ci else 28658c2ecf20Sopenharmony_ci seq_printf(m, "unsupported "); 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci if (host->device[scd->id].sync_xfer & 15) 28688c2ecf20Sopenharmony_ci seq_printf(m, "offset %d, %d ns\n", 28698c2ecf20Sopenharmony_ci host->device[scd->id].sync_xfer & 15, 28708c2ecf20Sopenharmony_ci acornscsi_getperiod(host->device[scd->id].sync_xfer)); 28718c2ecf20Sopenharmony_ci else 28728c2ecf20Sopenharmony_ci seq_printf(m, "async\n"); 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci } 28758c2ecf20Sopenharmony_ci return 0; 28768c2ecf20Sopenharmony_ci} 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_cistatic struct scsi_host_template acornscsi_template = { 28798c2ecf20Sopenharmony_ci .module = THIS_MODULE, 28808c2ecf20Sopenharmony_ci .show_info = acornscsi_show_info, 28818c2ecf20Sopenharmony_ci .name = "AcornSCSI", 28828c2ecf20Sopenharmony_ci .info = acornscsi_info, 28838c2ecf20Sopenharmony_ci .queuecommand = acornscsi_queuecmd, 28848c2ecf20Sopenharmony_ci .eh_abort_handler = acornscsi_abort, 28858c2ecf20Sopenharmony_ci .eh_host_reset_handler = acornscsi_host_reset, 28868c2ecf20Sopenharmony_ci .can_queue = 16, 28878c2ecf20Sopenharmony_ci .this_id = 7, 28888c2ecf20Sopenharmony_ci .sg_tablesize = SG_ALL, 28898c2ecf20Sopenharmony_ci .cmd_per_lun = 2, 28908c2ecf20Sopenharmony_ci .dma_boundary = PAGE_SIZE - 1, 28918c2ecf20Sopenharmony_ci .proc_name = "acornscsi", 28928c2ecf20Sopenharmony_ci}; 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_cistatic int acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id) 28958c2ecf20Sopenharmony_ci{ 28968c2ecf20Sopenharmony_ci struct Scsi_Host *host; 28978c2ecf20Sopenharmony_ci AS_Host *ashost; 28988c2ecf20Sopenharmony_ci int ret; 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_ci ret = ecard_request_resources(ec); 29018c2ecf20Sopenharmony_ci if (ret) 29028c2ecf20Sopenharmony_ci goto out; 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci host = scsi_host_alloc(&acornscsi_template, sizeof(AS_Host)); 29058c2ecf20Sopenharmony_ci if (!host) { 29068c2ecf20Sopenharmony_ci ret = -ENOMEM; 29078c2ecf20Sopenharmony_ci goto out_release; 29088c2ecf20Sopenharmony_ci } 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ci ashost = (AS_Host *)host->hostdata; 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci ashost->base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); 29138c2ecf20Sopenharmony_ci ashost->fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); 29148c2ecf20Sopenharmony_ci if (!ashost->base || !ashost->fast) { 29158c2ecf20Sopenharmony_ci ret = -ENOMEM; 29168c2ecf20Sopenharmony_ci goto out_put; 29178c2ecf20Sopenharmony_ci } 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_ci host->irq = ec->irq; 29208c2ecf20Sopenharmony_ci ashost->host = host; 29218c2ecf20Sopenharmony_ci ashost->scsi.irq = host->irq; 29228c2ecf20Sopenharmony_ci 29238c2ecf20Sopenharmony_ci ec->irqaddr = ashost->fast + INT_REG; 29248c2ecf20Sopenharmony_ci ec->irqmask = 0x0a; 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci ret = request_irq(host->irq, acornscsi_intr, 0, "acornscsi", ashost); 29278c2ecf20Sopenharmony_ci if (ret) { 29288c2ecf20Sopenharmony_ci printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n", 29298c2ecf20Sopenharmony_ci host->host_no, ashost->scsi.irq, ret); 29308c2ecf20Sopenharmony_ci goto out_put; 29318c2ecf20Sopenharmony_ci } 29328c2ecf20Sopenharmony_ci 29338c2ecf20Sopenharmony_ci memset(&ashost->stats, 0, sizeof (ashost->stats)); 29348c2ecf20Sopenharmony_ci queue_initialise(&ashost->queues.issue); 29358c2ecf20Sopenharmony_ci queue_initialise(&ashost->queues.disconnected); 29368c2ecf20Sopenharmony_ci msgqueue_initialise(&ashost->scsi.msgs); 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci acornscsi_resetcard(ashost); 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci ret = scsi_add_host(host, &ec->dev); 29418c2ecf20Sopenharmony_ci if (ret) 29428c2ecf20Sopenharmony_ci goto out_irq; 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci scsi_scan_host(host); 29458c2ecf20Sopenharmony_ci goto out; 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci out_irq: 29488c2ecf20Sopenharmony_ci free_irq(host->irq, ashost); 29498c2ecf20Sopenharmony_ci msgqueue_free(&ashost->scsi.msgs); 29508c2ecf20Sopenharmony_ci queue_free(&ashost->queues.disconnected); 29518c2ecf20Sopenharmony_ci queue_free(&ashost->queues.issue); 29528c2ecf20Sopenharmony_ci out_put: 29538c2ecf20Sopenharmony_ci ecardm_iounmap(ec, ashost->fast); 29548c2ecf20Sopenharmony_ci ecardm_iounmap(ec, ashost->base); 29558c2ecf20Sopenharmony_ci scsi_host_put(host); 29568c2ecf20Sopenharmony_ci out_release: 29578c2ecf20Sopenharmony_ci ecard_release_resources(ec); 29588c2ecf20Sopenharmony_ci out: 29598c2ecf20Sopenharmony_ci return ret; 29608c2ecf20Sopenharmony_ci} 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_cistatic void acornscsi_remove(struct expansion_card *ec) 29638c2ecf20Sopenharmony_ci{ 29648c2ecf20Sopenharmony_ci struct Scsi_Host *host = ecard_get_drvdata(ec); 29658c2ecf20Sopenharmony_ci AS_Host *ashost = (AS_Host *)host->hostdata; 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, NULL); 29688c2ecf20Sopenharmony_ci scsi_remove_host(host); 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_ci /* 29718c2ecf20Sopenharmony_ci * Put card into RESET state 29728c2ecf20Sopenharmony_ci */ 29738c2ecf20Sopenharmony_ci writeb(0x80, ashost->fast + PAGE_REG); 29748c2ecf20Sopenharmony_ci 29758c2ecf20Sopenharmony_ci free_irq(host->irq, ashost); 29768c2ecf20Sopenharmony_ci 29778c2ecf20Sopenharmony_ci msgqueue_free(&ashost->scsi.msgs); 29788c2ecf20Sopenharmony_ci queue_free(&ashost->queues.disconnected); 29798c2ecf20Sopenharmony_ci queue_free(&ashost->queues.issue); 29808c2ecf20Sopenharmony_ci ecardm_iounmap(ec, ashost->fast); 29818c2ecf20Sopenharmony_ci ecardm_iounmap(ec, ashost->base); 29828c2ecf20Sopenharmony_ci scsi_host_put(host); 29838c2ecf20Sopenharmony_ci ecard_release_resources(ec); 29848c2ecf20Sopenharmony_ci} 29858c2ecf20Sopenharmony_ci 29868c2ecf20Sopenharmony_cistatic const struct ecard_id acornscsi_cids[] = { 29878c2ecf20Sopenharmony_ci { MANU_ACORN, PROD_ACORN_SCSI }, 29888c2ecf20Sopenharmony_ci { 0xffff, 0xffff }, 29898c2ecf20Sopenharmony_ci}; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_cistatic struct ecard_driver acornscsi_driver = { 29928c2ecf20Sopenharmony_ci .probe = acornscsi_probe, 29938c2ecf20Sopenharmony_ci .remove = acornscsi_remove, 29948c2ecf20Sopenharmony_ci .id_table = acornscsi_cids, 29958c2ecf20Sopenharmony_ci .drv = { 29968c2ecf20Sopenharmony_ci .name = "acornscsi", 29978c2ecf20Sopenharmony_ci }, 29988c2ecf20Sopenharmony_ci}; 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_cistatic int __init acornscsi_init(void) 30018c2ecf20Sopenharmony_ci{ 30028c2ecf20Sopenharmony_ci return ecard_register_driver(&acornscsi_driver); 30038c2ecf20Sopenharmony_ci} 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_cistatic void __exit acornscsi_exit(void) 30068c2ecf20Sopenharmony_ci{ 30078c2ecf20Sopenharmony_ci ecard_remove_driver(&acornscsi_driver); 30088c2ecf20Sopenharmony_ci} 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_cimodule_init(acornscsi_init); 30118c2ecf20Sopenharmony_cimodule_exit(acornscsi_exit); 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King"); 30148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AcornSCSI driver"); 30158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3016