18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/acorn/scsi/fas216.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1997-2003 Russell King 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and 88c2ecf20Sopenharmony_ci * other sources, including: 98c2ecf20Sopenharmony_ci * the AMD Am53CF94 data sheet 108c2ecf20Sopenharmony_ci * the AMD Am53C94 data sheet 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This is a generic driver. To use it, have a look at cumana_2.c. You 138c2ecf20Sopenharmony_ci * should define your own structure that overlays FAS216_Info, eg: 148c2ecf20Sopenharmony_ci * struct my_host_data { 158c2ecf20Sopenharmony_ci * FAS216_Info info; 168c2ecf20Sopenharmony_ci * ... my host specific data ... 178c2ecf20Sopenharmony_ci * }; 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Changelog: 208c2ecf20Sopenharmony_ci * 30-08-1997 RMK Created 218c2ecf20Sopenharmony_ci * 14-09-1997 RMK Started disconnect support 228c2ecf20Sopenharmony_ci * 08-02-1998 RMK Corrected real DMA support 238c2ecf20Sopenharmony_ci * 15-02-1998 RMK Started sync xfer support 248c2ecf20Sopenharmony_ci * 06-04-1998 RMK Tightened conditions for printing incomplete 258c2ecf20Sopenharmony_ci * transfers 268c2ecf20Sopenharmony_ci * 02-05-1998 RMK Added extra checks in fas216_reset 278c2ecf20Sopenharmony_ci * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns 288c2ecf20Sopenharmony_ci * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h 298c2ecf20Sopenharmony_ci * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT 308c2ecf20Sopenharmony_ci * 02-04-2000 RMK Converted to use the new error handling, and 318c2ecf20Sopenharmony_ci * automatically request sense data upon check 328c2ecf20Sopenharmony_ci * condition status from targets. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci#include <linux/module.h> 358c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 368c2ecf20Sopenharmony_ci#include <linux/kernel.h> 378c2ecf20Sopenharmony_ci#include <linux/string.h> 388c2ecf20Sopenharmony_ci#include <linux/ioport.h> 398c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 408c2ecf20Sopenharmony_ci#include <linux/delay.h> 418c2ecf20Sopenharmony_ci#include <linux/bitops.h> 428c2ecf20Sopenharmony_ci#include <linux/init.h> 438c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <asm/dma.h> 468c2ecf20Sopenharmony_ci#include <asm/io.h> 478c2ecf20Sopenharmony_ci#include <asm/irq.h> 488c2ecf20Sopenharmony_ci#include <asm/ecard.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#include "../scsi.h" 518c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h> 528c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 538c2ecf20Sopenharmony_ci#include "fas216.h" 548c2ecf20Sopenharmony_ci#include "scsi.h" 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* NOTE: SCSI2 Synchronous transfers *require* DMA according to 578c2ecf20Sopenharmony_ci * the data sheet. This restriction is crazy, especially when 588c2ecf20Sopenharmony_ci * you only want to send 16 bytes! What were the guys who 598c2ecf20Sopenharmony_ci * designed this chip on at that time? Did they read the SCSI2 608c2ecf20Sopenharmony_ci * spec at all? The following sections are taken from the SCSI2 618c2ecf20Sopenharmony_ci * standard (s2r10) concerning this: 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * > IMPLEMENTORS NOTES: 648c2ecf20Sopenharmony_ci * > (1) Re-negotiation at every selection is not recommended, since a 658c2ecf20Sopenharmony_ci * > significant performance impact is likely. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * > The implied synchronous agreement shall remain in effect until a BUS DEVICE 688c2ecf20Sopenharmony_ci * > RESET message is received, until a hard reset condition occurs, or until one 698c2ecf20Sopenharmony_ci * > of the two SCSI devices elects to modify the agreement. The default data 708c2ecf20Sopenharmony_ci * > transfer mode is asynchronous data transfer mode. The default data transfer 718c2ecf20Sopenharmony_ci * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard 728c2ecf20Sopenharmony_ci * > reset condition. 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * In total, this means that once you have elected to use synchronous 758c2ecf20Sopenharmony_ci * transfers, you must always use DMA. 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * I was thinking that this was a good chip until I found this restriction ;( 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci#define SCSI2_SYNC 808c2ecf20Sopenharmony_ci#undef SCSI2_TAG 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci#undef DEBUG_CONNECT 838c2ecf20Sopenharmony_ci#undef DEBUG_MESSAGES 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#undef CHECK_STRUCTURE 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define LOG_CONNECT (1 << 0) 888c2ecf20Sopenharmony_ci#define LOG_BUSSERVICE (1 << 1) 898c2ecf20Sopenharmony_ci#define LOG_FUNCTIONDONE (1 << 2) 908c2ecf20Sopenharmony_ci#define LOG_MESSAGES (1 << 3) 918c2ecf20Sopenharmony_ci#define LOG_BUFFER (1 << 4) 928c2ecf20Sopenharmony_ci#define LOG_ERROR (1 << 8) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int level_mask = LOG_ERROR; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cimodule_param(level_mask, int, 0644); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#ifndef MODULE 998c2ecf20Sopenharmony_cistatic int __init fas216_log_setup(char *str) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci char *s; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci level_mask = 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci while ((s = strsep(&str, ",")) != NULL) { 1068c2ecf20Sopenharmony_ci switch (s[0]) { 1078c2ecf20Sopenharmony_ci case 'a': 1088c2ecf20Sopenharmony_ci if (strcmp(s, "all") == 0) 1098c2ecf20Sopenharmony_ci level_mask |= -1; 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci case 'b': 1128c2ecf20Sopenharmony_ci if (strncmp(s, "bus", 3) == 0) 1138c2ecf20Sopenharmony_ci level_mask |= LOG_BUSSERVICE; 1148c2ecf20Sopenharmony_ci if (strncmp(s, "buf", 3) == 0) 1158c2ecf20Sopenharmony_ci level_mask |= LOG_BUFFER; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case 'c': 1188c2ecf20Sopenharmony_ci level_mask |= LOG_CONNECT; 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci case 'e': 1218c2ecf20Sopenharmony_ci level_mask |= LOG_ERROR; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case 'm': 1248c2ecf20Sopenharmony_ci level_mask |= LOG_MESSAGES; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci case 'n': 1278c2ecf20Sopenharmony_ci if (strcmp(s, "none") == 0) 1288c2ecf20Sopenharmony_ci level_mask = 0; 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci case 's': 1318c2ecf20Sopenharmony_ci level_mask |= LOG_FUNCTIONDONE; 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci return 1; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci__setup("fas216_logging=", fas216_log_setup); 1398c2ecf20Sopenharmony_ci#endif 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci unsigned int off = reg << info->scsi.io_shift; 1448c2ecf20Sopenharmony_ci return readb(info->scsi.io_base + off); 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci unsigned int off = reg << info->scsi.io_shift; 1508c2ecf20Sopenharmony_ci writeb(val, info->scsi.io_base + off); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void fas216_dumpstate(FAS216_Info *info) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci unsigned char is, stat, inst; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci is = fas216_readb(info, REG_IS); 1588c2ecf20Sopenharmony_ci stat = fas216_readb(info, REG_STAT); 1598c2ecf20Sopenharmony_ci inst = fas216_readb(info, REG_INST); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X" 1628c2ecf20Sopenharmony_ci " INST=%02X IS=%02X CFIS=%02X", 1638c2ecf20Sopenharmony_ci fas216_readb(info, REG_CTCL), 1648c2ecf20Sopenharmony_ci fas216_readb(info, REG_CTCM), 1658c2ecf20Sopenharmony_ci fas216_readb(info, REG_CMD), stat, inst, is, 1668c2ecf20Sopenharmony_ci fas216_readb(info, REG_CFIS)); 1678c2ecf20Sopenharmony_ci printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n", 1688c2ecf20Sopenharmony_ci fas216_readb(info, REG_CNTL1), 1698c2ecf20Sopenharmony_ci fas216_readb(info, REG_CNTL2), 1708c2ecf20Sopenharmony_ci fas216_readb(info, REG_CNTL3), 1718c2ecf20Sopenharmony_ci fas216_readb(info, REG_CTCH)); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic void print_SCp(struct scsi_pointer *SCp, const char *prefix, const char *suffix) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s", 1778c2ecf20Sopenharmony_ci prefix, SCp->ptr, SCp->this_residual, SCp->buffer, 1788c2ecf20Sopenharmony_ci SCp->buffers_residual, suffix); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci#ifdef CHECK_STRUCTURE 1828c2ecf20Sopenharmony_cistatic void fas216_dumpinfo(FAS216_Info *info) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci static int used = 0; 1858c2ecf20Sopenharmony_ci int i; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (used++) 1888c2ecf20Sopenharmony_ci return; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci printk("FAS216_Info=\n"); 1918c2ecf20Sopenharmony_ci printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n", 1928c2ecf20Sopenharmony_ci info->magic_start, info->host, info->SCpnt, 1938c2ecf20Sopenharmony_ci info->origSCpnt); 1948c2ecf20Sopenharmony_ci printk(" scsi={ io_shift=%X irq=%X cfg={ %X %X %X %X }\n", 1958c2ecf20Sopenharmony_ci info->scsi.io_shift, info->scsi.irq, 1968c2ecf20Sopenharmony_ci info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2], 1978c2ecf20Sopenharmony_ci info->scsi.cfg[3]); 1988c2ecf20Sopenharmony_ci printk(" type=%p phase=%X\n", 1998c2ecf20Sopenharmony_ci info->scsi.type, info->scsi.phase); 2008c2ecf20Sopenharmony_ci print_SCp(&info->scsi.SCp, " SCp={ ", " }\n"); 2018c2ecf20Sopenharmony_ci printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", 2028c2ecf20Sopenharmony_ci info->scsi.async_stp, 2038c2ecf20Sopenharmony_ci info->scsi.disconnectable, info->scsi.aborting); 2048c2ecf20Sopenharmony_ci printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" 2058c2ecf20Sopenharmony_ci " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n", 2068c2ecf20Sopenharmony_ci info->stats.queues, info->stats.removes, info->stats.fins, 2078c2ecf20Sopenharmony_ci info->stats.reads, info->stats.writes, info->stats.miscs, 2088c2ecf20Sopenharmony_ci info->stats.disconnects, info->stats.aborts, info->stats.bus_resets, 2098c2ecf20Sopenharmony_ci info->stats.host_resets); 2108c2ecf20Sopenharmony_ci printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n", 2118c2ecf20Sopenharmony_ci info->ifcfg.clockrate, info->ifcfg.select_timeout, 2128c2ecf20Sopenharmony_ci info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); 2138c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 2148c2ecf20Sopenharmony_ci printk(" busyluns[%d]=%08lx dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", 2158c2ecf20Sopenharmony_ci i, info->busyluns[i], i, 2168c2ecf20Sopenharmony_ci info->device[i].disconnect_ok, info->device[i].stp, 2178c2ecf20Sopenharmony_ci info->device[i].sof, info->device[i].sync_state); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", 2208c2ecf20Sopenharmony_ci info->dma.transfer_type, info->dma.setup, 2218c2ecf20Sopenharmony_ci info->dma.pseudo, info->dma.stop); 2228c2ecf20Sopenharmony_ci printk(" internal_done=%X magic_end=%lX }\n", 2238c2ecf20Sopenharmony_ci info->internal_done, info->magic_end); 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void __fas216_checkmagic(FAS216_Info *info, const char *func) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int corruption = 0; 2298c2ecf20Sopenharmony_ci if (info->magic_start != MAGIC) { 2308c2ecf20Sopenharmony_ci printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n"); 2318c2ecf20Sopenharmony_ci corruption++; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci if (info->magic_end != MAGIC) { 2348c2ecf20Sopenharmony_ci printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n"); 2358c2ecf20Sopenharmony_ci corruption++; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci if (corruption) { 2388c2ecf20Sopenharmony_ci fas216_dumpinfo(info); 2398c2ecf20Sopenharmony_ci panic("scsi memory space corrupted in %s", func); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci#define fas216_checkmagic(info) __fas216_checkmagic((info), __func__) 2438c2ecf20Sopenharmony_ci#else 2448c2ecf20Sopenharmony_ci#define fas216_checkmagic(info) 2458c2ecf20Sopenharmony_ci#endif 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const char *fas216_bus_phase(int stat) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci static const char *phases[] = { 2508c2ecf20Sopenharmony_ci "DATA OUT", "DATA IN", 2518c2ecf20Sopenharmony_ci "COMMAND", "STATUS", 2528c2ecf20Sopenharmony_ci "MISC OUT", "MISC IN", 2538c2ecf20Sopenharmony_ci "MESG OUT", "MESG IN" 2548c2ecf20Sopenharmony_ci }; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return phases[stat & STAT_BUSMASK]; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic const char *fas216_drv_phase(FAS216_Info *info) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci static const char *phases[] = { 2628c2ecf20Sopenharmony_ci [PHASE_IDLE] = "idle", 2638c2ecf20Sopenharmony_ci [PHASE_SELECTION] = "selection", 2648c2ecf20Sopenharmony_ci [PHASE_COMMAND] = "command", 2658c2ecf20Sopenharmony_ci [PHASE_DATAOUT] = "data out", 2668c2ecf20Sopenharmony_ci [PHASE_DATAIN] = "data in", 2678c2ecf20Sopenharmony_ci [PHASE_MSGIN] = "message in", 2688c2ecf20Sopenharmony_ci [PHASE_MSGIN_DISCONNECT]= "disconnect", 2698c2ecf20Sopenharmony_ci [PHASE_MSGOUT_EXPECT] = "expect message out", 2708c2ecf20Sopenharmony_ci [PHASE_MSGOUT] = "message out", 2718c2ecf20Sopenharmony_ci [PHASE_STATUS] = "status", 2728c2ecf20Sopenharmony_ci [PHASE_DONE] = "done", 2738c2ecf20Sopenharmony_ci }; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (info->scsi.phase < ARRAY_SIZE(phases) && 2768c2ecf20Sopenharmony_ci phases[info->scsi.phase]) 2778c2ecf20Sopenharmony_ci return phases[info->scsi.phase]; 2788c2ecf20Sopenharmony_ci return "???"; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic char fas216_target(FAS216_Info *info) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci if (info->SCpnt) 2848c2ecf20Sopenharmony_ci return '0' + info->SCpnt->device->id; 2858c2ecf20Sopenharmony_ci else 2868c2ecf20Sopenharmony_ci return 'H'; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void 2908c2ecf20Sopenharmony_cifas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci static char buf[1024]; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci vsnprintf(buf, sizeof(buf), fmt, ap); 2958c2ecf20Sopenharmony_ci printk("scsi%d.%c: %s", info->host->host_no, target, buf); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void fas216_log_command(FAS216_Info *info, int level, 2998c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt, char *fmt, ...) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci va_list args; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (level != 0 && !(level & level_mask)) 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci va_start(args, fmt); 3078c2ecf20Sopenharmony_ci fas216_do_log(info, '0' + SCpnt->device->id, fmt, args); 3088c2ecf20Sopenharmony_ci va_end(args); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci scsi_print_command(SCpnt); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void 3148c2ecf20Sopenharmony_cifas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci va_list args; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (level != 0 && !(level & level_mask)) 3198c2ecf20Sopenharmony_ci return; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (target < 0) 3228c2ecf20Sopenharmony_ci target = 'H'; 3238c2ecf20Sopenharmony_ci else 3248c2ecf20Sopenharmony_ci target += '0'; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci va_start(args, fmt); 3278c2ecf20Sopenharmony_ci fas216_do_log(info, target, fmt, args); 3288c2ecf20Sopenharmony_ci va_end(args); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci printk("\n"); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic void fas216_log(FAS216_Info *info, int level, char *fmt, ...) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci va_list args; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (level != 0 && !(level & level_mask)) 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci va_start(args, fmt); 3418c2ecf20Sopenharmony_ci fas216_do_log(info, fas216_target(info), fmt, args); 3428c2ecf20Sopenharmony_ci va_end(args); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci printk("\n"); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci#define PH_SIZE 32 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE]; 3508c2ecf20Sopenharmony_cistatic int ph_ptr; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void add_debug_list(int stat, int ssr, int isr, int ph) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci ph_list[ph_ptr].stat = stat; 3558c2ecf20Sopenharmony_ci ph_list[ph_ptr].ssr = ssr; 3568c2ecf20Sopenharmony_ci ph_list[ph_ptr].isr = isr; 3578c2ecf20Sopenharmony_ci ph_list[ph_ptr].ph = ph; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ph_ptr = (ph_ptr + 1) & (PH_SIZE-1); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic struct { int command; void *from; } cmd_list[8]; 3638c2ecf20Sopenharmony_cistatic int cmd_ptr; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void fas216_cmd(FAS216_Info *info, unsigned int command) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci cmd_list[cmd_ptr].command = command; 3688c2ecf20Sopenharmony_ci cmd_list[cmd_ptr].from = __builtin_return_address(0); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci cmd_ptr = (cmd_ptr + 1) & 7; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, command); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void print_debug_list(void) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int i; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci i = ph_ptr; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci printk(KERN_ERR "SCSI IRQ trail\n"); 3828c2ecf20Sopenharmony_ci do { 3838c2ecf20Sopenharmony_ci printk(" %02x:%02x:%02x:%1x", 3848c2ecf20Sopenharmony_ci ph_list[i].stat, ph_list[i].ssr, 3858c2ecf20Sopenharmony_ci ph_list[i].isr, ph_list[i].ph); 3868c2ecf20Sopenharmony_ci i = (i + 1) & (PH_SIZE - 1); 3878c2ecf20Sopenharmony_ci if (((i ^ ph_ptr) & 7) == 0) 3888c2ecf20Sopenharmony_ci printk("\n"); 3898c2ecf20Sopenharmony_ci } while (i != ph_ptr); 3908c2ecf20Sopenharmony_ci if ((i ^ ph_ptr) & 7) 3918c2ecf20Sopenharmony_ci printk("\n"); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci i = cmd_ptr; 3948c2ecf20Sopenharmony_ci printk(KERN_ERR "FAS216 commands: "); 3958c2ecf20Sopenharmony_ci do { 3968c2ecf20Sopenharmony_ci printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from); 3978c2ecf20Sopenharmony_ci i = (i + 1) & 7; 3988c2ecf20Sopenharmony_ci } while (i != cmd_ptr); 3998c2ecf20Sopenharmony_ci printk("\n"); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void fas216_done(FAS216_Info *info, unsigned int result); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/** 4058c2ecf20Sopenharmony_ci * fas216_get_last_msg - retrive last message from the list 4068c2ecf20Sopenharmony_ci * @info: interface to search 4078c2ecf20Sopenharmony_ci * @pos: current fifo position 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Retrieve a last message from the list, using position in fifo. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_cistatic inline unsigned short 4128c2ecf20Sopenharmony_cifas216_get_last_msg(FAS216_Info *info, int pos) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci unsigned short packed_msg = NOP; 4158c2ecf20Sopenharmony_ci struct message *msg; 4168c2ecf20Sopenharmony_ci int msgnr = 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { 4198c2ecf20Sopenharmony_ci if (pos >= msg->fifo) 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (msg) { 4248c2ecf20Sopenharmony_ci if (msg->msg[0] == EXTENDED_MESSAGE) 4258c2ecf20Sopenharmony_ci packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; 4268c2ecf20Sopenharmony_ci else 4278c2ecf20Sopenharmony_ci packed_msg = msg->msg[0]; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci fas216_log(info, LOG_MESSAGES, 4318c2ecf20Sopenharmony_ci "Message: %04x found at position %02x\n", packed_msg, pos); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return packed_msg; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/** 4378c2ecf20Sopenharmony_ci * fas216_syncperiod - calculate STP register value 4388c2ecf20Sopenharmony_ci * @info: state structure for interface connected to device 4398c2ecf20Sopenharmony_ci * @ns: period in ns (between subsequent bytes) 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * Calculate value to be loaded into the STP register for a given period 4428c2ecf20Sopenharmony_ci * in ns. Returns a value suitable for REG_STP. 4438c2ecf20Sopenharmony_ci */ 4448c2ecf20Sopenharmony_cistatic int fas216_syncperiod(FAS216_Info *info, int ns) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci int value = (info->ifcfg.clockrate * ns) / 1000; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci fas216_checkmagic(info); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (value < 4) 4518c2ecf20Sopenharmony_ci value = 4; 4528c2ecf20Sopenharmony_ci else if (value > 35) 4538c2ecf20Sopenharmony_ci value = 35; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return value & 31; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/** 4598c2ecf20Sopenharmony_ci * fas216_set_sync - setup FAS216 chip for specified transfer period. 4608c2ecf20Sopenharmony_ci * @info: state structure for interface connected to device 4618c2ecf20Sopenharmony_ci * @target: target 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * Correctly setup FAS216 chip for specified transfer period. 4648c2ecf20Sopenharmony_ci * Notes : we need to switch the chip out of FASTSCSI mode if we have 4658c2ecf20Sopenharmony_ci * a transfer period >= 200ns - otherwise the chip will violate 4668c2ecf20Sopenharmony_ci * the SCSI timings. 4678c2ecf20Sopenharmony_ci */ 4688c2ecf20Sopenharmony_cistatic void fas216_set_sync(FAS216_Info *info, int target) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci unsigned int cntl3; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci fas216_writeb(info, REG_SOF, info->device[target].sof); 4738c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STP, info->device[target].stp); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci cntl3 = info->scsi.cfg[2]; 4768c2ecf20Sopenharmony_ci if (info->device[target].period >= (200 / 4)) 4778c2ecf20Sopenharmony_ci cntl3 = cntl3 & ~CNTL3_FASTSCSI; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, cntl3); 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci/* Synchronous transfer support 4838c2ecf20Sopenharmony_ci * 4848c2ecf20Sopenharmony_ci * Note: The SCSI II r10 spec says (5.6.12): 4858c2ecf20Sopenharmony_ci * 4868c2ecf20Sopenharmony_ci * (2) Due to historical problems with early host adapters that could 4878c2ecf20Sopenharmony_ci * not accept an SDTR message, some targets may not initiate synchronous 4888c2ecf20Sopenharmony_ci * negotiation after a power cycle as required by this standard. Host 4898c2ecf20Sopenharmony_ci * adapters that support synchronous mode may avoid the ensuing failure 4908c2ecf20Sopenharmony_ci * modes when the target is independently power cycled by initiating a 4918c2ecf20Sopenharmony_ci * synchronous negotiation on each REQUEST SENSE and INQUIRY command. 4928c2ecf20Sopenharmony_ci * This approach increases the SCSI bus overhead and is not recommended 4938c2ecf20Sopenharmony_ci * for new implementations. The correct method is to respond to an 4948c2ecf20Sopenharmony_ci * SDTR message with a MESSAGE REJECT message if the either the 4958c2ecf20Sopenharmony_ci * initiator or target devices does not support synchronous transfers 4968c2ecf20Sopenharmony_ci * or does not want to negotiate for synchronous transfers at the time. 4978c2ecf20Sopenharmony_ci * Using the correct method assures compatibility with wide data 4988c2ecf20Sopenharmony_ci * transfers and future enhancements. 4998c2ecf20Sopenharmony_ci * 5008c2ecf20Sopenharmony_ci * We will always initiate a synchronous transfer negotiation request on 5018c2ecf20Sopenharmony_ci * every INQUIRY or REQUEST SENSE message, unless the target itself has 5028c2ecf20Sopenharmony_ci * at some point performed a synchronous transfer negotiation request, or 5038c2ecf20Sopenharmony_ci * we have synchronous transfers disabled for this device. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/** 5078c2ecf20Sopenharmony_ci * fas216_handlesync - Handle a synchronous transfer message 5088c2ecf20Sopenharmony_ci * @info: state structure for interface 5098c2ecf20Sopenharmony_ci * @msg: message from target 5108c2ecf20Sopenharmony_ci * 5118c2ecf20Sopenharmony_ci * Handle a synchronous transfer message from the target 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistatic void fas216_handlesync(FAS216_Info *info, char *msg) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct fas216_device *dev = &info->device[info->SCpnt->device->id]; 5168c2ecf20Sopenharmony_ci enum { sync, async, none, reject } res = none; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci#ifdef SCSI2_SYNC 5198c2ecf20Sopenharmony_ci switch (msg[0]) { 5208c2ecf20Sopenharmony_ci case MESSAGE_REJECT: 5218c2ecf20Sopenharmony_ci /* Synchronous transfer request failed. 5228c2ecf20Sopenharmony_ci * Note: SCSI II r10: 5238c2ecf20Sopenharmony_ci * 5248c2ecf20Sopenharmony_ci * SCSI devices that are capable of synchronous 5258c2ecf20Sopenharmony_ci * data transfers shall not respond to an SDTR 5268c2ecf20Sopenharmony_ci * message with a MESSAGE REJECT message. 5278c2ecf20Sopenharmony_ci * 5288c2ecf20Sopenharmony_ci * Hence, if we get this condition, we disable 5298c2ecf20Sopenharmony_ci * negotiation for this device. 5308c2ecf20Sopenharmony_ci */ 5318c2ecf20Sopenharmony_ci if (dev->sync_state == neg_inprogress) { 5328c2ecf20Sopenharmony_ci dev->sync_state = neg_invalid; 5338c2ecf20Sopenharmony_ci res = async; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci break; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci case EXTENDED_MESSAGE: 5388c2ecf20Sopenharmony_ci switch (dev->sync_state) { 5398c2ecf20Sopenharmony_ci /* We don't accept synchronous transfer requests. 5408c2ecf20Sopenharmony_ci * Respond with a MESSAGE_REJECT to prevent a 5418c2ecf20Sopenharmony_ci * synchronous transfer agreement from being reached. 5428c2ecf20Sopenharmony_ci */ 5438c2ecf20Sopenharmony_ci case neg_invalid: 5448c2ecf20Sopenharmony_ci res = reject; 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* We were not negotiating a synchronous transfer, 5488c2ecf20Sopenharmony_ci * but the device sent us a negotiation request. 5498c2ecf20Sopenharmony_ci * Honour the request by sending back a SDTR 5508c2ecf20Sopenharmony_ci * message containing our capability, limited by 5518c2ecf20Sopenharmony_ci * the targets capability. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci default: 5548c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 5558c2ecf20Sopenharmony_ci if (msg[4] > info->ifcfg.sync_max_depth) 5568c2ecf20Sopenharmony_ci msg[4] = info->ifcfg.sync_max_depth; 5578c2ecf20Sopenharmony_ci if (msg[3] < 1000 / info->ifcfg.clockrate) 5588c2ecf20Sopenharmony_ci msg[3] = 1000 / info->ifcfg.clockrate; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 5618c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 5, 5628c2ecf20Sopenharmony_ci EXTENDED_MESSAGE, 3, EXTENDED_SDTR, 5638c2ecf20Sopenharmony_ci msg[3], msg[4]); 5648c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* This is wrong. The agreement is not in effect 5678c2ecf20Sopenharmony_ci * until this message is accepted by the device 5688c2ecf20Sopenharmony_ci */ 5698c2ecf20Sopenharmony_ci dev->sync_state = neg_targcomplete; 5708c2ecf20Sopenharmony_ci res = sync; 5718c2ecf20Sopenharmony_ci break; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci /* We initiated the synchronous transfer negotiation, 5748c2ecf20Sopenharmony_ci * and have successfully received a response from the 5758c2ecf20Sopenharmony_ci * target. The synchronous transfer agreement has been 5768c2ecf20Sopenharmony_ci * reached. Note: if the values returned are out of our 5778c2ecf20Sopenharmony_ci * bounds, we must reject the message. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci case neg_inprogress: 5808c2ecf20Sopenharmony_ci res = reject; 5818c2ecf20Sopenharmony_ci if (msg[4] <= info->ifcfg.sync_max_depth && 5828c2ecf20Sopenharmony_ci msg[3] >= 1000 / info->ifcfg.clockrate) { 5838c2ecf20Sopenharmony_ci dev->sync_state = neg_complete; 5848c2ecf20Sopenharmony_ci res = sync; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci break; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci#else 5908c2ecf20Sopenharmony_ci res = reject; 5918c2ecf20Sopenharmony_ci#endif 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci switch (res) { 5948c2ecf20Sopenharmony_ci case sync: 5958c2ecf20Sopenharmony_ci dev->period = msg[3]; 5968c2ecf20Sopenharmony_ci dev->sof = msg[4]; 5978c2ecf20Sopenharmony_ci dev->stp = fas216_syncperiod(info, msg[3] * 4); 5988c2ecf20Sopenharmony_ci fas216_set_sync(info, info->SCpnt->device->id); 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci case reject: 6028c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 6038c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 6048c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); 6058c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 6068c2ecf20Sopenharmony_ci fallthrough; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci case async: 6098c2ecf20Sopenharmony_ci dev->period = info->ifcfg.asyncperiod / 4; 6108c2ecf20Sopenharmony_ci dev->sof = 0; 6118c2ecf20Sopenharmony_ci dev->stp = info->scsi.async_stp; 6128c2ecf20Sopenharmony_ci fas216_set_sync(info, info->SCpnt->device->id); 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci case none: 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/** 6218c2ecf20Sopenharmony_ci * fas216_updateptrs - update data pointers after transfer suspended/paused 6228c2ecf20Sopenharmony_ci * @info: interface's local pointer to update 6238c2ecf20Sopenharmony_ci * @bytes_transferred: number of bytes transferred 6248c2ecf20Sopenharmony_ci * 6258c2ecf20Sopenharmony_ci * Update data pointers after transfer suspended/paused 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_cistatic void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct scsi_pointer *SCp = &info->scsi.SCp; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci fas216_checkmagic(info); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci BUG_ON(bytes_transferred < 0); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci SCp->phase -= bytes_transferred; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci while (bytes_transferred != 0) { 6388c2ecf20Sopenharmony_ci if (SCp->this_residual > bytes_transferred) 6398c2ecf20Sopenharmony_ci break; 6408c2ecf20Sopenharmony_ci /* 6418c2ecf20Sopenharmony_ci * We have used up this buffer. Move on to the 6428c2ecf20Sopenharmony_ci * next buffer. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci bytes_transferred -= SCp->this_residual; 6458c2ecf20Sopenharmony_ci if (!next_SCp(SCp) && bytes_transferred) { 6468c2ecf20Sopenharmony_ci printk(KERN_WARNING "scsi%d.%c: out of buffers\n", 6478c2ecf20Sopenharmony_ci info->host->host_no, '0' + info->SCpnt->device->id); 6488c2ecf20Sopenharmony_ci return; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci SCp->this_residual -= bytes_transferred; 6538c2ecf20Sopenharmony_ci if (SCp->this_residual) 6548c2ecf20Sopenharmony_ci SCp->ptr += bytes_transferred; 6558c2ecf20Sopenharmony_ci else 6568c2ecf20Sopenharmony_ci SCp->ptr = NULL; 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci/** 6608c2ecf20Sopenharmony_ci * fas216_pio - transfer data off of/on to card using programmed IO 6618c2ecf20Sopenharmony_ci * @info: interface to transfer data to/from 6628c2ecf20Sopenharmony_ci * @direction: direction to transfer data (DMA_OUT/DMA_IN) 6638c2ecf20Sopenharmony_ci * 6648c2ecf20Sopenharmony_ci * Transfer data off of/on to card using programmed IO. 6658c2ecf20Sopenharmony_ci * Notes: this is incredibly slow. 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_cistatic void fas216_pio(FAS216_Info *info, fasdmadir_t direction) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct scsi_pointer *SCp = &info->scsi.SCp; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci fas216_checkmagic(info); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (direction == DMA_OUT) 6748c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp)); 6758c2ecf20Sopenharmony_ci else 6768c2ecf20Sopenharmony_ci put_next_SCp_byte(SCp, fas216_readb(info, REG_FF)); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (SCp->this_residual == 0) 6798c2ecf20Sopenharmony_ci next_SCp(SCp); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic void fas216_set_stc(FAS216_Info *info, unsigned int length) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STCL, length); 6858c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STCM, length >> 8); 6868c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STCH, length >> 16); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic unsigned int fas216_get_ctc(FAS216_Info *info) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci return fas216_readb(info, REG_CTCL) + 6928c2ecf20Sopenharmony_ci (fas216_readb(info, REG_CTCM) << 8) + 6938c2ecf20Sopenharmony_ci (fas216_readb(info, REG_CTCH) << 16); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/** 6978c2ecf20Sopenharmony_ci * fas216_cleanuptransfer - clean up after a transfer has completed. 6988c2ecf20Sopenharmony_ci * @info: interface to clean up 6998c2ecf20Sopenharmony_ci * 7008c2ecf20Sopenharmony_ci * Update the data pointers according to the number of bytes transferred 7018c2ecf20Sopenharmony_ci * on the SCSI bus. 7028c2ecf20Sopenharmony_ci */ 7038c2ecf20Sopenharmony_cistatic void fas216_cleanuptransfer(FAS216_Info *info) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci unsigned long total, residual, fifo; 7068c2ecf20Sopenharmony_ci fasdmatype_t dmatype = info->dma.transfer_type; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci info->dma.transfer_type = fasdma_none; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* 7118c2ecf20Sopenharmony_ci * PIO transfers do not need to be cleaned up. 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_ci if (dmatype == fasdma_pio || dmatype == fasdma_none) 7148c2ecf20Sopenharmony_ci return; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (dmatype == fasdma_real_all) 7178c2ecf20Sopenharmony_ci total = info->scsi.SCp.phase; 7188c2ecf20Sopenharmony_ci else 7198c2ecf20Sopenharmony_ci total = info->scsi.SCp.this_residual; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci residual = fas216_get_ctc(info); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUFFER, "cleaning up from previous " 7268c2ecf20Sopenharmony_ci "transfer: length 0x%06x, residual 0x%x, fifo %d", 7278c2ecf20Sopenharmony_ci total, residual, fifo); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* 7308c2ecf20Sopenharmony_ci * If we were performing Data-Out, the transfer counter 7318c2ecf20Sopenharmony_ci * counts down each time a byte is transferred by the 7328c2ecf20Sopenharmony_ci * host to the FIFO. This means we must include the 7338c2ecf20Sopenharmony_ci * bytes left in the FIFO from the transfer counter. 7348c2ecf20Sopenharmony_ci */ 7358c2ecf20Sopenharmony_ci if (info->scsi.phase == PHASE_DATAOUT) 7368c2ecf20Sopenharmony_ci residual += fifo; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci fas216_updateptrs(info, total - residual); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci/** 7428c2ecf20Sopenharmony_ci * fas216_transfer - Perform a DMA/PIO transfer off of/on to card 7438c2ecf20Sopenharmony_ci * @info: interface from which device disconnected from 7448c2ecf20Sopenharmony_ci * 7458c2ecf20Sopenharmony_ci * Start a DMA/PIO transfer off of/on to card 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_cistatic void fas216_transfer(FAS216_Info *info) 7488c2ecf20Sopenharmony_ci{ 7498c2ecf20Sopenharmony_ci fasdmadir_t direction; 7508c2ecf20Sopenharmony_ci fasdmatype_t dmatype; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUFFER, 7538c2ecf20Sopenharmony_ci "starttransfer: buffer %p length 0x%06x reqlen 0x%06x", 7548c2ecf20Sopenharmony_ci info->scsi.SCp.ptr, info->scsi.SCp.this_residual, 7558c2ecf20Sopenharmony_ci info->scsi.SCp.phase); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (!info->scsi.SCp.ptr) { 7588c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "null buffer passed to " 7598c2ecf20Sopenharmony_ci "fas216_starttransfer"); 7608c2ecf20Sopenharmony_ci print_SCp(&info->scsi.SCp, "SCp: ", "\n"); 7618c2ecf20Sopenharmony_ci print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n"); 7628c2ecf20Sopenharmony_ci return; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* 7668c2ecf20Sopenharmony_ci * If we have a synchronous transfer agreement in effect, we must 7678c2ecf20Sopenharmony_ci * use DMA mode. If we are using asynchronous transfers, we may 7688c2ecf20Sopenharmony_ci * use DMA mode or PIO mode. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci if (info->device[info->SCpnt->device->id].sof) 7718c2ecf20Sopenharmony_ci dmatype = fasdma_real_all; 7728c2ecf20Sopenharmony_ci else 7738c2ecf20Sopenharmony_ci dmatype = fasdma_pio; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (info->scsi.phase == PHASE_DATAOUT) 7768c2ecf20Sopenharmony_ci direction = DMA_OUT; 7778c2ecf20Sopenharmony_ci else 7788c2ecf20Sopenharmony_ci direction = DMA_IN; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (info->dma.setup) 7818c2ecf20Sopenharmony_ci dmatype = info->dma.setup(info->host, &info->scsi.SCp, 7828c2ecf20Sopenharmony_ci direction, dmatype); 7838c2ecf20Sopenharmony_ci info->dma.transfer_type = dmatype; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (dmatype == fasdma_real_all) 7868c2ecf20Sopenharmony_ci fas216_set_stc(info, info->scsi.SCp.phase); 7878c2ecf20Sopenharmony_ci else 7888c2ecf20Sopenharmony_ci fas216_set_stc(info, info->scsi.SCp.this_residual); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci switch (dmatype) { 7918c2ecf20Sopenharmony_ci case fasdma_pio: 7928c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUFFER, "PIO transfer"); 7938c2ecf20Sopenharmony_ci fas216_writeb(info, REG_SOF, 0); 7948c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STP, info->scsi.async_stp); 7958c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO); 7968c2ecf20Sopenharmony_ci fas216_pio(info, direction); 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci case fasdma_pseudo: 8008c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUFFER, "pseudo transfer"); 8018c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); 8028c2ecf20Sopenharmony_ci info->dma.pseudo(info->host, &info->scsi.SCp, 8038c2ecf20Sopenharmony_ci direction, info->SCpnt->transfersize); 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci case fasdma_real_block: 8078c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUFFER, "block dma transfer"); 8088c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); 8098c2ecf20Sopenharmony_ci break; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci case fasdma_real_all: 8128c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUFFER, "total dma transfer"); 8138c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci default: 8178c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUFFER | LOG_ERROR, 8188c2ecf20Sopenharmony_ci "invalid FAS216 DMA type"); 8198c2ecf20Sopenharmony_ci break; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci/** 8248c2ecf20Sopenharmony_ci * fas216_stoptransfer - Stop a DMA transfer onto / off of the card 8258c2ecf20Sopenharmony_ci * @info: interface from which device disconnected from 8268c2ecf20Sopenharmony_ci * 8278c2ecf20Sopenharmony_ci * Called when we switch away from DATA IN or DATA OUT phases. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_cistatic void fas216_stoptransfer(FAS216_Info *info) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci fas216_checkmagic(info); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (info->dma.transfer_type == fasdma_real_all || 8348c2ecf20Sopenharmony_ci info->dma.transfer_type == fasdma_real_block) 8358c2ecf20Sopenharmony_ci info->dma.stop(info->host, &info->scsi.SCp); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci fas216_cleanuptransfer(info); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (info->scsi.phase == PHASE_DATAIN) { 8408c2ecf20Sopenharmony_ci unsigned int fifo; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci /* 8438c2ecf20Sopenharmony_ci * If we were performing Data-In, then the FIFO counter 8448c2ecf20Sopenharmony_ci * contains the number of bytes not transferred via DMA 8458c2ecf20Sopenharmony_ci * from the on-board FIFO. Read them manually. 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; 8488c2ecf20Sopenharmony_ci while (fifo && info->scsi.SCp.ptr) { 8498c2ecf20Sopenharmony_ci *info->scsi.SCp.ptr = fas216_readb(info, REG_FF); 8508c2ecf20Sopenharmony_ci fas216_updateptrs(info, 1); 8518c2ecf20Sopenharmony_ci fifo--; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci } else { 8548c2ecf20Sopenharmony_ci /* 8558c2ecf20Sopenharmony_ci * After a Data-Out phase, there may be unsent 8568c2ecf20Sopenharmony_ci * bytes left in the FIFO. Flush them out. 8578c2ecf20Sopenharmony_ci */ 8588c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci} 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic void fas216_aborttransfer(FAS216_Info *info) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci fas216_checkmagic(info); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (info->dma.transfer_type == fasdma_real_all || 8678c2ecf20Sopenharmony_ci info->dma.transfer_type == fasdma_real_block) 8688c2ecf20Sopenharmony_ci info->dma.stop(info->host, &info->scsi.SCp); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci info->dma.transfer_type = fasdma_none; 8718c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_cistatic void fas216_kick(FAS216_Info *info); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci/** 8778c2ecf20Sopenharmony_ci * fas216_disconnected_intr - handle device disconnection 8788c2ecf20Sopenharmony_ci * @info: interface from which device disconnected from 8798c2ecf20Sopenharmony_ci * 8808c2ecf20Sopenharmony_ci * Handle device disconnection 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_cistatic void fas216_disconnect_intr(FAS216_Info *info) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci unsigned long flags; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci fas216_checkmagic(info); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "disconnect phase=%02x", 8898c2ecf20Sopenharmony_ci info->scsi.phase); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci switch (info->scsi.phase) { 8948c2ecf20Sopenharmony_ci case PHASE_SELECTION: /* while selecting - no target */ 8958c2ecf20Sopenharmony_ci case PHASE_SELSTEPS: 8968c2ecf20Sopenharmony_ci fas216_done(info, DID_NO_CONNECT); 8978c2ecf20Sopenharmony_ci break; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ 9008c2ecf20Sopenharmony_ci info->scsi.disconnectable = 1; 9018c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_IDLE; 9028c2ecf20Sopenharmony_ci info->stats.disconnects += 1; 9038c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->host_lock, flags); 9048c2ecf20Sopenharmony_ci if (info->scsi.phase == PHASE_IDLE) 9058c2ecf20Sopenharmony_ci fas216_kick(info); 9068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->host_lock, flags); 9078c2ecf20Sopenharmony_ci break; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci case PHASE_DONE: /* at end of command - complete */ 9108c2ecf20Sopenharmony_ci fas216_done(info, DID_OK); 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci case PHASE_MSGOUT: /* message out - possible ABORT message */ 9148c2ecf20Sopenharmony_ci if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { 9158c2ecf20Sopenharmony_ci info->scsi.aborting = 0; 9168c2ecf20Sopenharmony_ci fas216_done(info, DID_ABORT); 9178c2ecf20Sopenharmony_ci break; 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci fallthrough; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci default: /* huh? */ 9228c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n", 9238c2ecf20Sopenharmony_ci info->host->host_no, fas216_target(info), fas216_drv_phase(info)); 9248c2ecf20Sopenharmony_ci print_debug_list(); 9258c2ecf20Sopenharmony_ci fas216_stoptransfer(info); 9268c2ecf20Sopenharmony_ci fas216_done(info, DID_ERROR); 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/** 9328c2ecf20Sopenharmony_ci * fas216_reselected_intr - start reconnection of a device 9338c2ecf20Sopenharmony_ci * @info: interface which was reselected 9348c2ecf20Sopenharmony_ci * 9358c2ecf20Sopenharmony_ci * Start reconnection of a device 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_cistatic void 9388c2ecf20Sopenharmony_cifas216_reselected_intr(FAS216_Info *info) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci unsigned int cfis, i; 9418c2ecf20Sopenharmony_ci unsigned char msg[4]; 9428c2ecf20Sopenharmony_ci unsigned char target, lun, tag; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci fas216_checkmagic(info); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci WARN_ON(info->scsi.phase == PHASE_SELECTION || 9478c2ecf20Sopenharmony_ci info->scsi.phase == PHASE_SELSTEPS); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci cfis = fas216_readb(info, REG_CFIS); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x", 9528c2ecf20Sopenharmony_ci info->scsi.phase, cfis); 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci cfis &= CFIS_CF; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (cfis < 2 || cfis > 4) { 9578c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", 9588c2ecf20Sopenharmony_ci info->host->host_no); 9598c2ecf20Sopenharmony_ci goto bad_message; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci for (i = 0; i < cfis; i++) 9638c2ecf20Sopenharmony_ci msg[i] = fas216_readb(info, REG_FF); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (!(msg[0] & (1 << info->host->this_id)) || 9668c2ecf20Sopenharmony_ci !(msg[1] & 0x80)) 9678c2ecf20Sopenharmony_ci goto initiator_error; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci target = msg[0] & ~(1 << info->host->this_id); 9708c2ecf20Sopenharmony_ci target = ffs(target) - 1; 9718c2ecf20Sopenharmony_ci lun = msg[1] & 7; 9728c2ecf20Sopenharmony_ci tag = 0; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (cfis >= 3) { 9758c2ecf20Sopenharmony_ci if (msg[2] != SIMPLE_QUEUE_TAG) 9768c2ecf20Sopenharmony_ci goto initiator_error; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci tag = msg[3]; 9798c2ecf20Sopenharmony_ci } 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci /* set up for synchronous transfers */ 9828c2ecf20Sopenharmony_ci fas216_writeb(info, REG_SDID, target); 9838c2ecf20Sopenharmony_ci fas216_set_sync(info, target); 9848c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x", 9878c2ecf20Sopenharmony_ci target, lun, tag); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (info->scsi.disconnectable && info->SCpnt) { 9908c2ecf20Sopenharmony_ci info->scsi.disconnectable = 0; 9918c2ecf20Sopenharmony_ci if (info->SCpnt->device->id == target && 9928c2ecf20Sopenharmony_ci info->SCpnt->device->lun == lun && 9938c2ecf20Sopenharmony_ci info->SCpnt->tag == tag) { 9948c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "reconnected previously executing command"); 9958c2ecf20Sopenharmony_ci } else { 9968c2ecf20Sopenharmony_ci queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); 9978c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue"); 9988c2ecf20Sopenharmony_ci info->SCpnt = NULL; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci if (!info->SCpnt) { 10028c2ecf20Sopenharmony_ci info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected, 10038c2ecf20Sopenharmony_ci target, lun, tag); 10048c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "had to get command"); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (info->SCpnt) { 10088c2ecf20Sopenharmony_ci /* 10098c2ecf20Sopenharmony_ci * Restore data pointer from SAVED data pointer 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_ci info->scsi.SCp = info->SCpnt->SCp; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]", 10148c2ecf20Sopenharmony_ci info->scsi.SCp.ptr, info->scsi.SCp.this_residual); 10158c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGIN; 10168c2ecf20Sopenharmony_ci } else { 10178c2ecf20Sopenharmony_ci /* 10188c2ecf20Sopenharmony_ci * Our command structure not found - abort the 10198c2ecf20Sopenharmony_ci * command on the target. Since we have no 10208c2ecf20Sopenharmony_ci * record of this command, we can't send 10218c2ecf20Sopenharmony_ci * an INITIATOR DETECTED ERROR message. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci#if 0 10268c2ecf20Sopenharmony_ci if (tag) 10278c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, tag); 10288c2ecf20Sopenharmony_ci else 10298c2ecf20Sopenharmony_ci#endif 10308c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); 10318c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 10328c2ecf20Sopenharmony_ci info->scsi.aborting = 1; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_MSGACCEPTED); 10368c2ecf20Sopenharmony_ci return; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci initiator_error: 10398c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.H: error during reselection: bytes", 10408c2ecf20Sopenharmony_ci info->host->host_no); 10418c2ecf20Sopenharmony_ci for (i = 0; i < cfis; i++) 10428c2ecf20Sopenharmony_ci printk(" %02x", msg[i]); 10438c2ecf20Sopenharmony_ci printk("\n"); 10448c2ecf20Sopenharmony_ci bad_message: 10458c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 10468c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 10478c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); 10488c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 10498c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_MSGACCEPTED); 10508c2ecf20Sopenharmony_ci} 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_cistatic void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci int i; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci switch (message[0]) { 10578c2ecf20Sopenharmony_ci case COMMAND_COMPLETE: 10588c2ecf20Sopenharmony_ci if (msglen != 1) 10598c2ecf20Sopenharmony_ci goto unrecognised; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: command complete with no " 10628c2ecf20Sopenharmony_ci "status in MESSAGE_IN?\n", 10638c2ecf20Sopenharmony_ci info->host->host_no, fas216_target(info)); 10648c2ecf20Sopenharmony_ci break; 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci case SAVE_POINTERS: 10678c2ecf20Sopenharmony_ci if (msglen != 1) 10688c2ecf20Sopenharmony_ci goto unrecognised; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* 10718c2ecf20Sopenharmony_ci * Save current data pointer to SAVED data pointer 10728c2ecf20Sopenharmony_ci * SCSI II standard says that we must not acknowledge 10738c2ecf20Sopenharmony_ci * this until we have really saved pointers. 10748c2ecf20Sopenharmony_ci * NOTE: we DO NOT save the command nor status pointers 10758c2ecf20Sopenharmony_ci * as required by the SCSI II standard. These always 10768c2ecf20Sopenharmony_ci * point to the start of their respective areas. 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_ci info->SCpnt->SCp = info->scsi.SCp; 10798c2ecf20Sopenharmony_ci info->SCpnt->SCp.sent_command = 0; 10808c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, 10818c2ecf20Sopenharmony_ci "save data pointers: [%p, %X]", 10828c2ecf20Sopenharmony_ci info->scsi.SCp.ptr, info->scsi.SCp.this_residual); 10838c2ecf20Sopenharmony_ci break; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci case RESTORE_POINTERS: 10868c2ecf20Sopenharmony_ci if (msglen != 1) 10878c2ecf20Sopenharmony_ci goto unrecognised; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* 10908c2ecf20Sopenharmony_ci * Restore current data pointer from SAVED data pointer 10918c2ecf20Sopenharmony_ci */ 10928c2ecf20Sopenharmony_ci info->scsi.SCp = info->SCpnt->SCp; 10938c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, 10948c2ecf20Sopenharmony_ci "restore data pointers: [%p, 0x%x]", 10958c2ecf20Sopenharmony_ci info->scsi.SCp.ptr, info->scsi.SCp.this_residual); 10968c2ecf20Sopenharmony_ci break; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci case DISCONNECT: 10998c2ecf20Sopenharmony_ci if (msglen != 1) 11008c2ecf20Sopenharmony_ci goto unrecognised; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGIN_DISCONNECT; 11038c2ecf20Sopenharmony_ci break; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci case MESSAGE_REJECT: 11068c2ecf20Sopenharmony_ci if (msglen != 1) 11078c2ecf20Sopenharmony_ci goto unrecognised; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) { 11108c2ecf20Sopenharmony_ci case EXTENDED_MESSAGE | EXTENDED_SDTR << 8: 11118c2ecf20Sopenharmony_ci fas216_handlesync(info, message); 11128c2ecf20Sopenharmony_ci break; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci default: 11158c2ecf20Sopenharmony_ci fas216_log(info, 0, "reject, last message 0x%04x", 11168c2ecf20Sopenharmony_ci fas216_get_last_msg(info, info->scsi.msgin_fifo)); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci break; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci case NOP: 11218c2ecf20Sopenharmony_ci break; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci case EXTENDED_MESSAGE: 11248c2ecf20Sopenharmony_ci if (msglen < 3) 11258c2ecf20Sopenharmony_ci goto unrecognised; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci switch (message[2]) { 11288c2ecf20Sopenharmony_ci case EXTENDED_SDTR: /* Sync transfer negotiation request/reply */ 11298c2ecf20Sopenharmony_ci fas216_handlesync(info, message); 11308c2ecf20Sopenharmony_ci break; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci default: 11338c2ecf20Sopenharmony_ci goto unrecognised; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci break; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci default: 11388c2ecf20Sopenharmony_ci goto unrecognised; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci return; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ciunrecognised: 11438c2ecf20Sopenharmony_ci fas216_log(info, 0, "unrecognised message, rejecting"); 11448c2ecf20Sopenharmony_ci printk("scsi%d.%c: message was", info->host->host_no, fas216_target(info)); 11458c2ecf20Sopenharmony_ci for (i = 0; i < msglen; i++) 11468c2ecf20Sopenharmony_ci printk("%s%02X", i & 31 ? " " : "\n ", message[i]); 11478c2ecf20Sopenharmony_ci printk("\n"); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* 11508c2ecf20Sopenharmony_ci * Something strange seems to be happening here - 11518c2ecf20Sopenharmony_ci * I can't use SETATN since the chip gives me an 11528c2ecf20Sopenharmony_ci * invalid command interrupt when I do. Weird. 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_cifas216_cmd(info, CMD_NOP); 11558c2ecf20Sopenharmony_cifas216_dumpstate(info); 11568c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 11578c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 11588c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); 11598c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 11608c2ecf20Sopenharmony_cifas216_dumpstate(info); 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic int fas216_wait_cmd(FAS216_Info *info, int cmd) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci int tout; 11668c2ecf20Sopenharmony_ci int stat; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci fas216_cmd(info, cmd); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci for (tout = 1000; tout; tout -= 1) { 11718c2ecf20Sopenharmony_ci stat = fas216_readb(info, REG_STAT); 11728c2ecf20Sopenharmony_ci if (stat & (STAT_INT|STAT_PARITYERROR)) 11738c2ecf20Sopenharmony_ci break; 11748c2ecf20Sopenharmony_ci udelay(1); 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci return stat; 11788c2ecf20Sopenharmony_ci} 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic int fas216_get_msg_byte(FAS216_Info *info) 11818c2ecf20Sopenharmony_ci{ 11828c2ecf20Sopenharmony_ci unsigned int stat = fas216_wait_cmd(info, CMD_MSGACCEPTED); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if ((stat & STAT_INT) == 0) 11858c2ecf20Sopenharmony_ci goto timedout; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci if ((stat & STAT_BUSMASK) != STAT_MESGIN) 11888c2ecf20Sopenharmony_ci goto unexpected_phase_change; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci fas216_readb(info, REG_INST); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci stat = fas216_wait_cmd(info, CMD_TRANSFERINFO); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci if ((stat & STAT_INT) == 0) 11958c2ecf20Sopenharmony_ci goto timedout; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci if (stat & STAT_PARITYERROR) 11988c2ecf20Sopenharmony_ci goto parity_error; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if ((stat & STAT_BUSMASK) != STAT_MESGIN) 12018c2ecf20Sopenharmony_ci goto unexpected_phase_change; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci fas216_readb(info, REG_INST); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci return fas216_readb(info, REG_FF); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_citimedout: 12088c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "timed out waiting for message byte"); 12098c2ecf20Sopenharmony_ci return -1; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ciunexpected_phase_change: 12128c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "unexpected phase change: status = %02x", stat); 12138c2ecf20Sopenharmony_ci return -2; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ciparity_error: 12168c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "parity error during message in phase"); 12178c2ecf20Sopenharmony_ci return -3; 12188c2ecf20Sopenharmony_ci} 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci/** 12218c2ecf20Sopenharmony_ci * fas216_message - handle a function done interrupt from FAS216 chip 12228c2ecf20Sopenharmony_ci * @info: interface which caused function done interrupt 12238c2ecf20Sopenharmony_ci * 12248c2ecf20Sopenharmony_ci * Handle a function done interrupt from FAS216 chip 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_cistatic void fas216_message(FAS216_Info *info) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci unsigned char *message = info->scsi.message; 12298c2ecf20Sopenharmony_ci unsigned int msglen = 1; 12308c2ecf20Sopenharmony_ci int msgbyte = 0; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci fas216_checkmagic(info); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci message[0] = fas216_readb(info, REG_FF); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (message[0] == EXTENDED_MESSAGE) { 12378c2ecf20Sopenharmony_ci msgbyte = fas216_get_msg_byte(info); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci if (msgbyte >= 0) { 12408c2ecf20Sopenharmony_ci message[1] = msgbyte; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci for (msglen = 2; msglen < message[1] + 2; msglen++) { 12438c2ecf20Sopenharmony_ci msgbyte = fas216_get_msg_byte(info); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if (msgbyte >= 0) 12468c2ecf20Sopenharmony_ci message[msglen] = msgbyte; 12478c2ecf20Sopenharmony_ci else 12488c2ecf20Sopenharmony_ci break; 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (msgbyte == -3) 12548c2ecf20Sopenharmony_ci goto parity_error; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci#ifdef DEBUG_MESSAGES 12578c2ecf20Sopenharmony_ci { 12588c2ecf20Sopenharmony_ci int i; 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci printk("scsi%d.%c: message in: ", 12618c2ecf20Sopenharmony_ci info->host->host_no, fas216_target(info)); 12628c2ecf20Sopenharmony_ci for (i = 0; i < msglen; i++) 12638c2ecf20Sopenharmony_ci printk("%02X ", message[i]); 12648c2ecf20Sopenharmony_ci printk("\n"); 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci#endif 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci fas216_parse_message(info, message, msglen); 12698c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_MSGACCEPTED); 12708c2ecf20Sopenharmony_ci return; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ciparity_error: 12738c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 12748c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 12758c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, MSG_PARITY_ERROR); 12768c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 12778c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_MSGACCEPTED); 12788c2ecf20Sopenharmony_ci return; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci/** 12828c2ecf20Sopenharmony_ci * fas216_send_command - send command after all message bytes have been sent 12838c2ecf20Sopenharmony_ci * @info: interface which caused bus service 12848c2ecf20Sopenharmony_ci * 12858c2ecf20Sopenharmony_ci * Send a command to a target after all message bytes have been sent 12868c2ecf20Sopenharmony_ci */ 12878c2ecf20Sopenharmony_cistatic void fas216_send_command(FAS216_Info *info) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci int i; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci fas216_checkmagic(info); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_NOP|CMD_WITHDMA); 12948c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci /* load command */ 12978c2ecf20Sopenharmony_ci for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++) 12988c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, info->SCpnt->cmnd[i]); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_COMMAND; 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci/** 13068c2ecf20Sopenharmony_ci * fas216_send_messageout - handle bus service to send a message 13078c2ecf20Sopenharmony_ci * @info: interface which caused bus service 13088c2ecf20Sopenharmony_ci * 13098c2ecf20Sopenharmony_ci * Handle bus service to send a message. 13108c2ecf20Sopenharmony_ci * Note: We do not allow the device to change the data direction! 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_cistatic void fas216_send_messageout(FAS216_Info *info, int start) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci fas216_checkmagic(info); 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci if (tot_msglen) { 13218c2ecf20Sopenharmony_ci struct message *msg; 13228c2ecf20Sopenharmony_ci int msgnr = 0; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { 13258c2ecf20Sopenharmony_ci int i; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci for (i = start; i < msg->length; i++) 13288c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, msg->msg[i]); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); 13318c2ecf20Sopenharmony_ci start = 0; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci } else 13348c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, NOP); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO); 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT; 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci/** 13428c2ecf20Sopenharmony_ci * fas216_busservice_intr - handle bus service interrupt from FAS216 chip 13438c2ecf20Sopenharmony_ci * @info: interface which caused bus service interrupt 13448c2ecf20Sopenharmony_ci * @stat: Status register contents 13458c2ecf20Sopenharmony_ci * @is: SCSI Status register contents 13468c2ecf20Sopenharmony_ci * 13478c2ecf20Sopenharmony_ci * Handle a bus service interrupt from FAS216 chip 13488c2ecf20Sopenharmony_ci */ 13498c2ecf20Sopenharmony_cistatic void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int is) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci fas216_checkmagic(info); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci fas216_log(info, LOG_BUSSERVICE, 13548c2ecf20Sopenharmony_ci "bus service: stat=%02x is=%02x phase=%02x", 13558c2ecf20Sopenharmony_ci stat, is, info->scsi.phase); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci switch (info->scsi.phase) { 13588c2ecf20Sopenharmony_ci case PHASE_SELECTION: 13598c2ecf20Sopenharmony_ci if ((is & IS_BITS) != IS_MSGBYTESENT) 13608c2ecf20Sopenharmony_ci goto bad_is; 13618c2ecf20Sopenharmony_ci break; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci case PHASE_SELSTEPS: 13648c2ecf20Sopenharmony_ci switch (is & IS_BITS) { 13658c2ecf20Sopenharmony_ci case IS_SELARB: 13668c2ecf20Sopenharmony_ci case IS_MSGBYTESENT: 13678c2ecf20Sopenharmony_ci goto bad_is; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci case IS_NOTCOMMAND: 13708c2ecf20Sopenharmony_ci case IS_EARLYPHASE: 13718c2ecf20Sopenharmony_ci if ((stat & STAT_BUSMASK) == STAT_MESGIN) 13728c2ecf20Sopenharmony_ci break; 13738c2ecf20Sopenharmony_ci goto bad_is; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci case IS_COMPLETE: 13768c2ecf20Sopenharmony_ci break; 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci default: 13808c2ecf20Sopenharmony_ci break; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_NOP); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci#define STATE(st,ph) ((ph) << 3 | (st)) 13868c2ecf20Sopenharmony_ci /* This table describes the legal SCSI state transitions, 13878c2ecf20Sopenharmony_ci * as described by the SCSI II spec. 13888c2ecf20Sopenharmony_ci */ 13898c2ecf20Sopenharmony_ci switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { 13908c2ecf20Sopenharmony_ci case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ 13918c2ecf20Sopenharmony_ci case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ 13928c2ecf20Sopenharmony_ci case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ 13938c2ecf20Sopenharmony_ci case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */ 13948c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_DATAIN; 13958c2ecf20Sopenharmony_ci fas216_transfer(info); 13968c2ecf20Sopenharmony_ci return; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */ 13998c2ecf20Sopenharmony_ci case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */ 14008c2ecf20Sopenharmony_ci fas216_cleanuptransfer(info); 14018c2ecf20Sopenharmony_ci fas216_transfer(info); 14028c2ecf20Sopenharmony_ci return; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ 14058c2ecf20Sopenharmony_ci case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ 14068c2ecf20Sopenharmony_ci case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ 14078c2ecf20Sopenharmony_ci case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */ 14088c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 14098c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_DATAOUT; 14108c2ecf20Sopenharmony_ci fas216_transfer(info); 14118c2ecf20Sopenharmony_ci return; 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ 14148c2ecf20Sopenharmony_ci case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ 14158c2ecf20Sopenharmony_ci fas216_stoptransfer(info); 14168c2ecf20Sopenharmony_ci fallthrough; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ 14198c2ecf20Sopenharmony_ci case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ 14208c2ecf20Sopenharmony_ci case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ 14218c2ecf20Sopenharmony_ci case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ 14228c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_INITCMDCOMPLETE); 14238c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_STATUS; 14248c2ecf20Sopenharmony_ci return; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ 14278c2ecf20Sopenharmony_ci case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ 14288c2ecf20Sopenharmony_ci fas216_stoptransfer(info); 14298c2ecf20Sopenharmony_ci fallthrough; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */ 14328c2ecf20Sopenharmony_ci case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ 14338c2ecf20Sopenharmony_ci case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ 14348c2ecf20Sopenharmony_ci info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; 14358c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 14368c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO); 14378c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGIN; 14388c2ecf20Sopenharmony_ci return; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci case STATE(STAT_MESGIN, PHASE_MSGIN): 14418c2ecf20Sopenharmony_ci info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; 14428c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO); 14438c2ecf20Sopenharmony_ci return; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ 14468c2ecf20Sopenharmony_ci case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ 14478c2ecf20Sopenharmony_ci fas216_send_command(info); 14488c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_COMMAND; 14498c2ecf20Sopenharmony_ci return; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci /* 14538c2ecf20Sopenharmony_ci * Selection -> Message Out 14548c2ecf20Sopenharmony_ci */ 14558c2ecf20Sopenharmony_ci case STATE(STAT_MESGOUT, PHASE_SELECTION): 14568c2ecf20Sopenharmony_ci fas216_send_messageout(info, 1); 14578c2ecf20Sopenharmony_ci return; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci /* 14608c2ecf20Sopenharmony_ci * Message Out -> Message Out 14618c2ecf20Sopenharmony_ci */ 14628c2ecf20Sopenharmony_ci case STATE(STAT_MESGOUT, PHASE_SELSTEPS): 14638c2ecf20Sopenharmony_ci case STATE(STAT_MESGOUT, PHASE_MSGOUT): 14648c2ecf20Sopenharmony_ci /* 14658c2ecf20Sopenharmony_ci * If we get another message out phase, this usually 14668c2ecf20Sopenharmony_ci * means some parity error occurred. Resend complete 14678c2ecf20Sopenharmony_ci * set of messages. If we have more than one byte to 14688c2ecf20Sopenharmony_ci * send, we need to assert ATN again. 14698c2ecf20Sopenharmony_ci */ 14708c2ecf20Sopenharmony_ci if (info->device[info->SCpnt->device->id].parity_check) { 14718c2ecf20Sopenharmony_ci /* 14728c2ecf20Sopenharmony_ci * We were testing... good, the device 14738c2ecf20Sopenharmony_ci * supports parity checking. 14748c2ecf20Sopenharmony_ci */ 14758c2ecf20Sopenharmony_ci info->device[info->SCpnt->device->id].parity_check = 0; 14768c2ecf20Sopenharmony_ci info->device[info->SCpnt->device->id].parity_enabled = 1; 14778c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); 14788c2ecf20Sopenharmony_ci } 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci if (msgqueue_msglength(&info->scsi.msgs) > 1) 14818c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 14828c2ecf20Sopenharmony_ci /*FALLTHROUGH*/ 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci /* 14858c2ecf20Sopenharmony_ci * Any -> Message Out 14868c2ecf20Sopenharmony_ci */ 14878c2ecf20Sopenharmony_ci case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT): 14888c2ecf20Sopenharmony_ci fas216_send_messageout(info, 0); 14898c2ecf20Sopenharmony_ci return; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci /* Error recovery rules. 14928c2ecf20Sopenharmony_ci * These either attempt to abort or retry the operation. 14938c2ecf20Sopenharmony_ci * TODO: we need more of these 14948c2ecf20Sopenharmony_ci */ 14958c2ecf20Sopenharmony_ci case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ 14968c2ecf20Sopenharmony_ci /* error - we've sent out all the command bytes 14978c2ecf20Sopenharmony_ci * we have. 14988c2ecf20Sopenharmony_ci * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS 14998c2ecf20Sopenharmony_ci * to include the command bytes sent for this to work 15008c2ecf20Sopenharmony_ci * correctly. 15018c2ecf20Sopenharmony_ci */ 15028c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: " 15038c2ecf20Sopenharmony_ci "target trying to receive more command bytes\n", 15048c2ecf20Sopenharmony_ci info->host->host_no, fas216_target(info)); 15058c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 15068c2ecf20Sopenharmony_ci fas216_set_stc(info, 15); 15078c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA); 15088c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 15098c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); 15108c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 15118c2ecf20Sopenharmony_ci return; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { 15158c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", 15168c2ecf20Sopenharmony_ci info->host->host_no, fas216_target(info), 15178c2ecf20Sopenharmony_ci fas216_bus_phase(stat)); 15188c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 15198c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SETATN); 15208c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); 15218c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_MSGOUT_EXPECT; 15228c2ecf20Sopenharmony_ci info->scsi.aborting = 1; 15238c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_TRANSFERINFO); 15248c2ecf20Sopenharmony_ci return; 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", 15278c2ecf20Sopenharmony_ci info->host->host_no, fas216_target(info), 15288c2ecf20Sopenharmony_ci fas216_bus_phase(stat), 15298c2ecf20Sopenharmony_ci fas216_drv_phase(info)); 15308c2ecf20Sopenharmony_ci print_debug_list(); 15318c2ecf20Sopenharmony_ci return; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_cibad_is: 15348c2ecf20Sopenharmony_ci fas216_log(info, 0, "bus service at step %d?", is & IS_BITS); 15358c2ecf20Sopenharmony_ci fas216_dumpstate(info); 15368c2ecf20Sopenharmony_ci print_debug_list(); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci fas216_done(info, DID_ERROR); 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci/** 15428c2ecf20Sopenharmony_ci * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip 15438c2ecf20Sopenharmony_ci * @info: interface which caused function done interrupt 15448c2ecf20Sopenharmony_ci * @stat: Status register contents 15458c2ecf20Sopenharmony_ci * @is: SCSI Status register contents 15468c2ecf20Sopenharmony_ci * 15478c2ecf20Sopenharmony_ci * Handle a function done interrupt from FAS216 chip 15488c2ecf20Sopenharmony_ci */ 15498c2ecf20Sopenharmony_cistatic void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci fas216_checkmagic(info); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci fas216_log(info, LOG_FUNCTIONDONE, 15568c2ecf20Sopenharmony_ci "function done: stat=%02x is=%02x phase=%02x", 15578c2ecf20Sopenharmony_ci stat, is, info->scsi.phase); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci switch (info->scsi.phase) { 15608c2ecf20Sopenharmony_ci case PHASE_STATUS: /* status phase - read status and msg */ 15618c2ecf20Sopenharmony_ci if (fifo_len != 2) { 15628c2ecf20Sopenharmony_ci fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len); 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci /* 15658c2ecf20Sopenharmony_ci * Read status then message byte. 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_ci info->scsi.SCp.Status = fas216_readb(info, REG_FF); 15688c2ecf20Sopenharmony_ci info->scsi.SCp.Message = fas216_readb(info, REG_FF); 15698c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_DONE; 15708c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_MSGACCEPTED); 15718c2ecf20Sopenharmony_ci break; 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci case PHASE_IDLE: 15748c2ecf20Sopenharmony_ci case PHASE_SELECTION: 15758c2ecf20Sopenharmony_ci case PHASE_SELSTEPS: 15768c2ecf20Sopenharmony_ci break; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci case PHASE_MSGIN: /* message in phase */ 15798c2ecf20Sopenharmony_ci if ((stat & STAT_BUSMASK) == STAT_MESGIN) { 15808c2ecf20Sopenharmony_ci info->scsi.msgin_fifo = fifo_len; 15818c2ecf20Sopenharmony_ci fas216_message(info); 15828c2ecf20Sopenharmony_ci break; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci fallthrough; 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci default: 15878c2ecf20Sopenharmony_ci fas216_log(info, 0, "internal phase %s for function done?" 15888c2ecf20Sopenharmony_ci " What do I do with this?", 15898c2ecf20Sopenharmony_ci fas216_target(info), fas216_drv_phase(info)); 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_cistatic void fas216_bus_reset(FAS216_Info *info) 15948c2ecf20Sopenharmony_ci{ 15958c2ecf20Sopenharmony_ci neg_t sync_state; 15968c2ecf20Sopenharmony_ci int i; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci sync_state = neg_invalid; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci#ifdef SCSI2_SYNC 16038c2ecf20Sopenharmony_ci if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA)) 16048c2ecf20Sopenharmony_ci sync_state = neg_wait; 16058c2ecf20Sopenharmony_ci#endif 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_IDLE; 16088c2ecf20Sopenharmony_ci info->SCpnt = NULL; /* bug! */ 16098c2ecf20Sopenharmony_ci memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp)); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 16128c2ecf20Sopenharmony_ci info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; 16138c2ecf20Sopenharmony_ci info->device[i].sync_state = sync_state; 16148c2ecf20Sopenharmony_ci info->device[i].period = info->ifcfg.asyncperiod / 4; 16158c2ecf20Sopenharmony_ci info->device[i].stp = info->scsi.async_stp; 16168c2ecf20Sopenharmony_ci info->device[i].sof = 0; 16178c2ecf20Sopenharmony_ci info->device[i].wide_xfer = 0; 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci info->rst_bus_status = 1; 16218c2ecf20Sopenharmony_ci wake_up(&info->eh_wait); 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci/** 16258c2ecf20Sopenharmony_ci * fas216_intr - handle interrupts to progress a command 16268c2ecf20Sopenharmony_ci * @info: interface to service 16278c2ecf20Sopenharmony_ci * 16288c2ecf20Sopenharmony_ci * Handle interrupts from the interface to progress a command 16298c2ecf20Sopenharmony_ci */ 16308c2ecf20Sopenharmony_ciirqreturn_t fas216_intr(FAS216_Info *info) 16318c2ecf20Sopenharmony_ci{ 16328c2ecf20Sopenharmony_ci unsigned char inst, is, stat; 16338c2ecf20Sopenharmony_ci int handled = IRQ_NONE; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci fas216_checkmagic(info); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci stat = fas216_readb(info, REG_STAT); 16388c2ecf20Sopenharmony_ci is = fas216_readb(info, REG_IS); 16398c2ecf20Sopenharmony_ci inst = fas216_readb(info, REG_INST); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci add_debug_list(stat, is, inst, info->scsi.phase); 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci if (stat & STAT_INT) { 16448c2ecf20Sopenharmony_ci if (inst & INST_BUSRESET) { 16458c2ecf20Sopenharmony_ci fas216_log(info, 0, "bus reset detected"); 16468c2ecf20Sopenharmony_ci fas216_bus_reset(info); 16478c2ecf20Sopenharmony_ci scsi_report_bus_reset(info->host, 0); 16488c2ecf20Sopenharmony_ci } else if (inst & INST_ILLEGALCMD) { 16498c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "illegal command given\n"); 16508c2ecf20Sopenharmony_ci fas216_dumpstate(info); 16518c2ecf20Sopenharmony_ci print_debug_list(); 16528c2ecf20Sopenharmony_ci } else if (inst & INST_DISCONNECT) 16538c2ecf20Sopenharmony_ci fas216_disconnect_intr(info); 16548c2ecf20Sopenharmony_ci else if (inst & INST_RESELECTED) /* reselected */ 16558c2ecf20Sopenharmony_ci fas216_reselected_intr(info); 16568c2ecf20Sopenharmony_ci else if (inst & INST_BUSSERVICE) /* bus service request */ 16578c2ecf20Sopenharmony_ci fas216_busservice_intr(info, stat, is); 16588c2ecf20Sopenharmony_ci else if (inst & INST_FUNCDONE) /* function done */ 16598c2ecf20Sopenharmony_ci fas216_funcdone_intr(info, stat, is); 16608c2ecf20Sopenharmony_ci else 16618c2ecf20Sopenharmony_ci fas216_log(info, 0, "unknown interrupt received:" 16628c2ecf20Sopenharmony_ci " phase %s inst %02X is %02X stat %02X", 16638c2ecf20Sopenharmony_ci fas216_drv_phase(info), inst, is, stat); 16648c2ecf20Sopenharmony_ci handled = IRQ_HANDLED; 16658c2ecf20Sopenharmony_ci } 16668c2ecf20Sopenharmony_ci return handled; 16678c2ecf20Sopenharmony_ci} 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cistatic void __fas216_start_command(FAS216_Info *info, struct scsi_cmnd *SCpnt) 16708c2ecf20Sopenharmony_ci{ 16718c2ecf20Sopenharmony_ci int tot_msglen; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci /* following what the ESP driver says */ 16748c2ecf20Sopenharmony_ci fas216_set_stc(info, 0); 16758c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_NOP | CMD_WITHDMA); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci /* flush FIFO */ 16788c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci /* load bus-id and timeout */ 16818c2ecf20Sopenharmony_ci fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); 16828c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci /* synchronous transfers */ 16858c2ecf20Sopenharmony_ci fas216_set_sync(info, SCpnt->device->id); 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci tot_msglen = msgqueue_msglength(&info->scsi.msgs); 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci#ifdef DEBUG_MESSAGES 16908c2ecf20Sopenharmony_ci { 16918c2ecf20Sopenharmony_ci struct message *msg; 16928c2ecf20Sopenharmony_ci int msgnr = 0, i; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci printk("scsi%d.%c: message out: ", 16958c2ecf20Sopenharmony_ci info->host->host_no, '0' + SCpnt->device->id); 16968c2ecf20Sopenharmony_ci while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { 16978c2ecf20Sopenharmony_ci printk("{ "); 16988c2ecf20Sopenharmony_ci for (i = 0; i < msg->length; i++) 16998c2ecf20Sopenharmony_ci printk("%02x ", msg->msg[i]); 17008c2ecf20Sopenharmony_ci printk("} "); 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci printk("\n"); 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci#endif 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (tot_msglen == 1 || tot_msglen == 3) { 17078c2ecf20Sopenharmony_ci /* 17088c2ecf20Sopenharmony_ci * We have an easy message length to send... 17098c2ecf20Sopenharmony_ci */ 17108c2ecf20Sopenharmony_ci struct message *msg; 17118c2ecf20Sopenharmony_ci int msgnr = 0, i; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_SELSTEPS; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci /* load message bytes */ 17168c2ecf20Sopenharmony_ci while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { 17178c2ecf20Sopenharmony_ci for (i = 0; i < msg->length; i++) 17188c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, msg->msg[i]); 17198c2ecf20Sopenharmony_ci msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci /* load command */ 17238c2ecf20Sopenharmony_ci for (i = 0; i < SCpnt->cmd_len; i++) 17248c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, SCpnt->cmnd[i]); 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci if (tot_msglen == 1) 17278c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SELECTATN); 17288c2ecf20Sopenharmony_ci else 17298c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SELECTATN3); 17308c2ecf20Sopenharmony_ci } else { 17318c2ecf20Sopenharmony_ci /* 17328c2ecf20Sopenharmony_ci * We have an unusual number of message bytes to send. 17338c2ecf20Sopenharmony_ci * Load first byte into fifo, and issue SELECT with ATN and 17348c2ecf20Sopenharmony_ci * stop steps. 17358c2ecf20Sopenharmony_ci */ 17368c2ecf20Sopenharmony_ci struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, msg->msg[0]); 17398c2ecf20Sopenharmony_ci msg->fifo = 1; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SELECTATNSTOP); 17428c2ecf20Sopenharmony_ci } 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci/* 17468c2ecf20Sopenharmony_ci * Decide whether we need to perform a parity test on this device. 17478c2ecf20Sopenharmony_ci * Can also be used to force parity error conditions during initial 17488c2ecf20Sopenharmony_ci * information transfer phase (message out) for test purposes. 17498c2ecf20Sopenharmony_ci */ 17508c2ecf20Sopenharmony_cistatic int parity_test(FAS216_Info *info, int target) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci#if 0 17538c2ecf20Sopenharmony_ci if (target == 3) { 17548c2ecf20Sopenharmony_ci info->device[target].parity_check = 0; 17558c2ecf20Sopenharmony_ci return 1; 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci#endif 17588c2ecf20Sopenharmony_ci return info->device[target].parity_check; 17598c2ecf20Sopenharmony_ci} 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_cistatic void fas216_start_command(FAS216_Info *info, struct scsi_cmnd *SCpnt) 17628c2ecf20Sopenharmony_ci{ 17638c2ecf20Sopenharmony_ci int disconnect_ok; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci /* 17668c2ecf20Sopenharmony_ci * claim host busy 17678c2ecf20Sopenharmony_ci */ 17688c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_SELECTION; 17698c2ecf20Sopenharmony_ci info->scsi.SCp = SCpnt->SCp; 17708c2ecf20Sopenharmony_ci info->SCpnt = SCpnt; 17718c2ecf20Sopenharmony_ci info->dma.transfer_type = fasdma_none; 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci if (parity_test(info, SCpnt->device->id)) 17748c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE); 17758c2ecf20Sopenharmony_ci else 17768c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci /* 17798c2ecf20Sopenharmony_ci * Don't allow request sense commands to disconnect. 17808c2ecf20Sopenharmony_ci */ 17818c2ecf20Sopenharmony_ci disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE && 17828c2ecf20Sopenharmony_ci info->device[SCpnt->device->id].disconnect_ok; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci /* 17858c2ecf20Sopenharmony_ci * build outgoing message bytes 17868c2ecf20Sopenharmony_ci */ 17878c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 17888c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->device->lun)); 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci /* 17918c2ecf20Sopenharmony_ci * add tag message if required 17928c2ecf20Sopenharmony_ci */ 17938c2ecf20Sopenharmony_ci if (SCpnt->tag) 17948c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci do { 17978c2ecf20Sopenharmony_ci#ifdef SCSI2_SYNC 17988c2ecf20Sopenharmony_ci if ((info->device[SCpnt->device->id].sync_state == neg_wait || 17998c2ecf20Sopenharmony_ci info->device[SCpnt->device->id].sync_state == neg_complete) && 18008c2ecf20Sopenharmony_ci (SCpnt->cmnd[0] == REQUEST_SENSE || 18018c2ecf20Sopenharmony_ci SCpnt->cmnd[0] == INQUIRY)) { 18028c2ecf20Sopenharmony_ci info->device[SCpnt->device->id].sync_state = neg_inprogress; 18038c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 5, 18048c2ecf20Sopenharmony_ci EXTENDED_MESSAGE, 3, EXTENDED_SDTR, 18058c2ecf20Sopenharmony_ci 1000 / info->ifcfg.clockrate, 18068c2ecf20Sopenharmony_ci info->ifcfg.sync_max_depth); 18078c2ecf20Sopenharmony_ci break; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci#endif 18108c2ecf20Sopenharmony_ci } while (0); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci __fas216_start_command(info, SCpnt); 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_cistatic void fas216_allocate_tag(FAS216_Info *info, struct scsi_cmnd *SCpnt) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci#ifdef SCSI2_TAG 18188c2ecf20Sopenharmony_ci /* 18198c2ecf20Sopenharmony_ci * tagged queuing - allocate a new tag to this command 18208c2ecf20Sopenharmony_ci */ 18218c2ecf20Sopenharmony_ci if (SCpnt->device->simple_tags && SCpnt->cmnd[0] != REQUEST_SENSE && 18228c2ecf20Sopenharmony_ci SCpnt->cmnd[0] != INQUIRY) { 18238c2ecf20Sopenharmony_ci SCpnt->device->current_tag += 1; 18248c2ecf20Sopenharmony_ci if (SCpnt->device->current_tag == 0) 18258c2ecf20Sopenharmony_ci SCpnt->device->current_tag = 1; 18268c2ecf20Sopenharmony_ci SCpnt->tag = SCpnt->device->current_tag; 18278c2ecf20Sopenharmony_ci } else 18288c2ecf20Sopenharmony_ci#endif 18298c2ecf20Sopenharmony_ci set_bit(SCpnt->device->id * 8 + 18308c2ecf20Sopenharmony_ci (u8)(SCpnt->device->lun & 0x7), info->busyluns); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci info->stats.removes += 1; 18338c2ecf20Sopenharmony_ci switch (SCpnt->cmnd[0]) { 18348c2ecf20Sopenharmony_ci case WRITE_6: 18358c2ecf20Sopenharmony_ci case WRITE_10: 18368c2ecf20Sopenharmony_ci case WRITE_12: 18378c2ecf20Sopenharmony_ci info->stats.writes += 1; 18388c2ecf20Sopenharmony_ci break; 18398c2ecf20Sopenharmony_ci case READ_6: 18408c2ecf20Sopenharmony_ci case READ_10: 18418c2ecf20Sopenharmony_ci case READ_12: 18428c2ecf20Sopenharmony_ci info->stats.reads += 1; 18438c2ecf20Sopenharmony_ci break; 18448c2ecf20Sopenharmony_ci default: 18458c2ecf20Sopenharmony_ci info->stats.miscs += 1; 18468c2ecf20Sopenharmony_ci break; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_cistatic void fas216_do_bus_device_reset(FAS216_Info *info, 18518c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci struct message *msg; 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci /* 18568c2ecf20Sopenharmony_ci * claim host busy 18578c2ecf20Sopenharmony_ci */ 18588c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_SELECTION; 18598c2ecf20Sopenharmony_ci info->scsi.SCp = SCpnt->SCp; 18608c2ecf20Sopenharmony_ci info->SCpnt = SCpnt; 18618c2ecf20Sopenharmony_ci info->dma.transfer_type = fasdma_none; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "sending bus device reset"); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci msgqueue_flush(&info->scsi.msgs); 18668c2ecf20Sopenharmony_ci msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET); 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci /* following what the ESP driver says */ 18698c2ecf20Sopenharmony_ci fas216_set_stc(info, 0); 18708c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_NOP | CMD_WITHDMA); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci /* flush FIFO */ 18738c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_FLUSHFIFO); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci /* load bus-id and timeout */ 18768c2ecf20Sopenharmony_ci fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); 18778c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci /* synchronous transfers */ 18808c2ecf20Sopenharmony_ci fas216_set_sync(info, SCpnt->device->id); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci msg = msgqueue_getmsg(&info->scsi.msgs, 0); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci fas216_writeb(info, REG_FF, BUS_DEVICE_RESET); 18858c2ecf20Sopenharmony_ci msg->fifo = 1; 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_SELECTATNSTOP); 18888c2ecf20Sopenharmony_ci} 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci/** 18918c2ecf20Sopenharmony_ci * fas216_kick - kick a command to the interface 18928c2ecf20Sopenharmony_ci * @info: our host interface to kick 18938c2ecf20Sopenharmony_ci * 18948c2ecf20Sopenharmony_ci * Kick a command to the interface, interface should be idle. 18958c2ecf20Sopenharmony_ci * Notes: Interrupts are always disabled! 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_cistatic void fas216_kick(FAS216_Info *info) 18988c2ecf20Sopenharmony_ci{ 18998c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt = NULL; 19008c2ecf20Sopenharmony_ci#define TYPE_OTHER 0 19018c2ecf20Sopenharmony_ci#define TYPE_RESET 1 19028c2ecf20Sopenharmony_ci#define TYPE_QUEUE 2 19038c2ecf20Sopenharmony_ci int where_from = TYPE_OTHER; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci fas216_checkmagic(info); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci /* 19088c2ecf20Sopenharmony_ci * Obtain the next command to process. 19098c2ecf20Sopenharmony_ci */ 19108c2ecf20Sopenharmony_ci do { 19118c2ecf20Sopenharmony_ci if (info->rstSCpnt) { 19128c2ecf20Sopenharmony_ci SCpnt = info->rstSCpnt; 19138c2ecf20Sopenharmony_ci /* don't remove it */ 19148c2ecf20Sopenharmony_ci where_from = TYPE_RESET; 19158c2ecf20Sopenharmony_ci break; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (info->reqSCpnt) { 19198c2ecf20Sopenharmony_ci SCpnt = info->reqSCpnt; 19208c2ecf20Sopenharmony_ci info->reqSCpnt = NULL; 19218c2ecf20Sopenharmony_ci break; 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci if (info->origSCpnt) { 19258c2ecf20Sopenharmony_ci SCpnt = info->origSCpnt; 19268c2ecf20Sopenharmony_ci info->origSCpnt = NULL; 19278c2ecf20Sopenharmony_ci break; 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci /* retrieve next command */ 19318c2ecf20Sopenharmony_ci if (!SCpnt) { 19328c2ecf20Sopenharmony_ci SCpnt = queue_remove_exclude(&info->queues.issue, 19338c2ecf20Sopenharmony_ci info->busyluns); 19348c2ecf20Sopenharmony_ci where_from = TYPE_QUEUE; 19358c2ecf20Sopenharmony_ci break; 19368c2ecf20Sopenharmony_ci } 19378c2ecf20Sopenharmony_ci } while (0); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci if (!SCpnt) { 19408c2ecf20Sopenharmony_ci /* 19418c2ecf20Sopenharmony_ci * no command pending, so enable reselection. 19428c2ecf20Sopenharmony_ci */ 19438c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_ENABLESEL); 19448c2ecf20Sopenharmony_ci return; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci /* 19488c2ecf20Sopenharmony_ci * We're going to start a command, so disable reselection 19498c2ecf20Sopenharmony_ci */ 19508c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_DISABLESEL); 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci if (info->scsi.disconnectable && info->SCpnt) { 19538c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, 19548c2ecf20Sopenharmony_ci "moved command for %d to disconnected queue", 19558c2ecf20Sopenharmony_ci info->SCpnt->device->id); 19568c2ecf20Sopenharmony_ci queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); 19578c2ecf20Sopenharmony_ci info->scsi.disconnectable = 0; 19588c2ecf20Sopenharmony_ci info->SCpnt = NULL; 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt, 19628c2ecf20Sopenharmony_ci "starting"); 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci switch (where_from) { 19658c2ecf20Sopenharmony_ci case TYPE_QUEUE: 19668c2ecf20Sopenharmony_ci fas216_allocate_tag(info, SCpnt); 19678c2ecf20Sopenharmony_ci fallthrough; 19688c2ecf20Sopenharmony_ci case TYPE_OTHER: 19698c2ecf20Sopenharmony_ci fas216_start_command(info, SCpnt); 19708c2ecf20Sopenharmony_ci break; 19718c2ecf20Sopenharmony_ci case TYPE_RESET: 19728c2ecf20Sopenharmony_ci fas216_do_bus_device_reset(info, SCpnt); 19738c2ecf20Sopenharmony_ci break; 19748c2ecf20Sopenharmony_ci } 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]", 19778c2ecf20Sopenharmony_ci info->scsi.SCp.ptr, info->scsi.SCp.this_residual); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci /* 19808c2ecf20Sopenharmony_ci * should now get either DISCONNECT or 19818c2ecf20Sopenharmony_ci * (FUNCTION DONE with BUS SERVICE) interrupt 19828c2ecf20Sopenharmony_ci */ 19838c2ecf20Sopenharmony_ci} 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci/* 19868c2ecf20Sopenharmony_ci * Clean up from issuing a BUS DEVICE RESET message to a device. 19878c2ecf20Sopenharmony_ci */ 19888c2ecf20Sopenharmony_cistatic void fas216_devicereset_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, 19898c2ecf20Sopenharmony_ci unsigned int result) 19908c2ecf20Sopenharmony_ci{ 19918c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "fas216 device reset complete"); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci info->rstSCpnt = NULL; 19948c2ecf20Sopenharmony_ci info->rst_dev_status = 1; 19958c2ecf20Sopenharmony_ci wake_up(&info->eh_wait); 19968c2ecf20Sopenharmony_ci} 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci/** 19998c2ecf20Sopenharmony_ci * fas216_rq_sns_done - Finish processing automatic request sense command 20008c2ecf20Sopenharmony_ci * @info: interface that completed 20018c2ecf20Sopenharmony_ci * @SCpnt: command that completed 20028c2ecf20Sopenharmony_ci * @result: driver byte of result 20038c2ecf20Sopenharmony_ci * 20048c2ecf20Sopenharmony_ci * Finish processing automatic request sense command 20058c2ecf20Sopenharmony_ci */ 20068c2ecf20Sopenharmony_cistatic void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, 20078c2ecf20Sopenharmony_ci unsigned int result) 20088c2ecf20Sopenharmony_ci{ 20098c2ecf20Sopenharmony_ci fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, 20108c2ecf20Sopenharmony_ci "request sense complete, result=0x%04x%02x%02x", 20118c2ecf20Sopenharmony_ci result, SCpnt->SCp.Message, SCpnt->SCp.Status); 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci if (result != DID_OK || SCpnt->SCp.Status != GOOD) 20148c2ecf20Sopenharmony_ci /* 20158c2ecf20Sopenharmony_ci * Something went wrong. Make sure that we don't 20168c2ecf20Sopenharmony_ci * have valid data in the sense buffer that could 20178c2ecf20Sopenharmony_ci * confuse the higher levels. 20188c2ecf20Sopenharmony_ci */ 20198c2ecf20Sopenharmony_ci memset(SCpnt->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); 20208c2ecf20Sopenharmony_ci//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id); 20218c2ecf20Sopenharmony_ci//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); } 20228c2ecf20Sopenharmony_ci /* 20238c2ecf20Sopenharmony_ci * Note that we don't set SCpnt->result, since that should 20248c2ecf20Sopenharmony_ci * reflect the status of the command that we were asked by 20258c2ecf20Sopenharmony_ci * the upper layers to process. This would have been set 20268c2ecf20Sopenharmony_ci * correctly by fas216_std_done. 20278c2ecf20Sopenharmony_ci */ 20288c2ecf20Sopenharmony_ci scsi_eh_restore_cmnd(SCpnt, &info->ses); 20298c2ecf20Sopenharmony_ci SCpnt->scsi_done(SCpnt); 20308c2ecf20Sopenharmony_ci} 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci/** 20338c2ecf20Sopenharmony_ci * fas216_std_done - finish processing of standard command 20348c2ecf20Sopenharmony_ci * @info: interface that completed 20358c2ecf20Sopenharmony_ci * @SCpnt: command that completed 20368c2ecf20Sopenharmony_ci * @result: driver byte of result 20378c2ecf20Sopenharmony_ci * 20388c2ecf20Sopenharmony_ci * Finish processing of standard command 20398c2ecf20Sopenharmony_ci */ 20408c2ecf20Sopenharmony_cistatic void 20418c2ecf20Sopenharmony_cifas216_std_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, unsigned int result) 20428c2ecf20Sopenharmony_ci{ 20438c2ecf20Sopenharmony_ci info->stats.fins += 1; 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | 20468c2ecf20Sopenharmony_ci info->scsi.SCp.Status; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci fas216_log_command(info, LOG_CONNECT, SCpnt, 20498c2ecf20Sopenharmony_ci "command complete, result=0x%08x", SCpnt->result); 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci /* 20528c2ecf20Sopenharmony_ci * If the driver detected an error, we're all done. 20538c2ecf20Sopenharmony_ci */ 20548c2ecf20Sopenharmony_ci if (host_byte(SCpnt->result) != DID_OK || 20558c2ecf20Sopenharmony_ci msg_byte(SCpnt->result) != COMMAND_COMPLETE) 20568c2ecf20Sopenharmony_ci goto done; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci /* 20598c2ecf20Sopenharmony_ci * If the command returned CHECK_CONDITION or COMMAND_TERMINATED 20608c2ecf20Sopenharmony_ci * status, request the sense information. 20618c2ecf20Sopenharmony_ci */ 20628c2ecf20Sopenharmony_ci if (status_byte(SCpnt->result) == CHECK_CONDITION || 20638c2ecf20Sopenharmony_ci status_byte(SCpnt->result) == COMMAND_TERMINATED) 20648c2ecf20Sopenharmony_ci goto request_sense; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci /* 20678c2ecf20Sopenharmony_ci * If the command did not complete with GOOD status, 20688c2ecf20Sopenharmony_ci * we are all done here. 20698c2ecf20Sopenharmony_ci */ 20708c2ecf20Sopenharmony_ci if (status_byte(SCpnt->result) != GOOD) 20718c2ecf20Sopenharmony_ci goto done; 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci /* 20748c2ecf20Sopenharmony_ci * We have successfully completed a command. Make sure that 20758c2ecf20Sopenharmony_ci * we do not have any buffers left to transfer. The world 20768c2ecf20Sopenharmony_ci * is not perfect, and we seem to occasionally hit this. 20778c2ecf20Sopenharmony_ci * It can be indicative of a buggy driver, target or the upper 20788c2ecf20Sopenharmony_ci * levels of the SCSI code. 20798c2ecf20Sopenharmony_ci */ 20808c2ecf20Sopenharmony_ci if (info->scsi.SCp.ptr) { 20818c2ecf20Sopenharmony_ci switch (SCpnt->cmnd[0]) { 20828c2ecf20Sopenharmony_ci case INQUIRY: 20838c2ecf20Sopenharmony_ci case START_STOP: 20848c2ecf20Sopenharmony_ci case MODE_SENSE: 20858c2ecf20Sopenharmony_ci break; 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci default: 20888c2ecf20Sopenharmony_ci scmd_printk(KERN_ERR, SCpnt, 20898c2ecf20Sopenharmony_ci "incomplete data transfer detected: res=%08X ptr=%p len=%X\n", 20908c2ecf20Sopenharmony_ci SCpnt->result, info->scsi.SCp.ptr, 20918c2ecf20Sopenharmony_ci info->scsi.SCp.this_residual); 20928c2ecf20Sopenharmony_ci scsi_print_command(SCpnt); 20938c2ecf20Sopenharmony_ci set_host_byte(SCpnt, DID_ERROR); 20948c2ecf20Sopenharmony_ci goto request_sense; 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_cidone: 20998c2ecf20Sopenharmony_ci if (SCpnt->scsi_done) { 21008c2ecf20Sopenharmony_ci SCpnt->scsi_done(SCpnt); 21018c2ecf20Sopenharmony_ci return; 21028c2ecf20Sopenharmony_ci } 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci panic("scsi%d.H: null scsi_done function in fas216_done", 21058c2ecf20Sopenharmony_ci info->host->host_no); 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_cirequest_sense: 21098c2ecf20Sopenharmony_ci if (SCpnt->cmnd[0] == REQUEST_SENSE) 21108c2ecf20Sopenharmony_ci goto done; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci scsi_eh_prep_cmnd(SCpnt, &info->ses, NULL, 0, ~0); 21138c2ecf20Sopenharmony_ci fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, 21148c2ecf20Sopenharmony_ci "requesting sense"); 21158c2ecf20Sopenharmony_ci init_SCp(SCpnt); 21168c2ecf20Sopenharmony_ci SCpnt->SCp.Message = 0; 21178c2ecf20Sopenharmony_ci SCpnt->SCp.Status = 0; 21188c2ecf20Sopenharmony_ci SCpnt->tag = 0; 21198c2ecf20Sopenharmony_ci SCpnt->host_scribble = (void *)fas216_rq_sns_done; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci /* 21228c2ecf20Sopenharmony_ci * Place this command into the high priority "request 21238c2ecf20Sopenharmony_ci * sense" slot. This will be the very next command 21248c2ecf20Sopenharmony_ci * executed, unless a target connects to us. 21258c2ecf20Sopenharmony_ci */ 21268c2ecf20Sopenharmony_ci if (info->reqSCpnt) 21278c2ecf20Sopenharmony_ci printk(KERN_WARNING "scsi%d.%c: losing request command\n", 21288c2ecf20Sopenharmony_ci info->host->host_no, '0' + SCpnt->device->id); 21298c2ecf20Sopenharmony_ci info->reqSCpnt = SCpnt; 21308c2ecf20Sopenharmony_ci} 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci/** 21338c2ecf20Sopenharmony_ci * fas216_done - complete processing for current command 21348c2ecf20Sopenharmony_ci * @info: interface that completed 21358c2ecf20Sopenharmony_ci * @result: driver byte of result 21368c2ecf20Sopenharmony_ci * 21378c2ecf20Sopenharmony_ci * Complete processing for current command 21388c2ecf20Sopenharmony_ci */ 21398c2ecf20Sopenharmony_cistatic void fas216_done(FAS216_Info *info, unsigned int result) 21408c2ecf20Sopenharmony_ci{ 21418c2ecf20Sopenharmony_ci void (*fn)(FAS216_Info *, struct scsi_cmnd *, unsigned int); 21428c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt; 21438c2ecf20Sopenharmony_ci unsigned long flags; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci fas216_checkmagic(info); 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci if (!info->SCpnt) 21488c2ecf20Sopenharmony_ci goto no_command; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci SCpnt = info->SCpnt; 21518c2ecf20Sopenharmony_ci info->SCpnt = NULL; 21528c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_IDLE; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci if (info->scsi.aborting) { 21558c2ecf20Sopenharmony_ci fas216_log(info, 0, "uncaught abort - returning DID_ABORT"); 21568c2ecf20Sopenharmony_ci result = DID_ABORT; 21578c2ecf20Sopenharmony_ci info->scsi.aborting = 0; 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci 21608c2ecf20Sopenharmony_ci /* 21618c2ecf20Sopenharmony_ci * Sanity check the completion - if we have zero bytes left 21628c2ecf20Sopenharmony_ci * to transfer, we should not have a valid pointer. 21638c2ecf20Sopenharmony_ci */ 21648c2ecf20Sopenharmony_ci if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { 21658c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, SCpnt, 21668c2ecf20Sopenharmony_ci "zero bytes left to transfer, but buffer pointer still valid: ptr=%p len=%08x\n", 21678c2ecf20Sopenharmony_ci info->scsi.SCp.ptr, info->scsi.SCp.this_residual); 21688c2ecf20Sopenharmony_ci info->scsi.SCp.ptr = NULL; 21698c2ecf20Sopenharmony_ci scsi_print_command(SCpnt); 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci /* 21738c2ecf20Sopenharmony_ci * Clear down this command as completed. If we need to request 21748c2ecf20Sopenharmony_ci * the sense information, fas216_kick will re-assert the busy 21758c2ecf20Sopenharmony_ci * status. 21768c2ecf20Sopenharmony_ci */ 21778c2ecf20Sopenharmony_ci info->device[SCpnt->device->id].parity_check = 0; 21788c2ecf20Sopenharmony_ci clear_bit(SCpnt->device->id * 8 + 21798c2ecf20Sopenharmony_ci (u8)(SCpnt->device->lun & 0x7), info->busyluns); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci fn = (void (*)(FAS216_Info *, struct scsi_cmnd *, unsigned int))SCpnt->host_scribble; 21828c2ecf20Sopenharmony_ci fn(info, SCpnt, result); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci if (info->scsi.irq) { 21858c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->host_lock, flags); 21868c2ecf20Sopenharmony_ci if (info->scsi.phase == PHASE_IDLE) 21878c2ecf20Sopenharmony_ci fas216_kick(info); 21888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->host_lock, flags); 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci return; 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_cino_command: 21938c2ecf20Sopenharmony_ci panic("scsi%d.H: null command in fas216_done", 21948c2ecf20Sopenharmony_ci info->host->host_no); 21958c2ecf20Sopenharmony_ci} 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci/** 21988c2ecf20Sopenharmony_ci * fas216_queue_command - queue a command for adapter to process. 21998c2ecf20Sopenharmony_ci * @SCpnt: Command to queue 22008c2ecf20Sopenharmony_ci * @done: done function to call once command is complete 22018c2ecf20Sopenharmony_ci * 22028c2ecf20Sopenharmony_ci * Queue a command for adapter to process. 22038c2ecf20Sopenharmony_ci * Returns: 0 on success, else error. 22048c2ecf20Sopenharmony_ci * Notes: io_request_lock is held, interrupts are disabled. 22058c2ecf20Sopenharmony_ci */ 22068c2ecf20Sopenharmony_cistatic int fas216_queue_command_lck(struct scsi_cmnd *SCpnt, 22078c2ecf20Sopenharmony_ci void (*done)(struct scsi_cmnd *)) 22088c2ecf20Sopenharmony_ci{ 22098c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; 22108c2ecf20Sopenharmony_ci int result; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci fas216_checkmagic(info); 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci fas216_log_command(info, LOG_CONNECT, SCpnt, 22158c2ecf20Sopenharmony_ci "received command (%p)", SCpnt); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci SCpnt->scsi_done = done; 22188c2ecf20Sopenharmony_ci SCpnt->host_scribble = (void *)fas216_std_done; 22198c2ecf20Sopenharmony_ci SCpnt->result = 0; 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci init_SCp(SCpnt); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci info->stats.queues += 1; 22248c2ecf20Sopenharmony_ci SCpnt->tag = 0; 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci spin_lock(&info->host_lock); 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci /* 22298c2ecf20Sopenharmony_ci * Add command into execute queue and let it complete under 22308c2ecf20Sopenharmony_ci * whatever scheme we're using. 22318c2ecf20Sopenharmony_ci */ 22328c2ecf20Sopenharmony_ci result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt); 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci /* 22358c2ecf20Sopenharmony_ci * If we successfully added the command, 22368c2ecf20Sopenharmony_ci * kick the interface to get it moving. 22378c2ecf20Sopenharmony_ci */ 22388c2ecf20Sopenharmony_ci if (result == 0 && info->scsi.phase == PHASE_IDLE) 22398c2ecf20Sopenharmony_ci fas216_kick(info); 22408c2ecf20Sopenharmony_ci spin_unlock(&info->host_lock); 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci fas216_log_target(info, LOG_CONNECT, -1, "queue %s", 22438c2ecf20Sopenharmony_ci result ? "failure" : "success"); 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci return result; 22468c2ecf20Sopenharmony_ci} 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ciDEF_SCSI_QCMD(fas216_queue_command) 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci/** 22518c2ecf20Sopenharmony_ci * fas216_internal_done - trigger restart of a waiting thread in fas216_noqueue_command 22528c2ecf20Sopenharmony_ci * @SCpnt: Command to wake 22538c2ecf20Sopenharmony_ci * 22548c2ecf20Sopenharmony_ci * Trigger restart of a waiting thread in fas216_command 22558c2ecf20Sopenharmony_ci */ 22568c2ecf20Sopenharmony_cistatic void fas216_internal_done(struct scsi_cmnd *SCpnt) 22578c2ecf20Sopenharmony_ci{ 22588c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci fas216_checkmagic(info); 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci info->internal_done = 1; 22638c2ecf20Sopenharmony_ci} 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci/** 22668c2ecf20Sopenharmony_ci * fas216_noqueue_command - process a command for the adapter. 22678c2ecf20Sopenharmony_ci * @SCpnt: Command to queue 22688c2ecf20Sopenharmony_ci * 22698c2ecf20Sopenharmony_ci * Queue a command for adapter to process. 22708c2ecf20Sopenharmony_ci * Returns: scsi result code. 22718c2ecf20Sopenharmony_ci * Notes: io_request_lock is held, interrupts are disabled. 22728c2ecf20Sopenharmony_ci */ 22738c2ecf20Sopenharmony_cistatic int fas216_noqueue_command_lck(struct scsi_cmnd *SCpnt, 22748c2ecf20Sopenharmony_ci void (*done)(struct scsi_cmnd *)) 22758c2ecf20Sopenharmony_ci{ 22768c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci fas216_checkmagic(info); 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci /* 22818c2ecf20Sopenharmony_ci * We should only be using this if we don't have an interrupt. 22828c2ecf20Sopenharmony_ci * Provide some "incentive" to use the queueing code. 22838c2ecf20Sopenharmony_ci */ 22848c2ecf20Sopenharmony_ci BUG_ON(info->scsi.irq); 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci info->internal_done = 0; 22878c2ecf20Sopenharmony_ci fas216_queue_command_lck(SCpnt, fas216_internal_done); 22888c2ecf20Sopenharmony_ci 22898c2ecf20Sopenharmony_ci /* 22908c2ecf20Sopenharmony_ci * This wastes time, since we can't return until the command is 22918c2ecf20Sopenharmony_ci * complete. We can't sleep either since we may get re-entered! 22928c2ecf20Sopenharmony_ci * However, we must re-enable interrupts, or else we'll be 22938c2ecf20Sopenharmony_ci * waiting forever. 22948c2ecf20Sopenharmony_ci */ 22958c2ecf20Sopenharmony_ci spin_unlock_irq(info->host->host_lock); 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci while (!info->internal_done) { 22988c2ecf20Sopenharmony_ci /* 22998c2ecf20Sopenharmony_ci * If we don't have an IRQ, then we must poll the card for 23008c2ecf20Sopenharmony_ci * it's interrupt, and use that to call this driver's 23018c2ecf20Sopenharmony_ci * interrupt routine. That way, we keep the command 23028c2ecf20Sopenharmony_ci * progressing. Maybe we can add some intelligence here 23038c2ecf20Sopenharmony_ci * and go to sleep if we know that the device is going 23048c2ecf20Sopenharmony_ci * to be some time (eg, disconnected). 23058c2ecf20Sopenharmony_ci */ 23068c2ecf20Sopenharmony_ci if (fas216_readb(info, REG_STAT) & STAT_INT) { 23078c2ecf20Sopenharmony_ci spin_lock_irq(info->host->host_lock); 23088c2ecf20Sopenharmony_ci fas216_intr(info); 23098c2ecf20Sopenharmony_ci spin_unlock_irq(info->host->host_lock); 23108c2ecf20Sopenharmony_ci } 23118c2ecf20Sopenharmony_ci } 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci spin_lock_irq(info->host->host_lock); 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci done(SCpnt); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci return 0; 23188c2ecf20Sopenharmony_ci} 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ciDEF_SCSI_QCMD(fas216_noqueue_command) 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci/* 23238c2ecf20Sopenharmony_ci * Error handler timeout function. Indicate that we timed out, 23248c2ecf20Sopenharmony_ci * and wake up any error handler process so it can continue. 23258c2ecf20Sopenharmony_ci */ 23268c2ecf20Sopenharmony_cistatic void fas216_eh_timer(struct timer_list *t) 23278c2ecf20Sopenharmony_ci{ 23288c2ecf20Sopenharmony_ci FAS216_Info *info = from_timer(info, t, eh_timer); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "error handling timed out\n"); 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci del_timer(&info->eh_timer); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci if (info->rst_bus_status == 0) 23358c2ecf20Sopenharmony_ci info->rst_bus_status = -1; 23368c2ecf20Sopenharmony_ci if (info->rst_dev_status == 0) 23378c2ecf20Sopenharmony_ci info->rst_dev_status = -1; 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci wake_up(&info->eh_wait); 23408c2ecf20Sopenharmony_ci} 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_cienum res_find { 23438c2ecf20Sopenharmony_ci res_failed, /* not found */ 23448c2ecf20Sopenharmony_ci res_success, /* command on issue queue */ 23458c2ecf20Sopenharmony_ci res_hw_abort /* command on disconnected dev */ 23468c2ecf20Sopenharmony_ci}; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci/** 23498c2ecf20Sopenharmony_ci * fas216_do_abort - decide how to abort a command 23508c2ecf20Sopenharmony_ci * @SCpnt: command to abort 23518c2ecf20Sopenharmony_ci * 23528c2ecf20Sopenharmony_ci * Decide how to abort a command. 23538c2ecf20Sopenharmony_ci * Returns: abort status 23548c2ecf20Sopenharmony_ci */ 23558c2ecf20Sopenharmony_cistatic enum res_find fas216_find_command(FAS216_Info *info, 23568c2ecf20Sopenharmony_ci struct scsi_cmnd *SCpnt) 23578c2ecf20Sopenharmony_ci{ 23588c2ecf20Sopenharmony_ci enum res_find res = res_failed; 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci if (queue_remove_cmd(&info->queues.issue, SCpnt)) { 23618c2ecf20Sopenharmony_ci /* 23628c2ecf20Sopenharmony_ci * The command was on the issue queue, and has not been 23638c2ecf20Sopenharmony_ci * issued yet. We can remove the command from the queue, 23648c2ecf20Sopenharmony_ci * and acknowledge the abort. Neither the device nor the 23658c2ecf20Sopenharmony_ci * interface know about the command. 23668c2ecf20Sopenharmony_ci */ 23678c2ecf20Sopenharmony_ci printk("on issue queue "); 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci res = res_success; 23708c2ecf20Sopenharmony_ci } else if (queue_remove_cmd(&info->queues.disconnected, SCpnt)) { 23718c2ecf20Sopenharmony_ci /* 23728c2ecf20Sopenharmony_ci * The command was on the disconnected queue. We must 23738c2ecf20Sopenharmony_ci * reconnect with the device if possible, and send it 23748c2ecf20Sopenharmony_ci * an abort message. 23758c2ecf20Sopenharmony_ci */ 23768c2ecf20Sopenharmony_ci printk("on disconnected queue "); 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci res = res_hw_abort; 23798c2ecf20Sopenharmony_ci } else if (info->SCpnt == SCpnt) { 23808c2ecf20Sopenharmony_ci printk("executing "); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci switch (info->scsi.phase) { 23838c2ecf20Sopenharmony_ci /* 23848c2ecf20Sopenharmony_ci * If the interface is idle, and the command is 'disconnectable', 23858c2ecf20Sopenharmony_ci * then it is the same as on the disconnected queue. 23868c2ecf20Sopenharmony_ci */ 23878c2ecf20Sopenharmony_ci case PHASE_IDLE: 23888c2ecf20Sopenharmony_ci if (info->scsi.disconnectable) { 23898c2ecf20Sopenharmony_ci info->scsi.disconnectable = 0; 23908c2ecf20Sopenharmony_ci info->SCpnt = NULL; 23918c2ecf20Sopenharmony_ci res = res_hw_abort; 23928c2ecf20Sopenharmony_ci } 23938c2ecf20Sopenharmony_ci break; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci default: 23968c2ecf20Sopenharmony_ci break; 23978c2ecf20Sopenharmony_ci } 23988c2ecf20Sopenharmony_ci } else if (info->origSCpnt == SCpnt) { 23998c2ecf20Sopenharmony_ci /* 24008c2ecf20Sopenharmony_ci * The command will be executed next, but a command 24018c2ecf20Sopenharmony_ci * is currently using the interface. This is similar to 24028c2ecf20Sopenharmony_ci * being on the issue queue, except the busylun bit has 24038c2ecf20Sopenharmony_ci * been set. 24048c2ecf20Sopenharmony_ci */ 24058c2ecf20Sopenharmony_ci info->origSCpnt = NULL; 24068c2ecf20Sopenharmony_ci clear_bit(SCpnt->device->id * 8 + 24078c2ecf20Sopenharmony_ci (u8)(SCpnt->device->lun & 0x7), info->busyluns); 24088c2ecf20Sopenharmony_ci printk("waiting for execution "); 24098c2ecf20Sopenharmony_ci res = res_success; 24108c2ecf20Sopenharmony_ci } else 24118c2ecf20Sopenharmony_ci printk("unknown "); 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci return res; 24148c2ecf20Sopenharmony_ci} 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci/** 24178c2ecf20Sopenharmony_ci * fas216_eh_abort - abort this command 24188c2ecf20Sopenharmony_ci * @SCpnt: command to abort 24198c2ecf20Sopenharmony_ci * 24208c2ecf20Sopenharmony_ci * Abort this command. 24218c2ecf20Sopenharmony_ci * Returns: FAILED if unable to abort 24228c2ecf20Sopenharmony_ci * Notes: io_request_lock is taken, and irqs are disabled 24238c2ecf20Sopenharmony_ci */ 24248c2ecf20Sopenharmony_ciint fas216_eh_abort(struct scsi_cmnd *SCpnt) 24258c2ecf20Sopenharmony_ci{ 24268c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; 24278c2ecf20Sopenharmony_ci int result = FAILED; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci fas216_checkmagic(info); 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_ci info->stats.aborts += 1; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci scmd_printk(KERN_WARNING, SCpnt, "abort command\n"); 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci print_debug_list(); 24368c2ecf20Sopenharmony_ci fas216_dumpstate(info); 24378c2ecf20Sopenharmony_ci 24388c2ecf20Sopenharmony_ci switch (fas216_find_command(info, SCpnt)) { 24398c2ecf20Sopenharmony_ci /* 24408c2ecf20Sopenharmony_ci * We found the command, and cleared it out. Either 24418c2ecf20Sopenharmony_ci * the command is still known to be executing on the 24428c2ecf20Sopenharmony_ci * target, or the busylun bit is not set. 24438c2ecf20Sopenharmony_ci */ 24448c2ecf20Sopenharmony_ci case res_success: 24458c2ecf20Sopenharmony_ci scmd_printk(KERN_WARNING, SCpnt, "abort %p success\n", SCpnt); 24468c2ecf20Sopenharmony_ci result = SUCCESS; 24478c2ecf20Sopenharmony_ci break; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci /* 24508c2ecf20Sopenharmony_ci * We need to reconnect to the target and send it an 24518c2ecf20Sopenharmony_ci * ABORT or ABORT_TAG message. We can only do this 24528c2ecf20Sopenharmony_ci * if the bus is free. 24538c2ecf20Sopenharmony_ci */ 24548c2ecf20Sopenharmony_ci case res_hw_abort: 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci /* 24578c2ecf20Sopenharmony_ci * We are unable to abort the command for some reason. 24588c2ecf20Sopenharmony_ci */ 24598c2ecf20Sopenharmony_ci default: 24608c2ecf20Sopenharmony_ci case res_failed: 24618c2ecf20Sopenharmony_ci scmd_printk(KERN_WARNING, SCpnt, "abort %p failed\n", SCpnt); 24628c2ecf20Sopenharmony_ci break; 24638c2ecf20Sopenharmony_ci } 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci return result; 24668c2ecf20Sopenharmony_ci} 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci/** 24698c2ecf20Sopenharmony_ci * fas216_eh_device_reset - Reset the device associated with this command 24708c2ecf20Sopenharmony_ci * @SCpnt: command specifing device to reset 24718c2ecf20Sopenharmony_ci * 24728c2ecf20Sopenharmony_ci * Reset the device associated with this command. 24738c2ecf20Sopenharmony_ci * Returns: FAILED if unable to reset. 24748c2ecf20Sopenharmony_ci * Notes: We won't be re-entered, so we'll only have one device 24758c2ecf20Sopenharmony_ci * reset on the go at one time. 24768c2ecf20Sopenharmony_ci */ 24778c2ecf20Sopenharmony_ciint fas216_eh_device_reset(struct scsi_cmnd *SCpnt) 24788c2ecf20Sopenharmony_ci{ 24798c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; 24808c2ecf20Sopenharmony_ci unsigned long flags; 24818c2ecf20Sopenharmony_ci int i, res = FAILED, target = SCpnt->device->id; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "device reset for target %d", target); 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->host_lock, flags); 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci do { 24888c2ecf20Sopenharmony_ci /* 24898c2ecf20Sopenharmony_ci * If we are currently connected to a device, and 24908c2ecf20Sopenharmony_ci * it is the device we want to reset, there is 24918c2ecf20Sopenharmony_ci * nothing we can do here. Chances are it is stuck, 24928c2ecf20Sopenharmony_ci * and we need a bus reset. 24938c2ecf20Sopenharmony_ci */ 24948c2ecf20Sopenharmony_ci if (info->SCpnt && !info->scsi.disconnectable && 24958c2ecf20Sopenharmony_ci info->SCpnt->device->id == SCpnt->device->id) 24968c2ecf20Sopenharmony_ci break; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci /* 24998c2ecf20Sopenharmony_ci * We're going to be resetting this device. Remove 25008c2ecf20Sopenharmony_ci * all pending commands from the driver. By doing 25018c2ecf20Sopenharmony_ci * so, we guarantee that we won't touch the command 25028c2ecf20Sopenharmony_ci * structures except to process the reset request. 25038c2ecf20Sopenharmony_ci */ 25048c2ecf20Sopenharmony_ci queue_remove_all_target(&info->queues.issue, target); 25058c2ecf20Sopenharmony_ci queue_remove_all_target(&info->queues.disconnected, target); 25068c2ecf20Sopenharmony_ci if (info->origSCpnt && info->origSCpnt->device->id == target) 25078c2ecf20Sopenharmony_ci info->origSCpnt = NULL; 25088c2ecf20Sopenharmony_ci if (info->reqSCpnt && info->reqSCpnt->device->id == target) 25098c2ecf20Sopenharmony_ci info->reqSCpnt = NULL; 25108c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 25118c2ecf20Sopenharmony_ci clear_bit(target * 8 + i, info->busyluns); 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci /* 25148c2ecf20Sopenharmony_ci * Hijack this SCSI command structure to send 25158c2ecf20Sopenharmony_ci * a bus device reset message to this device. 25168c2ecf20Sopenharmony_ci */ 25178c2ecf20Sopenharmony_ci SCpnt->host_scribble = (void *)fas216_devicereset_done; 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci info->rst_dev_status = 0; 25208c2ecf20Sopenharmony_ci info->rstSCpnt = SCpnt; 25218c2ecf20Sopenharmony_ci 25228c2ecf20Sopenharmony_ci if (info->scsi.phase == PHASE_IDLE) 25238c2ecf20Sopenharmony_ci fas216_kick(info); 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci mod_timer(&info->eh_timer, jiffies + 30 * HZ); 25268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->host_lock, flags); 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci /* 25298c2ecf20Sopenharmony_ci * Wait up to 30 seconds for the reset to complete. 25308c2ecf20Sopenharmony_ci */ 25318c2ecf20Sopenharmony_ci wait_event(info->eh_wait, info->rst_dev_status); 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_ci del_timer_sync(&info->eh_timer); 25348c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->host_lock, flags); 25358c2ecf20Sopenharmony_ci info->rstSCpnt = NULL; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci if (info->rst_dev_status == 1) 25388c2ecf20Sopenharmony_ci res = SUCCESS; 25398c2ecf20Sopenharmony_ci } while (0); 25408c2ecf20Sopenharmony_ci 25418c2ecf20Sopenharmony_ci SCpnt->host_scribble = NULL; 25428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->host_lock, flags); 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "device reset complete: %s\n", 25458c2ecf20Sopenharmony_ci res == SUCCESS ? "success" : "failed"); 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci return res; 25488c2ecf20Sopenharmony_ci} 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci/** 25518c2ecf20Sopenharmony_ci * fas216_eh_bus_reset - Reset the bus associated with the command 25528c2ecf20Sopenharmony_ci * @SCpnt: command specifing bus to reset 25538c2ecf20Sopenharmony_ci * 25548c2ecf20Sopenharmony_ci * Reset the bus associated with the command. 25558c2ecf20Sopenharmony_ci * Returns: FAILED if unable to reset. 25568c2ecf20Sopenharmony_ci * Notes: Further commands are blocked. 25578c2ecf20Sopenharmony_ci */ 25588c2ecf20Sopenharmony_ciint fas216_eh_bus_reset(struct scsi_cmnd *SCpnt) 25598c2ecf20Sopenharmony_ci{ 25608c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; 25618c2ecf20Sopenharmony_ci unsigned long flags; 25628c2ecf20Sopenharmony_ci struct scsi_device *SDpnt; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci fas216_checkmagic(info); 25658c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "resetting bus"); 25668c2ecf20Sopenharmony_ci 25678c2ecf20Sopenharmony_ci info->stats.bus_resets += 1; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci spin_lock_irqsave(&info->host_lock, flags); 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci /* 25728c2ecf20Sopenharmony_ci * Stop all activity on this interface. 25738c2ecf20Sopenharmony_ci */ 25748c2ecf20Sopenharmony_ci fas216_aborttransfer(info); 25758c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci /* 25788c2ecf20Sopenharmony_ci * Clear any pending interrupts. 25798c2ecf20Sopenharmony_ci */ 25808c2ecf20Sopenharmony_ci while (fas216_readb(info, REG_STAT) & STAT_INT) 25818c2ecf20Sopenharmony_ci fas216_readb(info, REG_INST); 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci info->rst_bus_status = 0; 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci /* 25868c2ecf20Sopenharmony_ci * For each attached hard-reset device, clear out 25878c2ecf20Sopenharmony_ci * all command structures. Leave the running 25888c2ecf20Sopenharmony_ci * command in place. 25898c2ecf20Sopenharmony_ci */ 25908c2ecf20Sopenharmony_ci shost_for_each_device(SDpnt, info->host) { 25918c2ecf20Sopenharmony_ci int i; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci if (SDpnt->soft_reset) 25948c2ecf20Sopenharmony_ci continue; 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci queue_remove_all_target(&info->queues.issue, SDpnt->id); 25978c2ecf20Sopenharmony_ci queue_remove_all_target(&info->queues.disconnected, SDpnt->id); 25988c2ecf20Sopenharmony_ci if (info->origSCpnt && info->origSCpnt->device->id == SDpnt->id) 25998c2ecf20Sopenharmony_ci info->origSCpnt = NULL; 26008c2ecf20Sopenharmony_ci if (info->reqSCpnt && info->reqSCpnt->device->id == SDpnt->id) 26018c2ecf20Sopenharmony_ci info->reqSCpnt = NULL; 26028c2ecf20Sopenharmony_ci info->SCpnt = NULL; 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) 26058c2ecf20Sopenharmony_ci clear_bit(SDpnt->id * 8 + i, info->busyluns); 26068c2ecf20Sopenharmony_ci } 26078c2ecf20Sopenharmony_ci 26088c2ecf20Sopenharmony_ci info->scsi.phase = PHASE_IDLE; 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci /* 26118c2ecf20Sopenharmony_ci * Reset the SCSI bus. Device cleanup happens in 26128c2ecf20Sopenharmony_ci * the interrupt handler. 26138c2ecf20Sopenharmony_ci */ 26148c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_RESETSCSI); 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci mod_timer(&info->eh_timer, jiffies + HZ); 26178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&info->host_lock, flags); 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci /* 26208c2ecf20Sopenharmony_ci * Wait one second for the interrupt. 26218c2ecf20Sopenharmony_ci */ 26228c2ecf20Sopenharmony_ci wait_event(info->eh_wait, info->rst_bus_status); 26238c2ecf20Sopenharmony_ci del_timer_sync(&info->eh_timer); 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "bus reset complete: %s\n", 26268c2ecf20Sopenharmony_ci info->rst_bus_status == 1 ? "success" : "failed"); 26278c2ecf20Sopenharmony_ci 26288c2ecf20Sopenharmony_ci return info->rst_bus_status == 1 ? SUCCESS : FAILED; 26298c2ecf20Sopenharmony_ci} 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci/** 26328c2ecf20Sopenharmony_ci * fas216_init_chip - Initialise FAS216 state after reset 26338c2ecf20Sopenharmony_ci * @info: state structure for interface 26348c2ecf20Sopenharmony_ci * 26358c2ecf20Sopenharmony_ci * Initialise FAS216 state after reset 26368c2ecf20Sopenharmony_ci */ 26378c2ecf20Sopenharmony_cistatic void fas216_init_chip(FAS216_Info *info) 26388c2ecf20Sopenharmony_ci{ 26398c2ecf20Sopenharmony_ci unsigned int clock = ((info->ifcfg.clockrate - 1) / 5 + 1) & 7; 26408c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CLKF, clock); 26418c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); 26428c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]); 26438c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); 26448c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); 26458c2ecf20Sopenharmony_ci fas216_writeb(info, REG_SOF, 0); 26468c2ecf20Sopenharmony_ci fas216_writeb(info, REG_STP, info->scsi.async_stp); 26478c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); 26488c2ecf20Sopenharmony_ci} 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci/** 26518c2ecf20Sopenharmony_ci * fas216_eh_host_reset - Reset the host associated with this command 26528c2ecf20Sopenharmony_ci * @SCpnt: command specifing host to reset 26538c2ecf20Sopenharmony_ci * 26548c2ecf20Sopenharmony_ci * Reset the host associated with this command. 26558c2ecf20Sopenharmony_ci * Returns: FAILED if unable to reset. 26568c2ecf20Sopenharmony_ci * Notes: io_request_lock is taken, and irqs are disabled 26578c2ecf20Sopenharmony_ci */ 26588c2ecf20Sopenharmony_ciint fas216_eh_host_reset(struct scsi_cmnd *SCpnt) 26598c2ecf20Sopenharmony_ci{ 26608c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci spin_lock_irq(info->host->host_lock); 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci fas216_checkmagic(info); 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci fas216_log(info, LOG_ERROR, "resetting host"); 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci /* 26698c2ecf20Sopenharmony_ci * Reset the SCSI chip. 26708c2ecf20Sopenharmony_ci */ 26718c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_RESETCHIP); 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci /* 26748c2ecf20Sopenharmony_ci * Ugly ugly ugly! 26758c2ecf20Sopenharmony_ci * We need to release the host_lock and enable 26768c2ecf20Sopenharmony_ci * IRQs if we sleep, but we must relock and disable 26778c2ecf20Sopenharmony_ci * IRQs after the sleep. 26788c2ecf20Sopenharmony_ci */ 26798c2ecf20Sopenharmony_ci spin_unlock_irq(info->host->host_lock); 26808c2ecf20Sopenharmony_ci msleep(50 * 1000/100); 26818c2ecf20Sopenharmony_ci spin_lock_irq(info->host->host_lock); 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci /* 26848c2ecf20Sopenharmony_ci * Release the SCSI reset. 26858c2ecf20Sopenharmony_ci */ 26868c2ecf20Sopenharmony_ci fas216_cmd(info, CMD_NOP); 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci fas216_init_chip(info); 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci spin_unlock_irq(info->host->host_lock); 26918c2ecf20Sopenharmony_ci return SUCCESS; 26928c2ecf20Sopenharmony_ci} 26938c2ecf20Sopenharmony_ci 26948c2ecf20Sopenharmony_ci#define TYPE_UNKNOWN 0 26958c2ecf20Sopenharmony_ci#define TYPE_NCR53C90 1 26968c2ecf20Sopenharmony_ci#define TYPE_NCR53C90A 2 26978c2ecf20Sopenharmony_ci#define TYPE_NCR53C9x 3 26988c2ecf20Sopenharmony_ci#define TYPE_Am53CF94 4 26998c2ecf20Sopenharmony_ci#define TYPE_EmFAS216 5 27008c2ecf20Sopenharmony_ci#define TYPE_QLFAS216 6 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_cistatic char *chip_types[] = { 27038c2ecf20Sopenharmony_ci "unknown", 27048c2ecf20Sopenharmony_ci "NS NCR53C90", 27058c2ecf20Sopenharmony_ci "NS NCR53C90A", 27068c2ecf20Sopenharmony_ci "NS NCR53C9x", 27078c2ecf20Sopenharmony_ci "AMD Am53CF94", 27088c2ecf20Sopenharmony_ci "Emulex FAS216", 27098c2ecf20Sopenharmony_ci "QLogic FAS216" 27108c2ecf20Sopenharmony_ci}; 27118c2ecf20Sopenharmony_ci 27128c2ecf20Sopenharmony_cistatic int fas216_detect_type(FAS216_Info *info) 27138c2ecf20Sopenharmony_ci{ 27148c2ecf20Sopenharmony_ci int family, rev; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci /* 27178c2ecf20Sopenharmony_ci * Reset the chip. 27188c2ecf20Sopenharmony_ci */ 27198c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_RESETCHIP); 27208c2ecf20Sopenharmony_ci udelay(50); 27218c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_NOP); 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci /* 27248c2ecf20Sopenharmony_ci * Check to see if control reg 2 is present. 27258c2ecf20Sopenharmony_ci */ 27268c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, 0); 27278c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL2, CNTL2_S2FE); 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci /* 27308c2ecf20Sopenharmony_ci * If we are unable to read back control reg 2 27318c2ecf20Sopenharmony_ci * correctly, it is not present, and we have a 27328c2ecf20Sopenharmony_ci * NCR53C90. 27338c2ecf20Sopenharmony_ci */ 27348c2ecf20Sopenharmony_ci if ((fas216_readb(info, REG_CNTL2) & (~0xe0)) != CNTL2_S2FE) 27358c2ecf20Sopenharmony_ci return TYPE_NCR53C90; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci /* 27388c2ecf20Sopenharmony_ci * Now, check control register 3 27398c2ecf20Sopenharmony_ci */ 27408c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL2, 0); 27418c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, 0); 27428c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, 5); 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci /* 27458c2ecf20Sopenharmony_ci * If we are unable to read the register back 27468c2ecf20Sopenharmony_ci * correctly, we have a NCR53C90A 27478c2ecf20Sopenharmony_ci */ 27488c2ecf20Sopenharmony_ci if (fas216_readb(info, REG_CNTL3) != 5) 27498c2ecf20Sopenharmony_ci return TYPE_NCR53C90A; 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci /* 27528c2ecf20Sopenharmony_ci * Now read the ID from the chip. 27538c2ecf20Sopenharmony_ci */ 27548c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, 0); 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, CNTL3_ADIDCHK); 27578c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL3, 0); 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_RESETCHIP); 27608c2ecf20Sopenharmony_ci udelay(50); 27618c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_WITHDMA | CMD_NOP); 27628c2ecf20Sopenharmony_ci 27638c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL2, CNTL2_ENF); 27648c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_RESETCHIP); 27658c2ecf20Sopenharmony_ci udelay(50); 27668c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_NOP); 27678c2ecf20Sopenharmony_ci 27688c2ecf20Sopenharmony_ci rev = fas216_readb(info, REG_ID); 27698c2ecf20Sopenharmony_ci family = rev >> 3; 27708c2ecf20Sopenharmony_ci rev &= 7; 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci switch (family) { 27738c2ecf20Sopenharmony_ci case 0x01: 27748c2ecf20Sopenharmony_ci if (rev == 4) 27758c2ecf20Sopenharmony_ci return TYPE_Am53CF94; 27768c2ecf20Sopenharmony_ci break; 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci case 0x02: 27798c2ecf20Sopenharmony_ci switch (rev) { 27808c2ecf20Sopenharmony_ci case 2: 27818c2ecf20Sopenharmony_ci return TYPE_EmFAS216; 27828c2ecf20Sopenharmony_ci case 3: 27838c2ecf20Sopenharmony_ci return TYPE_QLFAS216; 27848c2ecf20Sopenharmony_ci } 27858c2ecf20Sopenharmony_ci break; 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci default: 27888c2ecf20Sopenharmony_ci break; 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci printk("family %x rev %x\n", family, rev); 27918c2ecf20Sopenharmony_ci return TYPE_NCR53C9x; 27928c2ecf20Sopenharmony_ci} 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci/** 27958c2ecf20Sopenharmony_ci * fas216_reset_state - Initialise driver internal state 27968c2ecf20Sopenharmony_ci * @info: state to initialise 27978c2ecf20Sopenharmony_ci * 27988c2ecf20Sopenharmony_ci * Initialise driver internal state 27998c2ecf20Sopenharmony_ci */ 28008c2ecf20Sopenharmony_cistatic void fas216_reset_state(FAS216_Info *info) 28018c2ecf20Sopenharmony_ci{ 28028c2ecf20Sopenharmony_ci int i; 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci fas216_checkmagic(info); 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci fas216_bus_reset(info); 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci /* 28098c2ecf20Sopenharmony_ci * Clear out all stale info in our state structure 28108c2ecf20Sopenharmony_ci */ 28118c2ecf20Sopenharmony_ci memset(info->busyluns, 0, sizeof(info->busyluns)); 28128c2ecf20Sopenharmony_ci info->scsi.disconnectable = 0; 28138c2ecf20Sopenharmony_ci info->scsi.aborting = 0; 28148c2ecf20Sopenharmony_ci 28158c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 28168c2ecf20Sopenharmony_ci info->device[i].parity_enabled = 0; 28178c2ecf20Sopenharmony_ci info->device[i].parity_check = 1; 28188c2ecf20Sopenharmony_ci } 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci /* 28218c2ecf20Sopenharmony_ci * Drain all commands on disconnected queue 28228c2ecf20Sopenharmony_ci */ 28238c2ecf20Sopenharmony_ci while (queue_remove(&info->queues.disconnected) != NULL); 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci /* 28268c2ecf20Sopenharmony_ci * Remove executing commands. 28278c2ecf20Sopenharmony_ci */ 28288c2ecf20Sopenharmony_ci info->SCpnt = NULL; 28298c2ecf20Sopenharmony_ci info->reqSCpnt = NULL; 28308c2ecf20Sopenharmony_ci info->rstSCpnt = NULL; 28318c2ecf20Sopenharmony_ci info->origSCpnt = NULL; 28328c2ecf20Sopenharmony_ci} 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci/** 28358c2ecf20Sopenharmony_ci * fas216_init - initialise FAS/NCR/AMD SCSI structures. 28368c2ecf20Sopenharmony_ci * @host: a driver-specific filled-out structure 28378c2ecf20Sopenharmony_ci * 28388c2ecf20Sopenharmony_ci * Initialise FAS/NCR/AMD SCSI structures. 28398c2ecf20Sopenharmony_ci * Returns: 0 on success 28408c2ecf20Sopenharmony_ci */ 28418c2ecf20Sopenharmony_ciint fas216_init(struct Scsi_Host *host) 28428c2ecf20Sopenharmony_ci{ 28438c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)host->hostdata; 28448c2ecf20Sopenharmony_ci 28458c2ecf20Sopenharmony_ci info->magic_start = MAGIC; 28468c2ecf20Sopenharmony_ci info->magic_end = MAGIC; 28478c2ecf20Sopenharmony_ci info->host = host; 28488c2ecf20Sopenharmony_ci info->scsi.cfg[0] = host->this_id | CNTL1_PERE; 28498c2ecf20Sopenharmony_ci info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE; 28508c2ecf20Sopenharmony_ci info->scsi.cfg[2] = info->ifcfg.cntl3 | 28518c2ecf20Sopenharmony_ci CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM; 28528c2ecf20Sopenharmony_ci info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci info->rst_dev_status = -1; 28558c2ecf20Sopenharmony_ci info->rst_bus_status = -1; 28568c2ecf20Sopenharmony_ci init_waitqueue_head(&info->eh_wait); 28578c2ecf20Sopenharmony_ci timer_setup(&info->eh_timer, fas216_eh_timer, 0); 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci spin_lock_init(&info->host_lock); 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci memset(&info->stats, 0, sizeof(info->stats)); 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci msgqueue_initialise(&info->scsi.msgs); 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci if (!queue_initialise(&info->queues.issue)) 28668c2ecf20Sopenharmony_ci return -ENOMEM; 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci if (!queue_initialise(&info->queues.disconnected)) { 28698c2ecf20Sopenharmony_ci queue_free(&info->queues.issue); 28708c2ecf20Sopenharmony_ci return -ENOMEM; 28718c2ecf20Sopenharmony_ci } 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci return 0; 28748c2ecf20Sopenharmony_ci} 28758c2ecf20Sopenharmony_ci 28768c2ecf20Sopenharmony_ci/** 28778c2ecf20Sopenharmony_ci * fas216_add - initialise FAS/NCR/AMD SCSI ic. 28788c2ecf20Sopenharmony_ci * @host: a driver-specific filled-out structure 28798c2ecf20Sopenharmony_ci * @dev: parent device 28808c2ecf20Sopenharmony_ci * 28818c2ecf20Sopenharmony_ci * Initialise FAS/NCR/AMD SCSI ic. 28828c2ecf20Sopenharmony_ci * Returns: 0 on success 28838c2ecf20Sopenharmony_ci */ 28848c2ecf20Sopenharmony_ciint fas216_add(struct Scsi_Host *host, struct device *dev) 28858c2ecf20Sopenharmony_ci{ 28868c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)host->hostdata; 28878c2ecf20Sopenharmony_ci int type, ret; 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci if (info->ifcfg.clockrate <= 10 || info->ifcfg.clockrate > 40) { 28908c2ecf20Sopenharmony_ci printk(KERN_CRIT "fas216: invalid clock rate %u MHz\n", 28918c2ecf20Sopenharmony_ci info->ifcfg.clockrate); 28928c2ecf20Sopenharmony_ci return -EINVAL; 28938c2ecf20Sopenharmony_ci } 28948c2ecf20Sopenharmony_ci 28958c2ecf20Sopenharmony_ci fas216_reset_state(info); 28968c2ecf20Sopenharmony_ci type = fas216_detect_type(info); 28978c2ecf20Sopenharmony_ci info->scsi.type = chip_types[type]; 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci udelay(300); 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci /* 29028c2ecf20Sopenharmony_ci * Initialise the chip correctly. 29038c2ecf20Sopenharmony_ci */ 29048c2ecf20Sopenharmony_ci fas216_init_chip(info); 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ci /* 29078c2ecf20Sopenharmony_ci * Reset the SCSI bus. We don't want to see 29088c2ecf20Sopenharmony_ci * the resulting reset interrupt, so mask it 29098c2ecf20Sopenharmony_ci * out. 29108c2ecf20Sopenharmony_ci */ 29118c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_DISR); 29128c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_RESETSCSI); 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci /* 29158c2ecf20Sopenharmony_ci * scsi standard says wait 250ms 29168c2ecf20Sopenharmony_ci */ 29178c2ecf20Sopenharmony_ci spin_unlock_irq(info->host->host_lock); 29188c2ecf20Sopenharmony_ci msleep(100*1000/100); 29198c2ecf20Sopenharmony_ci spin_lock_irq(info->host->host_lock); 29208c2ecf20Sopenharmony_ci 29218c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); 29228c2ecf20Sopenharmony_ci fas216_readb(info, REG_INST); 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci fas216_checkmagic(info); 29258c2ecf20Sopenharmony_ci 29268c2ecf20Sopenharmony_ci ret = scsi_add_host(host, dev); 29278c2ecf20Sopenharmony_ci if (ret) 29288c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_RESETCHIP); 29298c2ecf20Sopenharmony_ci else 29308c2ecf20Sopenharmony_ci scsi_scan_host(host); 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci return ret; 29338c2ecf20Sopenharmony_ci} 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_civoid fas216_remove(struct Scsi_Host *host) 29368c2ecf20Sopenharmony_ci{ 29378c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)host->hostdata; 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci fas216_checkmagic(info); 29408c2ecf20Sopenharmony_ci scsi_remove_host(host); 29418c2ecf20Sopenharmony_ci 29428c2ecf20Sopenharmony_ci fas216_writeb(info, REG_CMD, CMD_RESETCHIP); 29438c2ecf20Sopenharmony_ci scsi_host_put(host); 29448c2ecf20Sopenharmony_ci} 29458c2ecf20Sopenharmony_ci 29468c2ecf20Sopenharmony_ci/** 29478c2ecf20Sopenharmony_ci * fas216_release - release all resources for FAS/NCR/AMD SCSI ic. 29488c2ecf20Sopenharmony_ci * @host: a driver-specific filled-out structure 29498c2ecf20Sopenharmony_ci * 29508c2ecf20Sopenharmony_ci * release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. 29518c2ecf20Sopenharmony_ci */ 29528c2ecf20Sopenharmony_civoid fas216_release(struct Scsi_Host *host) 29538c2ecf20Sopenharmony_ci{ 29548c2ecf20Sopenharmony_ci FAS216_Info *info = (FAS216_Info *)host->hostdata; 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci queue_free(&info->queues.disconnected); 29578c2ecf20Sopenharmony_ci queue_free(&info->queues.issue); 29588c2ecf20Sopenharmony_ci} 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_civoid fas216_print_host(FAS216_Info *info, struct seq_file *m) 29618c2ecf20Sopenharmony_ci{ 29628c2ecf20Sopenharmony_ci seq_printf(m, 29638c2ecf20Sopenharmony_ci "\n" 29648c2ecf20Sopenharmony_ci "Chip : %s\n" 29658c2ecf20Sopenharmony_ci " Address: 0x%p\n" 29668c2ecf20Sopenharmony_ci " IRQ : %d\n" 29678c2ecf20Sopenharmony_ci " DMA : %d\n", 29688c2ecf20Sopenharmony_ci info->scsi.type, info->scsi.io_base, 29698c2ecf20Sopenharmony_ci info->scsi.irq, info->scsi.dma); 29708c2ecf20Sopenharmony_ci} 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_civoid fas216_print_stats(FAS216_Info *info, struct seq_file *m) 29738c2ecf20Sopenharmony_ci{ 29748c2ecf20Sopenharmony_ci seq_printf(m, "\n" 29758c2ecf20Sopenharmony_ci "Command Statistics:\n" 29768c2ecf20Sopenharmony_ci " Queued : %u\n" 29778c2ecf20Sopenharmony_ci " Issued : %u\n" 29788c2ecf20Sopenharmony_ci " Completed : %u\n" 29798c2ecf20Sopenharmony_ci " Reads : %u\n" 29808c2ecf20Sopenharmony_ci " Writes : %u\n" 29818c2ecf20Sopenharmony_ci " Others : %u\n" 29828c2ecf20Sopenharmony_ci " Disconnects: %u\n" 29838c2ecf20Sopenharmony_ci " Aborts : %u\n" 29848c2ecf20Sopenharmony_ci " Bus resets : %u\n" 29858c2ecf20Sopenharmony_ci " Host resets: %u\n", 29868c2ecf20Sopenharmony_ci info->stats.queues, info->stats.removes, 29878c2ecf20Sopenharmony_ci info->stats.fins, info->stats.reads, 29888c2ecf20Sopenharmony_ci info->stats.writes, info->stats.miscs, 29898c2ecf20Sopenharmony_ci info->stats.disconnects, info->stats.aborts, 29908c2ecf20Sopenharmony_ci info->stats.bus_resets, info->stats.host_resets); 29918c2ecf20Sopenharmony_ci} 29928c2ecf20Sopenharmony_ci 29938c2ecf20Sopenharmony_civoid fas216_print_devices(FAS216_Info *info, struct seq_file *m) 29948c2ecf20Sopenharmony_ci{ 29958c2ecf20Sopenharmony_ci struct fas216_device *dev; 29968c2ecf20Sopenharmony_ci struct scsi_device *scd; 29978c2ecf20Sopenharmony_ci 29988c2ecf20Sopenharmony_ci seq_puts(m, "Device/Lun TaggedQ Parity Sync\n"); 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ci shost_for_each_device(scd, info->host) { 30018c2ecf20Sopenharmony_ci dev = &info->device[scd->id]; 30028c2ecf20Sopenharmony_ci seq_printf(m, " %d/%llu ", scd->id, scd->lun); 30038c2ecf20Sopenharmony_ci if (scd->tagged_supported) 30048c2ecf20Sopenharmony_ci seq_printf(m, "%3sabled(%3d) ", 30058c2ecf20Sopenharmony_ci scd->simple_tags ? "en" : "dis", 30068c2ecf20Sopenharmony_ci scd->current_tag); 30078c2ecf20Sopenharmony_ci else 30088c2ecf20Sopenharmony_ci seq_puts(m, "unsupported "); 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci seq_printf(m, "%3sabled ", dev->parity_enabled ? "en" : "dis"); 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci if (dev->sof) 30138c2ecf20Sopenharmony_ci seq_printf(m, "offset %d, %d ns\n", 30148c2ecf20Sopenharmony_ci dev->sof, dev->period * 4); 30158c2ecf20Sopenharmony_ci else 30168c2ecf20Sopenharmony_ci seq_puts(m, "async\n"); 30178c2ecf20Sopenharmony_ci } 30188c2ecf20Sopenharmony_ci} 30198c2ecf20Sopenharmony_ci 30208c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_init); 30218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_add); 30228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_queue_command); 30238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_noqueue_command); 30248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_intr); 30258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_remove); 30268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_release); 30278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_eh_abort); 30288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_eh_device_reset); 30298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_eh_bus_reset); 30308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_eh_host_reset); 30318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_print_host); 30328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_print_stats); 30338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fas216_print_devices); 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King"); 30368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core"); 30378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3038