18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Device driver for the SYMBIOS/LSILOGIC 53C8XX and 53C1010 family 48c2ecf20Sopenharmony_ci * of PCI-SCSI IO processors. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr> 78c2ecf20Sopenharmony_ci * Copyright (c) 2003-2005 Matthew Wilcox <matthew@wil.cx> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This driver is derived from the Linux sym53c8xx driver. 108c2ecf20Sopenharmony_ci * Copyright (C) 1998-2000 Gerard Roudier 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The sym53c8xx driver is derived from the ncr53c8xx driver that had been 138c2ecf20Sopenharmony_ci * a port of the FreeBSD ncr driver to Linux-1.2.13. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * The original ncr driver has been written for 386bsd and FreeBSD by 168c2ecf20Sopenharmony_ci * Wolfgang Stanglmeier <wolf@cologne.de> 178c2ecf20Sopenharmony_ci * Stefan Esser <se@mi.Uni-Koeln.de> 188c2ecf20Sopenharmony_ci * Copyright (C) 1994 Wolfgang Stanglmeier 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Other major contributions: 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * NVRAM detection and reading. 238c2ecf20Sopenharmony_ci * Copyright (C) 1997 Richard Waltham <dormouse@farsrobt.demon.co.uk> 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci *----------------------------------------------------------------------------- 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci#include <linux/ctype.h> 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/module.h> 308c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 318c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 328c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 338c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 348c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 358c2ecf20Sopenharmony_ci#include <scsi/scsi_transport.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#include "sym_glue.h" 388c2ecf20Sopenharmony_ci#include "sym_nvram.h" 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define NAME53C "sym53c" 418c2ecf20Sopenharmony_ci#define NAME53C8XX "sym53c8xx" 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistruct sym_driver_setup sym_driver_setup = SYM_LINUX_DRIVER_SETUP; 448c2ecf20Sopenharmony_ciunsigned int sym_debug_flags = 0; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic char *excl_string; 478c2ecf20Sopenharmony_cistatic char *safe_string; 488c2ecf20Sopenharmony_cimodule_param_named(cmd_per_lun, sym_driver_setup.max_tag, ushort, 0); 498c2ecf20Sopenharmony_cimodule_param_named(burst, sym_driver_setup.burst_order, byte, 0); 508c2ecf20Sopenharmony_cimodule_param_named(led, sym_driver_setup.scsi_led, byte, 0); 518c2ecf20Sopenharmony_cimodule_param_named(diff, sym_driver_setup.scsi_diff, byte, 0); 528c2ecf20Sopenharmony_cimodule_param_named(irqm, sym_driver_setup.irq_mode, byte, 0); 538c2ecf20Sopenharmony_cimodule_param_named(buschk, sym_driver_setup.scsi_bus_check, byte, 0); 548c2ecf20Sopenharmony_cimodule_param_named(hostid, sym_driver_setup.host_id, byte, 0); 558c2ecf20Sopenharmony_cimodule_param_named(verb, sym_driver_setup.verbose, byte, 0); 568c2ecf20Sopenharmony_cimodule_param_named(debug, sym_debug_flags, uint, 0); 578c2ecf20Sopenharmony_cimodule_param_named(settle, sym_driver_setup.settle_delay, byte, 0); 588c2ecf20Sopenharmony_cimodule_param_named(nvram, sym_driver_setup.use_nvram, byte, 0); 598c2ecf20Sopenharmony_cimodule_param_named(excl, excl_string, charp, 0); 608c2ecf20Sopenharmony_cimodule_param_named(safe, safe_string, charp, 0); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cmd_per_lun, "The maximum number of tags to use by default"); 638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(burst, "Maximum burst. 0 to disable, 255 to read from registers"); 648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(led, "Set to 1 to enable LED support"); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(diff, "0 for no differential mode, 1 for BIOS, 2 for always, 3 for not GPIO3"); 668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irqm, "0 for open drain, 1 to leave alone, 2 for totem pole"); 678c2ecf20Sopenharmony_ciMODULE_PARM_DESC(buschk, "0 to not check, 1 for detach on error, 2 for warn on error"); 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hostid, "The SCSI ID to use for the host adapters"); 698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(verb, "0 for minimal verbosity, 1 for normal, 2 for excessive"); 708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Set bits to enable debugging"); 718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(settle, "Settle delay in seconds. Default 3"); 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nvram, "Option currently not used"); 738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(excl, "List ioport addresses here to prevent controllers from being attached"); 748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(safe, "Set other settings to a \"safe mode\""); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 778c2ecf20Sopenharmony_ciMODULE_VERSION(SYM_VERSION); 788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Matthew Wilcox <matthew@wil.cx>"); 798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NCR, Symbios and LSI 8xx and 1010 PCI SCSI adapters"); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void sym2_setup_params(void) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci char *p = excl_string; 848c2ecf20Sopenharmony_ci int xi = 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while (p && (xi < 8)) { 878c2ecf20Sopenharmony_ci char *next_p; 888c2ecf20Sopenharmony_ci int val = (int) simple_strtoul(p, &next_p, 0); 898c2ecf20Sopenharmony_ci sym_driver_setup.excludes[xi++] = val; 908c2ecf20Sopenharmony_ci p = next_p; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (safe_string) { 948c2ecf20Sopenharmony_ci if (*safe_string == 'y') { 958c2ecf20Sopenharmony_ci sym_driver_setup.max_tag = 0; 968c2ecf20Sopenharmony_ci sym_driver_setup.burst_order = 0; 978c2ecf20Sopenharmony_ci sym_driver_setup.scsi_led = 0; 988c2ecf20Sopenharmony_ci sym_driver_setup.scsi_diff = 1; 998c2ecf20Sopenharmony_ci sym_driver_setup.irq_mode = 0; 1008c2ecf20Sopenharmony_ci sym_driver_setup.scsi_bus_check = 2; 1018c2ecf20Sopenharmony_ci sym_driver_setup.host_id = 7; 1028c2ecf20Sopenharmony_ci sym_driver_setup.verbose = 2; 1038c2ecf20Sopenharmony_ci sym_driver_setup.settle_delay = 10; 1048c2ecf20Sopenharmony_ci sym_driver_setup.use_nvram = 1; 1058c2ecf20Sopenharmony_ci } else if (*safe_string != 'n') { 1068c2ecf20Sopenharmony_ci printk(KERN_WARNING NAME53C8XX "Ignoring parameter %s" 1078c2ecf20Sopenharmony_ci " passed to safe option", safe_string); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic struct scsi_transport_template *sym2_transport_template = NULL; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * Driver private area in the SCSI command structure. 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistruct sym_ucmd { /* Override the SCSI pointer structure */ 1188c2ecf20Sopenharmony_ci struct completion *eh_done; /* SCSI error handling */ 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define SYM_UCMD_PTR(cmd) ((struct sym_ucmd *)(&(cmd)->SCp)) 1228c2ecf20Sopenharmony_ci#define SYM_SOFTC_PTR(cmd) sym_get_hcb(cmd->device->host) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * Complete a pending CAM CCB. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_civoid sym_xpt_done(struct sym_hcb *np, struct scsi_cmnd *cmd) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd); 1308c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(struct scsi_pointer) < sizeof(struct sym_ucmd)); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (ucmd->eh_done) 1338c2ecf20Sopenharmony_ci complete(ucmd->eh_done); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci scsi_dma_unmap(cmd); 1368c2ecf20Sopenharmony_ci cmd->scsi_done(cmd); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * Tell the SCSI layer about a BUS RESET. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_civoid sym_xpt_async_bus_reset(struct sym_hcb *np) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci printf_notice("%s: SCSI BUS has been reset.\n", sym_name(np)); 1458c2ecf20Sopenharmony_ci np->s.settle_time = jiffies + sym_driver_setup.settle_delay * HZ; 1468c2ecf20Sopenharmony_ci np->s.settle_time_valid = 1; 1478c2ecf20Sopenharmony_ci if (sym_verbose >= 2) 1488c2ecf20Sopenharmony_ci printf_info("%s: command processing suspended for %d seconds\n", 1498c2ecf20Sopenharmony_ci sym_name(np), sym_driver_setup.settle_delay); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* 1538c2ecf20Sopenharmony_ci * Choose the more appropriate CAM status if 1548c2ecf20Sopenharmony_ci * the IO encountered an extended error. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic int sym_xerr_cam_status(int cam_status, int x_status) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci if (x_status) { 1598c2ecf20Sopenharmony_ci if (x_status & XE_PARITY_ERR) 1608c2ecf20Sopenharmony_ci cam_status = DID_PARITY; 1618c2ecf20Sopenharmony_ci else 1628c2ecf20Sopenharmony_ci cam_status = DID_ERROR; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci return cam_status; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * Build CAM result for a failed or auto-sensed IO. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_civoid sym_set_cam_result_error(struct sym_hcb *np, struct sym_ccb *cp, int resid) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd = cp->cmd; 1738c2ecf20Sopenharmony_ci u_int cam_status, scsi_status, drv_status; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci drv_status = 0; 1768c2ecf20Sopenharmony_ci cam_status = DID_OK; 1778c2ecf20Sopenharmony_ci scsi_status = cp->ssss_status; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (cp->host_flags & HF_SENSE) { 1808c2ecf20Sopenharmony_ci scsi_status = cp->sv_scsi_status; 1818c2ecf20Sopenharmony_ci resid = cp->sv_resid; 1828c2ecf20Sopenharmony_ci if (sym_verbose && cp->sv_xerr_status) 1838c2ecf20Sopenharmony_ci sym_print_xerr(cmd, cp->sv_xerr_status); 1848c2ecf20Sopenharmony_ci if (cp->host_status == HS_COMPLETE && 1858c2ecf20Sopenharmony_ci cp->ssss_status == S_GOOD && 1868c2ecf20Sopenharmony_ci cp->xerr_status == 0) { 1878c2ecf20Sopenharmony_ci cam_status = sym_xerr_cam_status(DID_OK, 1888c2ecf20Sopenharmony_ci cp->sv_xerr_status); 1898c2ecf20Sopenharmony_ci drv_status = DRIVER_SENSE; 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * Bounce back the sense data to user. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); 1948c2ecf20Sopenharmony_ci memcpy(cmd->sense_buffer, cp->sns_bbuf, 1958c2ecf20Sopenharmony_ci min(SCSI_SENSE_BUFFERSIZE, SYM_SNS_BBUF_LEN)); 1968c2ecf20Sopenharmony_ci#if 0 1978c2ecf20Sopenharmony_ci /* 1988c2ecf20Sopenharmony_ci * If the device reports a UNIT ATTENTION condition 1998c2ecf20Sopenharmony_ci * due to a RESET condition, we should consider all 2008c2ecf20Sopenharmony_ci * disconnect CCBs for this unit as aborted. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci if (1) { 2038c2ecf20Sopenharmony_ci u_char *p; 2048c2ecf20Sopenharmony_ci p = (u_char *) cmd->sense_data; 2058c2ecf20Sopenharmony_ci if (p[0]==0x70 && p[2]==0x6 && p[12]==0x29) 2068c2ecf20Sopenharmony_ci sym_clear_tasks(np, DID_ABORT, 2078c2ecf20Sopenharmony_ci cp->target,cp->lun, -1); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci#endif 2108c2ecf20Sopenharmony_ci } else { 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * Error return from our internal request sense. This 2138c2ecf20Sopenharmony_ci * is bad: we must clear the contingent allegiance 2148c2ecf20Sopenharmony_ci * condition otherwise the device will always return 2158c2ecf20Sopenharmony_ci * BUSY. Use a big stick. 2168c2ecf20Sopenharmony_ci */ 2178c2ecf20Sopenharmony_ci sym_reset_scsi_target(np, cmd->device->id); 2188c2ecf20Sopenharmony_ci cam_status = DID_ERROR; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } else if (cp->host_status == HS_COMPLETE) /* Bad SCSI status */ 2218c2ecf20Sopenharmony_ci cam_status = DID_OK; 2228c2ecf20Sopenharmony_ci else if (cp->host_status == HS_SEL_TIMEOUT) /* Selection timeout */ 2238c2ecf20Sopenharmony_ci cam_status = DID_NO_CONNECT; 2248c2ecf20Sopenharmony_ci else if (cp->host_status == HS_UNEXPECTED) /* Unexpected BUS FREE*/ 2258c2ecf20Sopenharmony_ci cam_status = DID_ERROR; 2268c2ecf20Sopenharmony_ci else { /* Extended error */ 2278c2ecf20Sopenharmony_ci if (sym_verbose) { 2288c2ecf20Sopenharmony_ci sym_print_addr(cmd, "COMMAND FAILED (%x %x %x).\n", 2298c2ecf20Sopenharmony_ci cp->host_status, cp->ssss_status, 2308c2ecf20Sopenharmony_ci cp->xerr_status); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci /* 2338c2ecf20Sopenharmony_ci * Set the most appropriate value for CAM status. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci cam_status = sym_xerr_cam_status(DID_ERROR, cp->xerr_status); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci scsi_set_resid(cmd, resid); 2388c2ecf20Sopenharmony_ci cmd->result = (drv_status << 24) | (cam_status << 16) | scsi_status; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int sym_scatter(struct sym_hcb *np, struct sym_ccb *cp, struct scsi_cmnd *cmd) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci int segment; 2448c2ecf20Sopenharmony_ci int use_sg; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci cp->data_len = 0; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci use_sg = scsi_dma_map(cmd); 2498c2ecf20Sopenharmony_ci if (use_sg > 0) { 2508c2ecf20Sopenharmony_ci struct scatterlist *sg; 2518c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[cp->target]; 2528c2ecf20Sopenharmony_ci struct sym_tblmove *data; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (use_sg > SYM_CONF_MAX_SG) { 2558c2ecf20Sopenharmony_ci scsi_dma_unmap(cmd); 2568c2ecf20Sopenharmony_ci return -1; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci data = &cp->phys.data[SYM_CONF_MAX_SG - use_sg]; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci scsi_for_each_sg(cmd, sg, use_sg, segment) { 2628c2ecf20Sopenharmony_ci dma_addr_t baddr = sg_dma_address(sg); 2638c2ecf20Sopenharmony_ci unsigned int len = sg_dma_len(sg); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if ((len & 1) && (tp->head.wval & EWS)) { 2668c2ecf20Sopenharmony_ci len++; 2678c2ecf20Sopenharmony_ci cp->odd_byte_adjustment++; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci sym_build_sge(np, &data[segment], baddr, len); 2718c2ecf20Sopenharmony_ci cp->data_len += len; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } else { 2748c2ecf20Sopenharmony_ci segment = -2; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return segment; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* 2818c2ecf20Sopenharmony_ci * Queue a SCSI command. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_cistatic int sym_queue_command(struct sym_hcb *np, struct scsi_cmnd *cmd) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct scsi_device *sdev = cmd->device; 2868c2ecf20Sopenharmony_ci struct sym_tcb *tp; 2878c2ecf20Sopenharmony_ci struct sym_lcb *lp; 2888c2ecf20Sopenharmony_ci struct sym_ccb *cp; 2898c2ecf20Sopenharmony_ci int order; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * Retrieve the target descriptor. 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_ci tp = &np->target[sdev->id]; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* 2978c2ecf20Sopenharmony_ci * Select tagged/untagged. 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci lp = sym_lp(tp, sdev->lun); 3008c2ecf20Sopenharmony_ci order = (lp && lp->s.reqtags) ? M_SIMPLE_TAG : 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* 3038c2ecf20Sopenharmony_ci * Queue the SCSI IO. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci cp = sym_get_ccb(np, cmd, order); 3068c2ecf20Sopenharmony_ci if (!cp) 3078c2ecf20Sopenharmony_ci return 1; /* Means resource shortage */ 3088c2ecf20Sopenharmony_ci sym_queue_scsiio(np, cmd, cp); 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci/* 3138c2ecf20Sopenharmony_ci * Setup buffers and pointers that address the CDB. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cistatic inline int sym_setup_cdb(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci memcpy(cp->cdb_buf, cmd->cmnd, cmd->cmd_len); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci cp->phys.cmd.addr = CCB_BA(cp, cdb_buf[0]); 3208c2ecf20Sopenharmony_ci cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* 3268c2ecf20Sopenharmony_ci * Setup pointers that address the data and start the I/O. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ciint sym_setup_data_and_start(struct sym_hcb *np, struct scsi_cmnd *cmd, struct sym_ccb *cp) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci u32 lastp, goalp; 3318c2ecf20Sopenharmony_ci int dir; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* 3348c2ecf20Sopenharmony_ci * Build the CDB. 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_ci if (sym_setup_cdb(np, cmd, cp)) 3378c2ecf20Sopenharmony_ci goto out_abort; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * No direction means no data. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_ci dir = cmd->sc_data_direction; 3438c2ecf20Sopenharmony_ci if (dir != DMA_NONE) { 3448c2ecf20Sopenharmony_ci cp->segments = sym_scatter(np, cp, cmd); 3458c2ecf20Sopenharmony_ci if (cp->segments < 0) { 3468c2ecf20Sopenharmony_ci sym_set_cam_status(cmd, DID_ERROR); 3478c2ecf20Sopenharmony_ci goto out_abort; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* 3518c2ecf20Sopenharmony_ci * No segments means no data. 3528c2ecf20Sopenharmony_ci */ 3538c2ecf20Sopenharmony_ci if (!cp->segments) 3548c2ecf20Sopenharmony_ci dir = DMA_NONE; 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci cp->data_len = 0; 3578c2ecf20Sopenharmony_ci cp->segments = 0; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * Set the data pointer. 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_ci switch (dir) { 3648c2ecf20Sopenharmony_ci case DMA_BIDIRECTIONAL: 3658c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "got DMA_BIDIRECTIONAL command"); 3668c2ecf20Sopenharmony_ci sym_set_cam_status(cmd, DID_ERROR); 3678c2ecf20Sopenharmony_ci goto out_abort; 3688c2ecf20Sopenharmony_ci case DMA_TO_DEVICE: 3698c2ecf20Sopenharmony_ci goalp = SCRIPTA_BA(np, data_out2) + 8; 3708c2ecf20Sopenharmony_ci lastp = goalp - 8 - (cp->segments * (2*4)); 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci case DMA_FROM_DEVICE: 3738c2ecf20Sopenharmony_ci cp->host_flags |= HF_DATA_IN; 3748c2ecf20Sopenharmony_ci goalp = SCRIPTA_BA(np, data_in2) + 8; 3758c2ecf20Sopenharmony_ci lastp = goalp - 8 - (cp->segments * (2*4)); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci case DMA_NONE: 3788c2ecf20Sopenharmony_ci default: 3798c2ecf20Sopenharmony_ci lastp = goalp = SCRIPTB_BA(np, no_data); 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * Set all pointers values needed by SCRIPTS. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci cp->phys.head.lastp = cpu_to_scr(lastp); 3878c2ecf20Sopenharmony_ci cp->phys.head.savep = cpu_to_scr(lastp); 3888c2ecf20Sopenharmony_ci cp->startp = cp->phys.head.savep; 3898c2ecf20Sopenharmony_ci cp->goalp = cpu_to_scr(goalp); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * When `#ifed 1', the code below makes the driver 3938c2ecf20Sopenharmony_ci * panic on the first attempt to write to a SCSI device. 3948c2ecf20Sopenharmony_ci * It is the first test we want to do after a driver 3958c2ecf20Sopenharmony_ci * change that does not seem obviously safe. :) 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci#if 0 3988c2ecf20Sopenharmony_ci switch (cp->cdb_buf[0]) { 3998c2ecf20Sopenharmony_ci case 0x0A: case 0x2A: case 0xAA: 4008c2ecf20Sopenharmony_ci panic("XXXXXXXXXXXXX WRITE NOT YET ALLOWED XXXXXXXXXXXXXX\n"); 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci default: 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci#endif 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * activate this job. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci sym_put_start_queue(np, cp); 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ciout_abort: 4148c2ecf20Sopenharmony_ci sym_free_ccb(np, cp); 4158c2ecf20Sopenharmony_ci sym_xpt_done(np, cmd); 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci/* 4218c2ecf20Sopenharmony_ci * timer daemon. 4228c2ecf20Sopenharmony_ci * 4238c2ecf20Sopenharmony_ci * Misused to keep the driver running when 4248c2ecf20Sopenharmony_ci * interrupts are not configured correctly. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_cistatic void sym_timer(struct sym_hcb *np) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci unsigned long thistime = jiffies; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* 4318c2ecf20Sopenharmony_ci * Restart the timer. 4328c2ecf20Sopenharmony_ci */ 4338c2ecf20Sopenharmony_ci np->s.timer.expires = thistime + SYM_CONF_TIMER_INTERVAL; 4348c2ecf20Sopenharmony_ci add_timer(&np->s.timer); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * If we are resetting the ncr, wait for settle_time before 4388c2ecf20Sopenharmony_ci * clearing it. Then command processing will be resumed. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci if (np->s.settle_time_valid) { 4418c2ecf20Sopenharmony_ci if (time_before_eq(np->s.settle_time, thistime)) { 4428c2ecf20Sopenharmony_ci if (sym_verbose >= 2 ) 4438c2ecf20Sopenharmony_ci printk("%s: command processing resumed\n", 4448c2ecf20Sopenharmony_ci sym_name(np)); 4458c2ecf20Sopenharmony_ci np->s.settle_time_valid = 0; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci return; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci /* 4518c2ecf20Sopenharmony_ci * Nothing to do for now, but that may come. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (np->s.lasttime + 4*HZ < thistime) { 4548c2ecf20Sopenharmony_ci np->s.lasttime = thistime; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci#ifdef SYM_CONF_PCIQ_MAY_MISS_COMPLETIONS 4588c2ecf20Sopenharmony_ci /* 4598c2ecf20Sopenharmony_ci * Some way-broken PCI bridges may lead to 4608c2ecf20Sopenharmony_ci * completions being lost when the clearing 4618c2ecf20Sopenharmony_ci * of the INTFLY flag by the CPU occurs 4628c2ecf20Sopenharmony_ci * concurrently with the chip raising this flag. 4638c2ecf20Sopenharmony_ci * If this ever happen, lost completions will 4648c2ecf20Sopenharmony_ci * be reaped here. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci sym_wakeup_done(np); 4678c2ecf20Sopenharmony_ci#endif 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci/* 4728c2ecf20Sopenharmony_ci * PCI BUS error handler. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_civoid sym_log_bus_error(struct Scsi_Host *shost) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct sym_data *sym_data = shost_priv(shost); 4778c2ecf20Sopenharmony_ci struct pci_dev *pdev = sym_data->pdev; 4788c2ecf20Sopenharmony_ci unsigned short pci_sts; 4798c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_STATUS, &pci_sts); 4808c2ecf20Sopenharmony_ci if (pci_sts & 0xf900) { 4818c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_STATUS, pci_sts); 4828c2ecf20Sopenharmony_ci shost_printk(KERN_WARNING, shost, 4838c2ecf20Sopenharmony_ci "PCI bus error: status = 0x%04x\n", pci_sts & 0xf900); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* 4888c2ecf20Sopenharmony_ci * queuecommand method. Entered with the host adapter lock held and 4898c2ecf20Sopenharmony_ci * interrupts disabled. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic int sym53c8xx_queue_command_lck(struct scsi_cmnd *cmd, 4928c2ecf20Sopenharmony_ci void (*done)(struct scsi_cmnd *)) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct sym_hcb *np = SYM_SOFTC_PTR(cmd); 4958c2ecf20Sopenharmony_ci struct sym_ucmd *ucp = SYM_UCMD_PTR(cmd); 4968c2ecf20Sopenharmony_ci int sts = 0; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci cmd->scsi_done = done; 4998c2ecf20Sopenharmony_ci memset(ucp, 0, sizeof(*ucp)); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* 5028c2ecf20Sopenharmony_ci * Shorten our settle_time if needed for 5038c2ecf20Sopenharmony_ci * this command not to time out. 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_ci if (np->s.settle_time_valid && cmd->request->timeout) { 5068c2ecf20Sopenharmony_ci unsigned long tlimit = jiffies + cmd->request->timeout; 5078c2ecf20Sopenharmony_ci tlimit -= SYM_CONF_TIMER_INTERVAL*2; 5088c2ecf20Sopenharmony_ci if (time_after(np->s.settle_time, tlimit)) { 5098c2ecf20Sopenharmony_ci np->s.settle_time = tlimit; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (np->s.settle_time_valid) 5148c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci sts = sym_queue_command(np, cmd); 5178c2ecf20Sopenharmony_ci if (sts) 5188c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 5198c2ecf20Sopenharmony_ci return 0; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(sym53c8xx_queue_command) 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/* 5258c2ecf20Sopenharmony_ci * Linux entry point of the interrupt handler. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistatic irqreturn_t sym53c8xx_intr(int irq, void *dev_id) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_id; 5308c2ecf20Sopenharmony_ci struct sym_data *sym_data = shost_priv(shost); 5318c2ecf20Sopenharmony_ci irqreturn_t result; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Avoid spinloop trying to handle interrupts on frozen device */ 5348c2ecf20Sopenharmony_ci if (pci_channel_offline(sym_data->pdev)) 5358c2ecf20Sopenharmony_ci return IRQ_NONE; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("["); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci spin_lock(shost->host_lock); 5408c2ecf20Sopenharmony_ci result = sym_interrupt(shost); 5418c2ecf20Sopenharmony_ci spin_unlock(shost->host_lock); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (DEBUG_FLAGS & DEBUG_TINY) printf_debug ("]\n"); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return result; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/* 5498c2ecf20Sopenharmony_ci * Linux entry point of the timer handler 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_cistatic void sym53c8xx_timer(struct timer_list *t) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct sym_hcb *np = from_timer(np, t, s.timer); 5548c2ecf20Sopenharmony_ci unsigned long flags; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci spin_lock_irqsave(np->s.host->host_lock, flags); 5578c2ecf20Sopenharmony_ci sym_timer(np); 5588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(np->s.host->host_lock, flags); 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/* 5638c2ecf20Sopenharmony_ci * What the eh thread wants us to perform. 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci#define SYM_EH_ABORT 0 5668c2ecf20Sopenharmony_ci#define SYM_EH_DEVICE_RESET 1 5678c2ecf20Sopenharmony_ci#define SYM_EH_BUS_RESET 2 5688c2ecf20Sopenharmony_ci#define SYM_EH_HOST_RESET 3 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/* 5718c2ecf20Sopenharmony_ci * Generic method for our eh processing. 5728c2ecf20Sopenharmony_ci * The 'op' argument tells what we have to do. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cistatic int sym_eh_handler(int op, char *opname, struct scsi_cmnd *cmd) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct sym_ucmd *ucmd = SYM_UCMD_PTR(cmd); 5778c2ecf20Sopenharmony_ci struct Scsi_Host *shost = cmd->device->host; 5788c2ecf20Sopenharmony_ci struct sym_data *sym_data = shost_priv(shost); 5798c2ecf20Sopenharmony_ci struct pci_dev *pdev = sym_data->pdev; 5808c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_data->ncb; 5818c2ecf20Sopenharmony_ci SYM_QUEHEAD *qp; 5828c2ecf20Sopenharmony_ci int cmd_queued = 0; 5838c2ecf20Sopenharmony_ci int sts = -1; 5848c2ecf20Sopenharmony_ci struct completion eh_done; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci scmd_printk(KERN_WARNING, cmd, "%s operation started\n", opname); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* We may be in an error condition because the PCI bus 5898c2ecf20Sopenharmony_ci * went down. In this case, we need to wait until the 5908c2ecf20Sopenharmony_ci * PCI bus is reset, the card is reset, and only then 5918c2ecf20Sopenharmony_ci * proceed with the scsi error recovery. There's no 5928c2ecf20Sopenharmony_ci * point in hurrying; take a leisurely wait. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_ci#define WAIT_FOR_PCI_RECOVERY 35 5958c2ecf20Sopenharmony_ci if (pci_channel_offline(pdev)) { 5968c2ecf20Sopenharmony_ci int finished_reset = 0; 5978c2ecf20Sopenharmony_ci init_completion(&eh_done); 5988c2ecf20Sopenharmony_ci spin_lock_irq(shost->host_lock); 5998c2ecf20Sopenharmony_ci /* Make sure we didn't race */ 6008c2ecf20Sopenharmony_ci if (pci_channel_offline(pdev)) { 6018c2ecf20Sopenharmony_ci BUG_ON(sym_data->io_reset); 6028c2ecf20Sopenharmony_ci sym_data->io_reset = &eh_done; 6038c2ecf20Sopenharmony_ci } else { 6048c2ecf20Sopenharmony_ci finished_reset = 1; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci spin_unlock_irq(shost->host_lock); 6078c2ecf20Sopenharmony_ci if (!finished_reset) 6088c2ecf20Sopenharmony_ci finished_reset = wait_for_completion_timeout 6098c2ecf20Sopenharmony_ci (sym_data->io_reset, 6108c2ecf20Sopenharmony_ci WAIT_FOR_PCI_RECOVERY*HZ); 6118c2ecf20Sopenharmony_ci spin_lock_irq(shost->host_lock); 6128c2ecf20Sopenharmony_ci sym_data->io_reset = NULL; 6138c2ecf20Sopenharmony_ci spin_unlock_irq(shost->host_lock); 6148c2ecf20Sopenharmony_ci if (!finished_reset) 6158c2ecf20Sopenharmony_ci return SCSI_FAILED; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci spin_lock_irq(shost->host_lock); 6198c2ecf20Sopenharmony_ci /* This one is queued in some place -> to wait for completion */ 6208c2ecf20Sopenharmony_ci FOR_EACH_QUEUED_ELEMENT(&np->busy_ccbq, qp) { 6218c2ecf20Sopenharmony_ci struct sym_ccb *cp = sym_que_entry(qp, struct sym_ccb, link_ccbq); 6228c2ecf20Sopenharmony_ci if (cp->cmd == cmd) { 6238c2ecf20Sopenharmony_ci cmd_queued = 1; 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* Try to proceed the operation we have been asked for */ 6298c2ecf20Sopenharmony_ci sts = -1; 6308c2ecf20Sopenharmony_ci switch(op) { 6318c2ecf20Sopenharmony_ci case SYM_EH_ABORT: 6328c2ecf20Sopenharmony_ci sts = sym_abort_scsiio(np, cmd, 1); 6338c2ecf20Sopenharmony_ci break; 6348c2ecf20Sopenharmony_ci case SYM_EH_DEVICE_RESET: 6358c2ecf20Sopenharmony_ci sts = sym_reset_scsi_target(np, cmd->device->id); 6368c2ecf20Sopenharmony_ci break; 6378c2ecf20Sopenharmony_ci case SYM_EH_BUS_RESET: 6388c2ecf20Sopenharmony_ci sym_reset_scsi_bus(np, 1); 6398c2ecf20Sopenharmony_ci sts = 0; 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci case SYM_EH_HOST_RESET: 6428c2ecf20Sopenharmony_ci sym_reset_scsi_bus(np, 0); 6438c2ecf20Sopenharmony_ci sym_start_up(shost, 1); 6448c2ecf20Sopenharmony_ci sts = 0; 6458c2ecf20Sopenharmony_ci break; 6468c2ecf20Sopenharmony_ci default: 6478c2ecf20Sopenharmony_ci break; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* On error, restore everything and cross fingers :) */ 6518c2ecf20Sopenharmony_ci if (sts) 6528c2ecf20Sopenharmony_ci cmd_queued = 0; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (cmd_queued) { 6558c2ecf20Sopenharmony_ci init_completion(&eh_done); 6568c2ecf20Sopenharmony_ci ucmd->eh_done = &eh_done; 6578c2ecf20Sopenharmony_ci spin_unlock_irq(shost->host_lock); 6588c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&eh_done, 5*HZ)) { 6598c2ecf20Sopenharmony_ci ucmd->eh_done = NULL; 6608c2ecf20Sopenharmony_ci sts = -2; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci } else { 6638c2ecf20Sopenharmony_ci spin_unlock_irq(shost->host_lock); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci dev_warn(&cmd->device->sdev_gendev, "%s operation %s.\n", opname, 6678c2ecf20Sopenharmony_ci sts==0 ? "complete" :sts==-2 ? "timed-out" : "failed"); 6688c2ecf20Sopenharmony_ci return sts ? SCSI_FAILED : SCSI_SUCCESS; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci/* 6738c2ecf20Sopenharmony_ci * Error handlers called from the eh thread (one thread per HBA). 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_cistatic int sym53c8xx_eh_abort_handler(struct scsi_cmnd *cmd) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci return sym_eh_handler(SYM_EH_ABORT, "ABORT", cmd); 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic int sym53c8xx_eh_device_reset_handler(struct scsi_cmnd *cmd) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci return sym_eh_handler(SYM_EH_DEVICE_RESET, "DEVICE RESET", cmd); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic int sym53c8xx_eh_bus_reset_handler(struct scsi_cmnd *cmd) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci return sym_eh_handler(SYM_EH_BUS_RESET, "BUS RESET", cmd); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic int sym53c8xx_eh_host_reset_handler(struct scsi_cmnd *cmd) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci return sym_eh_handler(SYM_EH_HOST_RESET, "HOST RESET", cmd); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci/* 6968c2ecf20Sopenharmony_ci * Tune device queuing depth, according to various limits. 6978c2ecf20Sopenharmony_ci */ 6988c2ecf20Sopenharmony_cistatic void sym_tune_dev_queuing(struct sym_tcb *tp, int lun, u_short reqtags) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct sym_lcb *lp = sym_lp(tp, lun); 7018c2ecf20Sopenharmony_ci u_short oldtags; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (!lp) 7048c2ecf20Sopenharmony_ci return; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci oldtags = lp->s.reqtags; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (reqtags > lp->s.scdev_depth) 7098c2ecf20Sopenharmony_ci reqtags = lp->s.scdev_depth; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci lp->s.reqtags = reqtags; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (reqtags != oldtags) { 7148c2ecf20Sopenharmony_ci dev_info(&tp->starget->dev, 7158c2ecf20Sopenharmony_ci "tagged command queuing %s, command queue depth %d.\n", 7168c2ecf20Sopenharmony_ci lp->s.reqtags ? "enabled" : "disabled", reqtags); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int sym53c8xx_slave_alloc(struct scsi_device *sdev) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(sdev->host); 7238c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[sdev->id]; 7248c2ecf20Sopenharmony_ci struct sym_lcb *lp; 7258c2ecf20Sopenharmony_ci unsigned long flags; 7268c2ecf20Sopenharmony_ci int error; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (sdev->id >= SYM_CONF_MAX_TARGET || sdev->lun >= SYM_CONF_MAX_LUN) 7298c2ecf20Sopenharmony_ci return -ENXIO; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci spin_lock_irqsave(np->s.host->host_lock, flags); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* 7348c2ecf20Sopenharmony_ci * Fail the device init if the device is flagged NOSCAN at BOOT in 7358c2ecf20Sopenharmony_ci * the NVRAM. This may speed up boot and maintain coherency with 7368c2ecf20Sopenharmony_ci * BIOS device numbering. Clearing the flag allows the user to 7378c2ecf20Sopenharmony_ci * rescan skipped devices later. We also return an error for 7388c2ecf20Sopenharmony_ci * devices not flagged for SCAN LUNS in the NVRAM since some single 7398c2ecf20Sopenharmony_ci * lun devices behave badly when asked for a non zero LUN. 7408c2ecf20Sopenharmony_ci */ 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (tp->usrflags & SYM_SCAN_BOOT_DISABLED) { 7438c2ecf20Sopenharmony_ci tp->usrflags &= ~SYM_SCAN_BOOT_DISABLED; 7448c2ecf20Sopenharmony_ci starget_printk(KERN_INFO, sdev->sdev_target, 7458c2ecf20Sopenharmony_ci "Scan at boot disabled in NVRAM\n"); 7468c2ecf20Sopenharmony_ci error = -ENXIO; 7478c2ecf20Sopenharmony_ci goto out; 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (tp->usrflags & SYM_SCAN_LUNS_DISABLED) { 7518c2ecf20Sopenharmony_ci if (sdev->lun != 0) { 7528c2ecf20Sopenharmony_ci error = -ENXIO; 7538c2ecf20Sopenharmony_ci goto out; 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci starget_printk(KERN_INFO, sdev->sdev_target, 7568c2ecf20Sopenharmony_ci "Multiple LUNs disabled in NVRAM\n"); 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci lp = sym_alloc_lcb(np, sdev->id, sdev->lun); 7608c2ecf20Sopenharmony_ci if (!lp) { 7618c2ecf20Sopenharmony_ci error = -ENOMEM; 7628c2ecf20Sopenharmony_ci goto out; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci if (tp->nlcb == 1) 7658c2ecf20Sopenharmony_ci tp->starget = sdev->sdev_target; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci spi_min_period(tp->starget) = tp->usr_period; 7688c2ecf20Sopenharmony_ci spi_max_width(tp->starget) = tp->usr_width; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci error = 0; 7718c2ecf20Sopenharmony_ciout: 7728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(np->s.host->host_lock, flags); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci return error; 7758c2ecf20Sopenharmony_ci} 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci/* 7788c2ecf20Sopenharmony_ci * Linux entry point for device queue sizing. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_cistatic int sym53c8xx_slave_configure(struct scsi_device *sdev) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(sdev->host); 7838c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[sdev->id]; 7848c2ecf20Sopenharmony_ci struct sym_lcb *lp = sym_lp(tp, sdev->lun); 7858c2ecf20Sopenharmony_ci int reqtags, depth_to_use; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* 7888c2ecf20Sopenharmony_ci * Get user flags. 7898c2ecf20Sopenharmony_ci */ 7908c2ecf20Sopenharmony_ci lp->curr_flags = lp->user_flags; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* 7938c2ecf20Sopenharmony_ci * Select queue depth from driver setup. 7948c2ecf20Sopenharmony_ci * Do not use more than configured by user. 7958c2ecf20Sopenharmony_ci * Use at least 1. 7968c2ecf20Sopenharmony_ci * Do not use more than our maximum. 7978c2ecf20Sopenharmony_ci */ 7988c2ecf20Sopenharmony_ci reqtags = sym_driver_setup.max_tag; 7998c2ecf20Sopenharmony_ci if (reqtags > tp->usrtags) 8008c2ecf20Sopenharmony_ci reqtags = tp->usrtags; 8018c2ecf20Sopenharmony_ci if (!sdev->tagged_supported) 8028c2ecf20Sopenharmony_ci reqtags = 0; 8038c2ecf20Sopenharmony_ci if (reqtags > SYM_CONF_MAX_TAG) 8048c2ecf20Sopenharmony_ci reqtags = SYM_CONF_MAX_TAG; 8058c2ecf20Sopenharmony_ci depth_to_use = reqtags ? reqtags : 1; 8068c2ecf20Sopenharmony_ci scsi_change_queue_depth(sdev, depth_to_use); 8078c2ecf20Sopenharmony_ci lp->s.scdev_depth = depth_to_use; 8088c2ecf20Sopenharmony_ci sym_tune_dev_queuing(tp, sdev->lun, reqtags); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (!spi_initial_dv(sdev->sdev_target)) 8118c2ecf20Sopenharmony_ci spi_dv_device(sdev); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci return 0; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic void sym53c8xx_slave_destroy(struct scsi_device *sdev) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(sdev->host); 8198c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[sdev->id]; 8208c2ecf20Sopenharmony_ci struct sym_lcb *lp = sym_lp(tp, sdev->lun); 8218c2ecf20Sopenharmony_ci unsigned long flags; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci /* if slave_alloc returned before allocating a sym_lcb, return */ 8248c2ecf20Sopenharmony_ci if (!lp) 8258c2ecf20Sopenharmony_ci return; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci spin_lock_irqsave(np->s.host->host_lock, flags); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (lp->busy_itlq || lp->busy_itl) { 8308c2ecf20Sopenharmony_ci /* 8318c2ecf20Sopenharmony_ci * This really shouldn't happen, but we can't return an error 8328c2ecf20Sopenharmony_ci * so let's try to stop all on-going I/O. 8338c2ecf20Sopenharmony_ci */ 8348c2ecf20Sopenharmony_ci starget_printk(KERN_WARNING, tp->starget, 8358c2ecf20Sopenharmony_ci "Removing busy LCB (%d)\n", (u8)sdev->lun); 8368c2ecf20Sopenharmony_ci sym_reset_scsi_bus(np, 1); 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (sym_free_lcb(np, sdev->id, sdev->lun) == 0) { 8408c2ecf20Sopenharmony_ci /* 8418c2ecf20Sopenharmony_ci * It was the last unit for this target. 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_ci tp->head.sval = 0; 8448c2ecf20Sopenharmony_ci tp->head.wval = np->rv_scntl3; 8458c2ecf20Sopenharmony_ci tp->head.uval = 0; 8468c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 8478c2ecf20Sopenharmony_ci tp->starget = NULL; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(np->s.host->host_lock, flags); 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci/* 8548c2ecf20Sopenharmony_ci * Linux entry point for info() function 8558c2ecf20Sopenharmony_ci */ 8568c2ecf20Sopenharmony_cistatic const char *sym53c8xx_info (struct Scsi_Host *host) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci return SYM_DRIVER_NAME; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_PROC_INFO_SUPPORT 8638c2ecf20Sopenharmony_ci/* 8648c2ecf20Sopenharmony_ci * Proc file system stuff 8658c2ecf20Sopenharmony_ci * 8668c2ecf20Sopenharmony_ci * A read operation returns adapter information. 8678c2ecf20Sopenharmony_ci * A write operation is a control command. 8688c2ecf20Sopenharmony_ci * The string is parsed in the driver code and the command is passed 8698c2ecf20Sopenharmony_ci * to the sym_usercmd() function. 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_USER_COMMAND_SUPPORT 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_cistruct sym_usrcmd { 8758c2ecf20Sopenharmony_ci u_long target; 8768c2ecf20Sopenharmony_ci u_long lun; 8778c2ecf20Sopenharmony_ci u_long data; 8788c2ecf20Sopenharmony_ci u_long cmd; 8798c2ecf20Sopenharmony_ci}; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci#define UC_SETSYNC 10 8828c2ecf20Sopenharmony_ci#define UC_SETTAGS 11 8838c2ecf20Sopenharmony_ci#define UC_SETDEBUG 12 8848c2ecf20Sopenharmony_ci#define UC_SETWIDE 14 8858c2ecf20Sopenharmony_ci#define UC_SETFLAG 15 8868c2ecf20Sopenharmony_ci#define UC_SETVERBOSE 17 8878c2ecf20Sopenharmony_ci#define UC_RESETDEV 18 8888c2ecf20Sopenharmony_ci#define UC_CLEARDEV 19 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic void sym_exec_user_command (struct sym_hcb *np, struct sym_usrcmd *uc) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct sym_tcb *tp; 8938c2ecf20Sopenharmony_ci int t, l; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci switch (uc->cmd) { 8968c2ecf20Sopenharmony_ci case 0: return; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT 8998c2ecf20Sopenharmony_ci case UC_SETDEBUG: 9008c2ecf20Sopenharmony_ci sym_debug_flags = uc->data; 9018c2ecf20Sopenharmony_ci break; 9028c2ecf20Sopenharmony_ci#endif 9038c2ecf20Sopenharmony_ci case UC_SETVERBOSE: 9048c2ecf20Sopenharmony_ci np->verbose = uc->data; 9058c2ecf20Sopenharmony_ci break; 9068c2ecf20Sopenharmony_ci default: 9078c2ecf20Sopenharmony_ci /* 9088c2ecf20Sopenharmony_ci * We assume that other commands apply to targets. 9098c2ecf20Sopenharmony_ci * This should always be the case and avoid the below 9108c2ecf20Sopenharmony_ci * 4 lines to be repeated 6 times. 9118c2ecf20Sopenharmony_ci */ 9128c2ecf20Sopenharmony_ci for (t = 0; t < SYM_CONF_MAX_TARGET; t++) { 9138c2ecf20Sopenharmony_ci if (!((uc->target >> t) & 1)) 9148c2ecf20Sopenharmony_ci continue; 9158c2ecf20Sopenharmony_ci tp = &np->target[t]; 9168c2ecf20Sopenharmony_ci if (!tp->nlcb) 9178c2ecf20Sopenharmony_ci continue; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci switch (uc->cmd) { 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci case UC_SETSYNC: 9228c2ecf20Sopenharmony_ci if (!uc->data || uc->data >= 255) { 9238c2ecf20Sopenharmony_ci tp->tgoal.iu = tp->tgoal.dt = 9248c2ecf20Sopenharmony_ci tp->tgoal.qas = 0; 9258c2ecf20Sopenharmony_ci tp->tgoal.offset = 0; 9268c2ecf20Sopenharmony_ci } else if (uc->data <= 9 && np->minsync_dt) { 9278c2ecf20Sopenharmony_ci if (uc->data < np->minsync_dt) 9288c2ecf20Sopenharmony_ci uc->data = np->minsync_dt; 9298c2ecf20Sopenharmony_ci tp->tgoal.iu = tp->tgoal.dt = 9308c2ecf20Sopenharmony_ci tp->tgoal.qas = 1; 9318c2ecf20Sopenharmony_ci tp->tgoal.width = 1; 9328c2ecf20Sopenharmony_ci tp->tgoal.period = uc->data; 9338c2ecf20Sopenharmony_ci tp->tgoal.offset = np->maxoffs_dt; 9348c2ecf20Sopenharmony_ci } else { 9358c2ecf20Sopenharmony_ci if (uc->data < np->minsync) 9368c2ecf20Sopenharmony_ci uc->data = np->minsync; 9378c2ecf20Sopenharmony_ci tp->tgoal.iu = tp->tgoal.dt = 9388c2ecf20Sopenharmony_ci tp->tgoal.qas = 0; 9398c2ecf20Sopenharmony_ci tp->tgoal.period = uc->data; 9408c2ecf20Sopenharmony_ci tp->tgoal.offset = np->maxoffs; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci case UC_SETWIDE: 9458c2ecf20Sopenharmony_ci tp->tgoal.width = uc->data ? 1 : 0; 9468c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 9478c2ecf20Sopenharmony_ci break; 9488c2ecf20Sopenharmony_ci case UC_SETTAGS: 9498c2ecf20Sopenharmony_ci for (l = 0; l < SYM_CONF_MAX_LUN; l++) 9508c2ecf20Sopenharmony_ci sym_tune_dev_queuing(tp, l, uc->data); 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci case UC_RESETDEV: 9538c2ecf20Sopenharmony_ci tp->to_reset = 1; 9548c2ecf20Sopenharmony_ci np->istat_sem = SEM; 9558c2ecf20Sopenharmony_ci OUTB(np, nc_istat, SIGP|SEM); 9568c2ecf20Sopenharmony_ci break; 9578c2ecf20Sopenharmony_ci case UC_CLEARDEV: 9588c2ecf20Sopenharmony_ci for (l = 0; l < SYM_CONF_MAX_LUN; l++) { 9598c2ecf20Sopenharmony_ci struct sym_lcb *lp = sym_lp(tp, l); 9608c2ecf20Sopenharmony_ci if (lp) lp->to_clear = 1; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci np->istat_sem = SEM; 9638c2ecf20Sopenharmony_ci OUTB(np, nc_istat, SIGP|SEM); 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci case UC_SETFLAG: 9668c2ecf20Sopenharmony_ci tp->usrflags = uc->data; 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci} 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic int sym_skip_spaces(char *ptr, int len) 9758c2ecf20Sopenharmony_ci{ 9768c2ecf20Sopenharmony_ci int cnt, c; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci for (cnt = len; cnt > 0 && (c = *ptr++) && isspace(c); cnt--); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci return (len - cnt); 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic int get_int_arg(char *ptr, int len, u_long *pv) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci char *end; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci *pv = simple_strtoul(ptr, &end, 10); 9888c2ecf20Sopenharmony_ci return (end - ptr); 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic int is_keyword(char *ptr, int len, char *verb) 9928c2ecf20Sopenharmony_ci{ 9938c2ecf20Sopenharmony_ci int verb_len = strlen(verb); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (len >= verb_len && !memcmp(verb, ptr, verb_len)) 9968c2ecf20Sopenharmony_ci return verb_len; 9978c2ecf20Sopenharmony_ci else 9988c2ecf20Sopenharmony_ci return 0; 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci#define SKIP_SPACES(ptr, len) \ 10028c2ecf20Sopenharmony_ci if ((arg_len = sym_skip_spaces(ptr, len)) < 1) \ 10038c2ecf20Sopenharmony_ci return -EINVAL; \ 10048c2ecf20Sopenharmony_ci ptr += arg_len; len -= arg_len; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci#define GET_INT_ARG(ptr, len, v) \ 10078c2ecf20Sopenharmony_ci if (!(arg_len = get_int_arg(ptr, len, &(v)))) \ 10088c2ecf20Sopenharmony_ci return -EINVAL; \ 10098c2ecf20Sopenharmony_ci ptr += arg_len; len -= arg_len; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci/* 10138c2ecf20Sopenharmony_ci * Parse a control command 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cistatic int sym_user_command(struct Scsi_Host *shost, char *buffer, int length) 10178c2ecf20Sopenharmony_ci{ 10188c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 10198c2ecf20Sopenharmony_ci char *ptr = buffer; 10208c2ecf20Sopenharmony_ci int len = length; 10218c2ecf20Sopenharmony_ci struct sym_usrcmd cmd, *uc = &cmd; 10228c2ecf20Sopenharmony_ci int arg_len; 10238c2ecf20Sopenharmony_ci u_long target; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci memset(uc, 0, sizeof(*uc)); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (len > 0 && ptr[len-1] == '\n') 10288c2ecf20Sopenharmony_ci --len; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if ((arg_len = is_keyword(ptr, len, "setsync")) != 0) 10318c2ecf20Sopenharmony_ci uc->cmd = UC_SETSYNC; 10328c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "settags")) != 0) 10338c2ecf20Sopenharmony_ci uc->cmd = UC_SETTAGS; 10348c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "setverbose")) != 0) 10358c2ecf20Sopenharmony_ci uc->cmd = UC_SETVERBOSE; 10368c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "setwide")) != 0) 10378c2ecf20Sopenharmony_ci uc->cmd = UC_SETWIDE; 10388c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT 10398c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "setdebug")) != 0) 10408c2ecf20Sopenharmony_ci uc->cmd = UC_SETDEBUG; 10418c2ecf20Sopenharmony_ci#endif 10428c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "setflag")) != 0) 10438c2ecf20Sopenharmony_ci uc->cmd = UC_SETFLAG; 10448c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "resetdev")) != 0) 10458c2ecf20Sopenharmony_ci uc->cmd = UC_RESETDEV; 10468c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "cleardev")) != 0) 10478c2ecf20Sopenharmony_ci uc->cmd = UC_CLEARDEV; 10488c2ecf20Sopenharmony_ci else 10498c2ecf20Sopenharmony_ci arg_len = 0; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci#ifdef DEBUG_PROC_INFO 10528c2ecf20Sopenharmony_ciprintk("sym_user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd); 10538c2ecf20Sopenharmony_ci#endif 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (!arg_len) 10568c2ecf20Sopenharmony_ci return -EINVAL; 10578c2ecf20Sopenharmony_ci ptr += arg_len; len -= arg_len; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci switch(uc->cmd) { 10608c2ecf20Sopenharmony_ci case UC_SETSYNC: 10618c2ecf20Sopenharmony_ci case UC_SETTAGS: 10628c2ecf20Sopenharmony_ci case UC_SETWIDE: 10638c2ecf20Sopenharmony_ci case UC_SETFLAG: 10648c2ecf20Sopenharmony_ci case UC_RESETDEV: 10658c2ecf20Sopenharmony_ci case UC_CLEARDEV: 10668c2ecf20Sopenharmony_ci SKIP_SPACES(ptr, len); 10678c2ecf20Sopenharmony_ci if ((arg_len = is_keyword(ptr, len, "all")) != 0) { 10688c2ecf20Sopenharmony_ci ptr += arg_len; len -= arg_len; 10698c2ecf20Sopenharmony_ci uc->target = ~0; 10708c2ecf20Sopenharmony_ci } else { 10718c2ecf20Sopenharmony_ci GET_INT_ARG(ptr, len, target); 10728c2ecf20Sopenharmony_ci uc->target = (1<<target); 10738c2ecf20Sopenharmony_ci#ifdef DEBUG_PROC_INFO 10748c2ecf20Sopenharmony_ciprintk("sym_user_command: target=%ld\n", target); 10758c2ecf20Sopenharmony_ci#endif 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci break; 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci switch(uc->cmd) { 10818c2ecf20Sopenharmony_ci case UC_SETVERBOSE: 10828c2ecf20Sopenharmony_ci case UC_SETSYNC: 10838c2ecf20Sopenharmony_ci case UC_SETTAGS: 10848c2ecf20Sopenharmony_ci case UC_SETWIDE: 10858c2ecf20Sopenharmony_ci SKIP_SPACES(ptr, len); 10868c2ecf20Sopenharmony_ci GET_INT_ARG(ptr, len, uc->data); 10878c2ecf20Sopenharmony_ci#ifdef DEBUG_PROC_INFO 10888c2ecf20Sopenharmony_ciprintk("sym_user_command: data=%ld\n", uc->data); 10898c2ecf20Sopenharmony_ci#endif 10908c2ecf20Sopenharmony_ci break; 10918c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_DEBUG_CONTROL_SUPPORT 10928c2ecf20Sopenharmony_ci case UC_SETDEBUG: 10938c2ecf20Sopenharmony_ci while (len > 0) { 10948c2ecf20Sopenharmony_ci SKIP_SPACES(ptr, len); 10958c2ecf20Sopenharmony_ci if ((arg_len = is_keyword(ptr, len, "alloc"))) 10968c2ecf20Sopenharmony_ci uc->data |= DEBUG_ALLOC; 10978c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "phase"))) 10988c2ecf20Sopenharmony_ci uc->data |= DEBUG_PHASE; 10998c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "queue"))) 11008c2ecf20Sopenharmony_ci uc->data |= DEBUG_QUEUE; 11018c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "result"))) 11028c2ecf20Sopenharmony_ci uc->data |= DEBUG_RESULT; 11038c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "scatter"))) 11048c2ecf20Sopenharmony_ci uc->data |= DEBUG_SCATTER; 11058c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "script"))) 11068c2ecf20Sopenharmony_ci uc->data |= DEBUG_SCRIPT; 11078c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "tiny"))) 11088c2ecf20Sopenharmony_ci uc->data |= DEBUG_TINY; 11098c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "timing"))) 11108c2ecf20Sopenharmony_ci uc->data |= DEBUG_TIMING; 11118c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "nego"))) 11128c2ecf20Sopenharmony_ci uc->data |= DEBUG_NEGO; 11138c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "tags"))) 11148c2ecf20Sopenharmony_ci uc->data |= DEBUG_TAGS; 11158c2ecf20Sopenharmony_ci else if ((arg_len = is_keyword(ptr, len, "pointer"))) 11168c2ecf20Sopenharmony_ci uc->data |= DEBUG_POINTER; 11178c2ecf20Sopenharmony_ci else 11188c2ecf20Sopenharmony_ci return -EINVAL; 11198c2ecf20Sopenharmony_ci ptr += arg_len; len -= arg_len; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci#ifdef DEBUG_PROC_INFO 11228c2ecf20Sopenharmony_ciprintk("sym_user_command: data=%ld\n", uc->data); 11238c2ecf20Sopenharmony_ci#endif 11248c2ecf20Sopenharmony_ci break; 11258c2ecf20Sopenharmony_ci#endif /* SYM_LINUX_DEBUG_CONTROL_SUPPORT */ 11268c2ecf20Sopenharmony_ci case UC_SETFLAG: 11278c2ecf20Sopenharmony_ci while (len > 0) { 11288c2ecf20Sopenharmony_ci SKIP_SPACES(ptr, len); 11298c2ecf20Sopenharmony_ci if ((arg_len = is_keyword(ptr, len, "no_disc"))) 11308c2ecf20Sopenharmony_ci uc->data &= ~SYM_DISC_ENABLED; 11318c2ecf20Sopenharmony_ci else 11328c2ecf20Sopenharmony_ci return -EINVAL; 11338c2ecf20Sopenharmony_ci ptr += arg_len; len -= arg_len; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci break; 11368c2ecf20Sopenharmony_ci default: 11378c2ecf20Sopenharmony_ci break; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (len) 11418c2ecf20Sopenharmony_ci return -EINVAL; 11428c2ecf20Sopenharmony_ci else { 11438c2ecf20Sopenharmony_ci unsigned long flags; 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 11468c2ecf20Sopenharmony_ci sym_exec_user_command(np, uc); 11478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci return length; 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci#endif /* SYM_LINUX_USER_COMMAND_SUPPORT */ 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci/* 11568c2ecf20Sopenharmony_ci * Copy formatted information into the input buffer. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_cistatic int sym_show_info(struct seq_file *m, struct Scsi_Host *shost) 11598c2ecf20Sopenharmony_ci{ 11608c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_USER_INFO_SUPPORT 11618c2ecf20Sopenharmony_ci struct sym_data *sym_data = shost_priv(shost); 11628c2ecf20Sopenharmony_ci struct pci_dev *pdev = sym_data->pdev; 11638c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_data->ncb; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci seq_printf(m, "Chip " NAME53C "%s, device id 0x%x, " 11668c2ecf20Sopenharmony_ci "revision id 0x%x\n", np->s.chip_name, 11678c2ecf20Sopenharmony_ci pdev->device, pdev->revision); 11688c2ecf20Sopenharmony_ci seq_printf(m, "At PCI address %s, IRQ %u\n", 11698c2ecf20Sopenharmony_ci pci_name(pdev), pdev->irq); 11708c2ecf20Sopenharmony_ci seq_printf(m, "Min. period factor %d, %s SCSI BUS%s\n", 11718c2ecf20Sopenharmony_ci (int) (np->minsync_dt ? np->minsync_dt : np->minsync), 11728c2ecf20Sopenharmony_ci np->maxwide ? "Wide" : "Narrow", 11738c2ecf20Sopenharmony_ci np->minsync_dt ? ", DT capable" : ""); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci seq_printf(m, "Max. started commands %d, " 11768c2ecf20Sopenharmony_ci "max. commands per LUN %d\n", 11778c2ecf20Sopenharmony_ci SYM_CONF_MAX_START, SYM_CONF_MAX_TAG); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci#else 11818c2ecf20Sopenharmony_ci return -EINVAL; 11828c2ecf20Sopenharmony_ci#endif /* SYM_LINUX_USER_INFO_SUPPORT */ 11838c2ecf20Sopenharmony_ci} 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci#endif /* SYM_LINUX_PROC_INFO_SUPPORT */ 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci/* 11888c2ecf20Sopenharmony_ci * Free resources claimed by sym_iomap_device(). Note that 11898c2ecf20Sopenharmony_ci * sym_free_resources() should be used instead of this function after calling 11908c2ecf20Sopenharmony_ci * sym_attach(). 11918c2ecf20Sopenharmony_ci */ 11928c2ecf20Sopenharmony_cistatic void sym_iounmap_device(struct sym_device *device) 11938c2ecf20Sopenharmony_ci{ 11948c2ecf20Sopenharmony_ci if (device->s.ioaddr) 11958c2ecf20Sopenharmony_ci pci_iounmap(device->pdev, device->s.ioaddr); 11968c2ecf20Sopenharmony_ci if (device->s.ramaddr) 11978c2ecf20Sopenharmony_ci pci_iounmap(device->pdev, device->s.ramaddr); 11988c2ecf20Sopenharmony_ci} 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci/* 12018c2ecf20Sopenharmony_ci * Free controller resources. 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_cistatic void sym_free_resources(struct sym_hcb *np, struct pci_dev *pdev, 12048c2ecf20Sopenharmony_ci int do_free_irq) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci /* 12078c2ecf20Sopenharmony_ci * Free O/S specific resources. 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci if (do_free_irq) 12108c2ecf20Sopenharmony_ci free_irq(pdev->irq, np->s.host); 12118c2ecf20Sopenharmony_ci if (np->s.ioaddr) 12128c2ecf20Sopenharmony_ci pci_iounmap(pdev, np->s.ioaddr); 12138c2ecf20Sopenharmony_ci if (np->s.ramaddr) 12148c2ecf20Sopenharmony_ci pci_iounmap(pdev, np->s.ramaddr); 12158c2ecf20Sopenharmony_ci /* 12168c2ecf20Sopenharmony_ci * Free O/S independent resources. 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_ci sym_hcb_free(np); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci sym_mfree_dma(np, sizeof(*np), "HCB"); 12218c2ecf20Sopenharmony_ci} 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci/* 12248c2ecf20Sopenharmony_ci * Host attach and initialisations. 12258c2ecf20Sopenharmony_ci * 12268c2ecf20Sopenharmony_ci * Allocate host data and ncb structure. 12278c2ecf20Sopenharmony_ci * Remap MMIO region. 12288c2ecf20Sopenharmony_ci * Do chip initialization. 12298c2ecf20Sopenharmony_ci * If all is OK, install interrupt handling and 12308c2ecf20Sopenharmony_ci * start the timer daemon. 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_cistatic struct Scsi_Host *sym_attach(struct scsi_host_template *tpnt, int unit, 12338c2ecf20Sopenharmony_ci struct sym_device *dev) 12348c2ecf20Sopenharmony_ci{ 12358c2ecf20Sopenharmony_ci struct sym_data *sym_data; 12368c2ecf20Sopenharmony_ci struct sym_hcb *np = NULL; 12378c2ecf20Sopenharmony_ci struct Scsi_Host *shost = NULL; 12388c2ecf20Sopenharmony_ci struct pci_dev *pdev = dev->pdev; 12398c2ecf20Sopenharmony_ci unsigned long flags; 12408c2ecf20Sopenharmony_ci struct sym_fw *fw; 12418c2ecf20Sopenharmony_ci int do_free_irq = 0; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci printk(KERN_INFO "sym%d: <%s> rev 0x%x at pci %s irq %u\n", 12448c2ecf20Sopenharmony_ci unit, dev->chip.name, pdev->revision, pci_name(pdev), 12458c2ecf20Sopenharmony_ci pdev->irq); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci /* 12488c2ecf20Sopenharmony_ci * Get the firmware for this chip. 12498c2ecf20Sopenharmony_ci */ 12508c2ecf20Sopenharmony_ci fw = sym_find_firmware(&dev->chip); 12518c2ecf20Sopenharmony_ci if (!fw) 12528c2ecf20Sopenharmony_ci goto attach_failed; 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci shost = scsi_host_alloc(tpnt, sizeof(*sym_data)); 12558c2ecf20Sopenharmony_ci if (!shost) 12568c2ecf20Sopenharmony_ci goto attach_failed; 12578c2ecf20Sopenharmony_ci sym_data = shost_priv(shost); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* 12608c2ecf20Sopenharmony_ci * Allocate immediately the host control block, 12618c2ecf20Sopenharmony_ci * since we are only expecting to succeed. :) 12628c2ecf20Sopenharmony_ci * We keep track in the HCB of all the resources that 12638c2ecf20Sopenharmony_ci * are to be released on error. 12648c2ecf20Sopenharmony_ci */ 12658c2ecf20Sopenharmony_ci np = __sym_calloc_dma(&pdev->dev, sizeof(*np), "HCB"); 12668c2ecf20Sopenharmony_ci if (!np) 12678c2ecf20Sopenharmony_ci goto attach_failed; 12688c2ecf20Sopenharmony_ci np->bus_dmat = &pdev->dev; /* Result in 1 DMA pool per HBA */ 12698c2ecf20Sopenharmony_ci sym_data->ncb = np; 12708c2ecf20Sopenharmony_ci sym_data->pdev = pdev; 12718c2ecf20Sopenharmony_ci np->s.host = shost; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, shost); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci /* 12768c2ecf20Sopenharmony_ci * Copy some useful infos to the HCB. 12778c2ecf20Sopenharmony_ci */ 12788c2ecf20Sopenharmony_ci np->hcb_ba = vtobus(np); 12798c2ecf20Sopenharmony_ci np->verbose = sym_driver_setup.verbose; 12808c2ecf20Sopenharmony_ci np->s.unit = unit; 12818c2ecf20Sopenharmony_ci np->features = dev->chip.features; 12828c2ecf20Sopenharmony_ci np->clock_divn = dev->chip.nr_divisor; 12838c2ecf20Sopenharmony_ci np->maxoffs = dev->chip.offset_max; 12848c2ecf20Sopenharmony_ci np->maxburst = dev->chip.burst_max; 12858c2ecf20Sopenharmony_ci np->myaddr = dev->host_id; 12868c2ecf20Sopenharmony_ci np->mmio_ba = (u32)dev->mmio_base; 12878c2ecf20Sopenharmony_ci np->ram_ba = (u32)dev->ram_base; 12888c2ecf20Sopenharmony_ci np->s.ioaddr = dev->s.ioaddr; 12898c2ecf20Sopenharmony_ci np->s.ramaddr = dev->s.ramaddr; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * Edit its name. 12938c2ecf20Sopenharmony_ci */ 12948c2ecf20Sopenharmony_ci strlcpy(np->s.chip_name, dev->chip.name, sizeof(np->s.chip_name)); 12958c2ecf20Sopenharmony_ci sprintf(np->s.inst_name, "sym%d", np->s.unit); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci if ((SYM_CONF_DMA_ADDRESSING_MODE > 0) && (np->features & FE_DAC) && 12988c2ecf20Sopenharmony_ci !dma_set_mask(&pdev->dev, DMA_DAC_MASK)) { 12998c2ecf20Sopenharmony_ci set_dac(np); 13008c2ecf20Sopenharmony_ci } else if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { 13018c2ecf20Sopenharmony_ci printf_warning("%s: No suitable DMA available\n", sym_name(np)); 13028c2ecf20Sopenharmony_ci goto attach_failed; 13038c2ecf20Sopenharmony_ci } 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (sym_hcb_attach(shost, fw, dev->nvram)) 13068c2ecf20Sopenharmony_ci goto attach_failed; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci /* 13098c2ecf20Sopenharmony_ci * Install the interrupt handler. 13108c2ecf20Sopenharmony_ci * If we synchonize the C code with SCRIPTS on interrupt, 13118c2ecf20Sopenharmony_ci * we do not want to share the INTR line at all. 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_ci if (request_irq(pdev->irq, sym53c8xx_intr, IRQF_SHARED, NAME53C8XX, 13148c2ecf20Sopenharmony_ci shost)) { 13158c2ecf20Sopenharmony_ci printf_err("%s: request irq %u failure\n", 13168c2ecf20Sopenharmony_ci sym_name(np), pdev->irq); 13178c2ecf20Sopenharmony_ci goto attach_failed; 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci do_free_irq = 1; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci /* 13228c2ecf20Sopenharmony_ci * After SCSI devices have been opened, we cannot 13238c2ecf20Sopenharmony_ci * reset the bus safely, so we do it here. 13248c2ecf20Sopenharmony_ci */ 13258c2ecf20Sopenharmony_ci spin_lock_irqsave(shost->host_lock, flags); 13268c2ecf20Sopenharmony_ci if (sym_reset_scsi_bus(np, 0)) 13278c2ecf20Sopenharmony_ci goto reset_failed; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* 13308c2ecf20Sopenharmony_ci * Start the SCRIPTS. 13318c2ecf20Sopenharmony_ci */ 13328c2ecf20Sopenharmony_ci sym_start_up(shost, 1); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci /* 13358c2ecf20Sopenharmony_ci * Start the timer daemon 13368c2ecf20Sopenharmony_ci */ 13378c2ecf20Sopenharmony_ci timer_setup(&np->s.timer, sym53c8xx_timer, 0); 13388c2ecf20Sopenharmony_ci np->s.lasttime=0; 13398c2ecf20Sopenharmony_ci sym_timer (np); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci /* 13428c2ecf20Sopenharmony_ci * Fill Linux host instance structure 13438c2ecf20Sopenharmony_ci * and return success. 13448c2ecf20Sopenharmony_ci */ 13458c2ecf20Sopenharmony_ci shost->max_channel = 0; 13468c2ecf20Sopenharmony_ci shost->this_id = np->myaddr; 13478c2ecf20Sopenharmony_ci shost->max_id = np->maxwide ? 16 : 8; 13488c2ecf20Sopenharmony_ci shost->max_lun = SYM_CONF_MAX_LUN; 13498c2ecf20Sopenharmony_ci shost->unique_id = pci_resource_start(pdev, 0); 13508c2ecf20Sopenharmony_ci shost->cmd_per_lun = SYM_CONF_MAX_TAG; 13518c2ecf20Sopenharmony_ci shost->can_queue = (SYM_CONF_MAX_START-2); 13528c2ecf20Sopenharmony_ci shost->sg_tablesize = SYM_CONF_MAX_SG; 13538c2ecf20Sopenharmony_ci shost->max_cmd_len = 16; 13548c2ecf20Sopenharmony_ci BUG_ON(sym2_transport_template == NULL); 13558c2ecf20Sopenharmony_ci shost->transportt = sym2_transport_template; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci /* 53c896 rev 1 errata: DMA may not cross 16MB boundary */ 13588c2ecf20Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && pdev->revision < 2) 13598c2ecf20Sopenharmony_ci shost->dma_boundary = 0xFFFFFF; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci return shost; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci reset_failed: 13668c2ecf20Sopenharmony_ci printf_err("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, " 13678c2ecf20Sopenharmony_ci "TERMINATION, DEVICE POWER etc.!\n", sym_name(np)); 13688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(shost->host_lock, flags); 13698c2ecf20Sopenharmony_ci attach_failed: 13708c2ecf20Sopenharmony_ci printf_info("sym%d: giving up ...\n", unit); 13718c2ecf20Sopenharmony_ci if (np) 13728c2ecf20Sopenharmony_ci sym_free_resources(np, pdev, do_free_irq); 13738c2ecf20Sopenharmony_ci else 13748c2ecf20Sopenharmony_ci sym_iounmap_device(dev); 13758c2ecf20Sopenharmony_ci if (shost) 13768c2ecf20Sopenharmony_ci scsi_host_put(shost); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci return NULL; 13798c2ecf20Sopenharmony_ci} 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci/* 13838c2ecf20Sopenharmony_ci * Detect and try to read SYMBIOS and TEKRAM NVRAM. 13848c2ecf20Sopenharmony_ci */ 13858c2ecf20Sopenharmony_ci#if SYM_CONF_NVRAM_SUPPORT 13868c2ecf20Sopenharmony_cistatic void sym_get_nvram(struct sym_device *devp, struct sym_nvram *nvp) 13878c2ecf20Sopenharmony_ci{ 13888c2ecf20Sopenharmony_ci devp->nvram = nvp; 13898c2ecf20Sopenharmony_ci nvp->type = 0; 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci sym_read_nvram(devp, nvp); 13928c2ecf20Sopenharmony_ci} 13938c2ecf20Sopenharmony_ci#else 13948c2ecf20Sopenharmony_cistatic inline void sym_get_nvram(struct sym_device *devp, struct sym_nvram *nvp) 13958c2ecf20Sopenharmony_ci{ 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci#endif /* SYM_CONF_NVRAM_SUPPORT */ 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_cistatic int sym_check_supported(struct sym_device *device) 14008c2ecf20Sopenharmony_ci{ 14018c2ecf20Sopenharmony_ci struct sym_chip *chip; 14028c2ecf20Sopenharmony_ci struct pci_dev *pdev = device->pdev; 14038c2ecf20Sopenharmony_ci unsigned long io_port = pci_resource_start(pdev, 0); 14048c2ecf20Sopenharmony_ci int i; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci /* 14078c2ecf20Sopenharmony_ci * If user excluded this chip, do not initialize it. 14088c2ecf20Sopenharmony_ci * I hate this code so much. Must kill it. 14098c2ecf20Sopenharmony_ci */ 14108c2ecf20Sopenharmony_ci if (io_port) { 14118c2ecf20Sopenharmony_ci for (i = 0 ; i < 8 ; i++) { 14128c2ecf20Sopenharmony_ci if (sym_driver_setup.excludes[i] == io_port) 14138c2ecf20Sopenharmony_ci return -ENODEV; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci /* 14188c2ecf20Sopenharmony_ci * Check if the chip is supported. Then copy the chip description 14198c2ecf20Sopenharmony_ci * to our device structure so we can make it match the actual device 14208c2ecf20Sopenharmony_ci * and options. 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_ci chip = sym_lookup_chip_table(pdev->device, pdev->revision); 14238c2ecf20Sopenharmony_ci if (!chip) { 14248c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "device not supported\n"); 14258c2ecf20Sopenharmony_ci return -ENODEV; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci memcpy(&device->chip, chip, sizeof(device->chip)); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci return 0; 14308c2ecf20Sopenharmony_ci} 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci/* 14338c2ecf20Sopenharmony_ci * Ignore Symbios chips controlled by various RAID controllers. 14348c2ecf20Sopenharmony_ci * These controllers set value 0x52414944 at RAM end - 16. 14358c2ecf20Sopenharmony_ci */ 14368c2ecf20Sopenharmony_cistatic int sym_check_raid(struct sym_device *device) 14378c2ecf20Sopenharmony_ci{ 14388c2ecf20Sopenharmony_ci unsigned int ram_size, ram_val; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci if (!device->s.ramaddr) 14418c2ecf20Sopenharmony_ci return 0; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (device->chip.features & FE_RAM8K) 14448c2ecf20Sopenharmony_ci ram_size = 8192; 14458c2ecf20Sopenharmony_ci else 14468c2ecf20Sopenharmony_ci ram_size = 4096; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci ram_val = readl(device->s.ramaddr + ram_size - 16); 14498c2ecf20Sopenharmony_ci if (ram_val != 0x52414944) 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci dev_info(&device->pdev->dev, 14538c2ecf20Sopenharmony_ci "not initializing, driven by RAID controller.\n"); 14548c2ecf20Sopenharmony_ci return -ENODEV; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic int sym_set_workarounds(struct sym_device *device) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci struct sym_chip *chip = &device->chip; 14608c2ecf20Sopenharmony_ci struct pci_dev *pdev = device->pdev; 14618c2ecf20Sopenharmony_ci u_short status_reg; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* 14648c2ecf20Sopenharmony_ci * (ITEM 12 of a DEL about the 896 I haven't yet). 14658c2ecf20Sopenharmony_ci * We must ensure the chip will use WRITE AND INVALIDATE. 14668c2ecf20Sopenharmony_ci * The revision number limit is for now arbitrary. 14678c2ecf20Sopenharmony_ci */ 14688c2ecf20Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_NCR_53C896 && pdev->revision < 0x4) { 14698c2ecf20Sopenharmony_ci chip->features |= (FE_WRIE | FE_CLSE); 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci /* If the chip can do Memory Write Invalidate, enable it */ 14738c2ecf20Sopenharmony_ci if (chip->features & FE_WRIE) { 14748c2ecf20Sopenharmony_ci if (pci_set_mwi(pdev)) 14758c2ecf20Sopenharmony_ci return -ENODEV; 14768c2ecf20Sopenharmony_ci } 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci /* 14798c2ecf20Sopenharmony_ci * Work around for errant bit in 895A. The 66Mhz 14808c2ecf20Sopenharmony_ci * capable bit is set erroneously. Clear this bit. 14818c2ecf20Sopenharmony_ci * (Item 1 DEL 533) 14828c2ecf20Sopenharmony_ci * 14838c2ecf20Sopenharmony_ci * Make sure Config space and Features agree. 14848c2ecf20Sopenharmony_ci * 14858c2ecf20Sopenharmony_ci * Recall: writes are not normal to status register - 14868c2ecf20Sopenharmony_ci * write a 1 to clear and a 0 to leave unchanged. 14878c2ecf20Sopenharmony_ci * Can only reset bits. 14888c2ecf20Sopenharmony_ci */ 14898c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_STATUS, &status_reg); 14908c2ecf20Sopenharmony_ci if (chip->features & FE_66MHZ) { 14918c2ecf20Sopenharmony_ci if (!(status_reg & PCI_STATUS_66MHZ)) 14928c2ecf20Sopenharmony_ci chip->features &= ~FE_66MHZ; 14938c2ecf20Sopenharmony_ci } else { 14948c2ecf20Sopenharmony_ci if (status_reg & PCI_STATUS_66MHZ) { 14958c2ecf20Sopenharmony_ci status_reg = PCI_STATUS_66MHZ; 14968c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_STATUS, status_reg); 14978c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_STATUS, &status_reg); 14988c2ecf20Sopenharmony_ci } 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci return 0; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci/* 15058c2ecf20Sopenharmony_ci * Map HBA registers and on-chip SRAM (if present). 15068c2ecf20Sopenharmony_ci */ 15078c2ecf20Sopenharmony_cistatic int sym_iomap_device(struct sym_device *device) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci struct pci_dev *pdev = device->pdev; 15108c2ecf20Sopenharmony_ci struct pci_bus_region bus_addr; 15118c2ecf20Sopenharmony_ci int i = 2; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci pcibios_resource_to_bus(pdev->bus, &bus_addr, &pdev->resource[1]); 15148c2ecf20Sopenharmony_ci device->mmio_base = bus_addr.start; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci if (device->chip.features & FE_RAM) { 15178c2ecf20Sopenharmony_ci /* 15188c2ecf20Sopenharmony_ci * If the BAR is 64-bit, resource 2 will be occupied by the 15198c2ecf20Sopenharmony_ci * upper 32 bits 15208c2ecf20Sopenharmony_ci */ 15218c2ecf20Sopenharmony_ci if (!pdev->resource[i].flags) 15228c2ecf20Sopenharmony_ci i++; 15238c2ecf20Sopenharmony_ci pcibios_resource_to_bus(pdev->bus, &bus_addr, 15248c2ecf20Sopenharmony_ci &pdev->resource[i]); 15258c2ecf20Sopenharmony_ci device->ram_base = bus_addr.start; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_SYM53C8XX_MMIO 15298c2ecf20Sopenharmony_ci if (device->mmio_base) 15308c2ecf20Sopenharmony_ci device->s.ioaddr = pci_iomap(pdev, 1, 15318c2ecf20Sopenharmony_ci pci_resource_len(pdev, 1)); 15328c2ecf20Sopenharmony_ci#endif 15338c2ecf20Sopenharmony_ci if (!device->s.ioaddr) 15348c2ecf20Sopenharmony_ci device->s.ioaddr = pci_iomap(pdev, 0, 15358c2ecf20Sopenharmony_ci pci_resource_len(pdev, 0)); 15368c2ecf20Sopenharmony_ci if (!device->s.ioaddr) { 15378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not map registers; giving up.\n"); 15388c2ecf20Sopenharmony_ci return -EIO; 15398c2ecf20Sopenharmony_ci } 15408c2ecf20Sopenharmony_ci if (device->ram_base) { 15418c2ecf20Sopenharmony_ci device->s.ramaddr = pci_iomap(pdev, i, 15428c2ecf20Sopenharmony_ci pci_resource_len(pdev, i)); 15438c2ecf20Sopenharmony_ci if (!device->s.ramaddr) { 15448c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 15458c2ecf20Sopenharmony_ci "could not map SRAM; continuing anyway.\n"); 15468c2ecf20Sopenharmony_ci device->ram_base = 0; 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci return 0; 15518c2ecf20Sopenharmony_ci} 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci/* 15548c2ecf20Sopenharmony_ci * The NCR PQS and PDS cards are constructed as a DEC bridge 15558c2ecf20Sopenharmony_ci * behind which sits a proprietary NCR memory controller and 15568c2ecf20Sopenharmony_ci * either four or two 53c875s as separate devices. We can tell 15578c2ecf20Sopenharmony_ci * if an 875 is part of a PQS/PDS or not since if it is, it will 15588c2ecf20Sopenharmony_ci * be on the same bus as the memory controller. In its usual 15598c2ecf20Sopenharmony_ci * mode of operation, the 875s are slaved to the memory 15608c2ecf20Sopenharmony_ci * controller for all transfers. To operate with the Linux 15618c2ecf20Sopenharmony_ci * driver, the memory controller is disabled and the 875s 15628c2ecf20Sopenharmony_ci * freed to function independently. The only wrinkle is that 15638c2ecf20Sopenharmony_ci * the preset SCSI ID (which may be zero) must be read in from 15648c2ecf20Sopenharmony_ci * a special configuration space register of the 875. 15658c2ecf20Sopenharmony_ci */ 15668c2ecf20Sopenharmony_cistatic void sym_config_pqs(struct pci_dev *pdev, struct sym_device *sym_dev) 15678c2ecf20Sopenharmony_ci{ 15688c2ecf20Sopenharmony_ci int slot; 15698c2ecf20Sopenharmony_ci u8 tmp; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci for (slot = 0; slot < 256; slot++) { 15728c2ecf20Sopenharmony_ci struct pci_dev *memc = pci_get_slot(pdev->bus, slot); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (!memc || memc->vendor != 0x101a || memc->device == 0x0009) { 15758c2ecf20Sopenharmony_ci pci_dev_put(memc); 15768c2ecf20Sopenharmony_ci continue; 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci /* bit 1: allow individual 875 configuration */ 15808c2ecf20Sopenharmony_ci pci_read_config_byte(memc, 0x44, &tmp); 15818c2ecf20Sopenharmony_ci if ((tmp & 0x2) == 0) { 15828c2ecf20Sopenharmony_ci tmp |= 0x2; 15838c2ecf20Sopenharmony_ci pci_write_config_byte(memc, 0x44, tmp); 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci /* bit 2: drive individual 875 interrupts to the bus */ 15878c2ecf20Sopenharmony_ci pci_read_config_byte(memc, 0x45, &tmp); 15888c2ecf20Sopenharmony_ci if ((tmp & 0x4) == 0) { 15898c2ecf20Sopenharmony_ci tmp |= 0x4; 15908c2ecf20Sopenharmony_ci pci_write_config_byte(memc, 0x45, tmp); 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci pci_dev_put(memc); 15948c2ecf20Sopenharmony_ci break; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci pci_read_config_byte(pdev, 0x84, &tmp); 15988c2ecf20Sopenharmony_ci sym_dev->host_id = tmp; 15998c2ecf20Sopenharmony_ci} 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci/* 16028c2ecf20Sopenharmony_ci * Called before unloading the module. 16038c2ecf20Sopenharmony_ci * Detach the host. 16048c2ecf20Sopenharmony_ci * We have to free resources and halt the NCR chip. 16058c2ecf20Sopenharmony_ci */ 16068c2ecf20Sopenharmony_cistatic int sym_detach(struct Scsi_Host *shost, struct pci_dev *pdev) 16078c2ecf20Sopenharmony_ci{ 16088c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 16098c2ecf20Sopenharmony_ci printk("%s: detaching ...\n", sym_name(np)); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci del_timer_sync(&np->s.timer); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci /* 16148c2ecf20Sopenharmony_ci * Reset NCR chip. 16158c2ecf20Sopenharmony_ci * We should use sym_soft_reset(), but we don't want to do 16168c2ecf20Sopenharmony_ci * so, since we may not be safe if interrupts occur. 16178c2ecf20Sopenharmony_ci */ 16188c2ecf20Sopenharmony_ci printk("%s: resetting chip\n", sym_name(np)); 16198c2ecf20Sopenharmony_ci OUTB(np, nc_istat, SRST); 16208c2ecf20Sopenharmony_ci INB(np, nc_mbox1); 16218c2ecf20Sopenharmony_ci udelay(10); 16228c2ecf20Sopenharmony_ci OUTB(np, nc_istat, 0); 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci sym_free_resources(np, pdev, 1); 16258c2ecf20Sopenharmony_ci scsi_host_put(shost); 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci return 1; 16288c2ecf20Sopenharmony_ci} 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci/* 16318c2ecf20Sopenharmony_ci * Driver host template. 16328c2ecf20Sopenharmony_ci */ 16338c2ecf20Sopenharmony_cistatic struct scsi_host_template sym2_template = { 16348c2ecf20Sopenharmony_ci .module = THIS_MODULE, 16358c2ecf20Sopenharmony_ci .name = "sym53c8xx", 16368c2ecf20Sopenharmony_ci .info = sym53c8xx_info, 16378c2ecf20Sopenharmony_ci .queuecommand = sym53c8xx_queue_command, 16388c2ecf20Sopenharmony_ci .slave_alloc = sym53c8xx_slave_alloc, 16398c2ecf20Sopenharmony_ci .slave_configure = sym53c8xx_slave_configure, 16408c2ecf20Sopenharmony_ci .slave_destroy = sym53c8xx_slave_destroy, 16418c2ecf20Sopenharmony_ci .eh_abort_handler = sym53c8xx_eh_abort_handler, 16428c2ecf20Sopenharmony_ci .eh_device_reset_handler = sym53c8xx_eh_device_reset_handler, 16438c2ecf20Sopenharmony_ci .eh_bus_reset_handler = sym53c8xx_eh_bus_reset_handler, 16448c2ecf20Sopenharmony_ci .eh_host_reset_handler = sym53c8xx_eh_host_reset_handler, 16458c2ecf20Sopenharmony_ci .this_id = 7, 16468c2ecf20Sopenharmony_ci .max_sectors = 0xFFFF, 16478c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_PROC_INFO_SUPPORT 16488c2ecf20Sopenharmony_ci .show_info = sym_show_info, 16498c2ecf20Sopenharmony_ci#ifdef SYM_LINUX_USER_COMMAND_SUPPORT 16508c2ecf20Sopenharmony_ci .write_info = sym_user_command, 16518c2ecf20Sopenharmony_ci#endif 16528c2ecf20Sopenharmony_ci .proc_name = NAME53C8XX, 16538c2ecf20Sopenharmony_ci#endif 16548c2ecf20Sopenharmony_ci}; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_cistatic int attach_count; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic int sym2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci struct sym_device sym_dev; 16618c2ecf20Sopenharmony_ci struct sym_nvram nvram; 16628c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 16638c2ecf20Sopenharmony_ci int do_iounmap = 0; 16648c2ecf20Sopenharmony_ci int do_disable_device = 1; 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci memset(&sym_dev, 0, sizeof(sym_dev)); 16678c2ecf20Sopenharmony_ci memset(&nvram, 0, sizeof(nvram)); 16688c2ecf20Sopenharmony_ci sym_dev.pdev = pdev; 16698c2ecf20Sopenharmony_ci sym_dev.host_id = SYM_SETUP_HOST_ID; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) 16728c2ecf20Sopenharmony_ci goto leave; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci pci_set_master(pdev); 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci if (pci_request_regions(pdev, NAME53C8XX)) 16778c2ecf20Sopenharmony_ci goto disable; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci if (sym_check_supported(&sym_dev)) 16808c2ecf20Sopenharmony_ci goto free; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (sym_iomap_device(&sym_dev)) 16838c2ecf20Sopenharmony_ci goto free; 16848c2ecf20Sopenharmony_ci do_iounmap = 1; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci if (sym_check_raid(&sym_dev)) { 16878c2ecf20Sopenharmony_ci do_disable_device = 0; /* Don't disable the device */ 16888c2ecf20Sopenharmony_ci goto free; 16898c2ecf20Sopenharmony_ci } 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci if (sym_set_workarounds(&sym_dev)) 16928c2ecf20Sopenharmony_ci goto free; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci sym_config_pqs(pdev, &sym_dev); 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci sym_get_nvram(&sym_dev, &nvram); 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci do_iounmap = 0; /* Don't sym_iounmap_device() after sym_attach(). */ 16998c2ecf20Sopenharmony_ci shost = sym_attach(&sym2_template, attach_count, &sym_dev); 17008c2ecf20Sopenharmony_ci if (!shost) 17018c2ecf20Sopenharmony_ci goto free; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci if (scsi_add_host(shost, &pdev->dev)) 17048c2ecf20Sopenharmony_ci goto detach; 17058c2ecf20Sopenharmony_ci scsi_scan_host(shost); 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci attach_count++; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci return 0; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci detach: 17128c2ecf20Sopenharmony_ci sym_detach(pci_get_drvdata(pdev), pdev); 17138c2ecf20Sopenharmony_ci free: 17148c2ecf20Sopenharmony_ci if (do_iounmap) 17158c2ecf20Sopenharmony_ci sym_iounmap_device(&sym_dev); 17168c2ecf20Sopenharmony_ci pci_release_regions(pdev); 17178c2ecf20Sopenharmony_ci disable: 17188c2ecf20Sopenharmony_ci if (do_disable_device) 17198c2ecf20Sopenharmony_ci pci_disable_device(pdev); 17208c2ecf20Sopenharmony_ci leave: 17218c2ecf20Sopenharmony_ci return -ENODEV; 17228c2ecf20Sopenharmony_ci} 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_cistatic void sym2_remove(struct pci_dev *pdev) 17258c2ecf20Sopenharmony_ci{ 17268c2ecf20Sopenharmony_ci struct Scsi_Host *shost = pci_get_drvdata(pdev); 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci scsi_remove_host(shost); 17298c2ecf20Sopenharmony_ci sym_detach(shost, pdev); 17308c2ecf20Sopenharmony_ci pci_release_regions(pdev); 17318c2ecf20Sopenharmony_ci pci_disable_device(pdev); 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci attach_count--; 17348c2ecf20Sopenharmony_ci} 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci/** 17378c2ecf20Sopenharmony_ci * sym2_io_error_detected() - called when PCI error is detected 17388c2ecf20Sopenharmony_ci * @pdev: pointer to PCI device 17398c2ecf20Sopenharmony_ci * @state: current state of the PCI slot 17408c2ecf20Sopenharmony_ci */ 17418c2ecf20Sopenharmony_cistatic pci_ers_result_t sym2_io_error_detected(struct pci_dev *pdev, 17428c2ecf20Sopenharmony_ci pci_channel_state_t state) 17438c2ecf20Sopenharmony_ci{ 17448c2ecf20Sopenharmony_ci /* If slot is permanently frozen, turn everything off */ 17458c2ecf20Sopenharmony_ci if (state == pci_channel_io_perm_failure) { 17468c2ecf20Sopenharmony_ci sym2_remove(pdev); 17478c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci disable_irq(pdev->irq); 17518c2ecf20Sopenharmony_ci pci_disable_device(pdev); 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci /* Request that MMIO be enabled, so register dump can be taken. */ 17548c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_CAN_RECOVER; 17558c2ecf20Sopenharmony_ci} 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci/** 17588c2ecf20Sopenharmony_ci * sym2_io_slot_dump - Enable MMIO and dump debug registers 17598c2ecf20Sopenharmony_ci * @pdev: pointer to PCI device 17608c2ecf20Sopenharmony_ci */ 17618c2ecf20Sopenharmony_cistatic pci_ers_result_t sym2_io_slot_dump(struct pci_dev *pdev) 17628c2ecf20Sopenharmony_ci{ 17638c2ecf20Sopenharmony_ci struct Scsi_Host *shost = pci_get_drvdata(pdev); 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci sym_dump_registers(shost); 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci /* Request a slot reset. */ 17688c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 17698c2ecf20Sopenharmony_ci} 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci/** 17728c2ecf20Sopenharmony_ci * sym2_reset_workarounds - hardware-specific work-arounds 17738c2ecf20Sopenharmony_ci * @pdev: pointer to PCI device 17748c2ecf20Sopenharmony_ci * 17758c2ecf20Sopenharmony_ci * This routine is similar to sym_set_workarounds(), except 17768c2ecf20Sopenharmony_ci * that, at this point, we already know that the device was 17778c2ecf20Sopenharmony_ci * successfully initialized at least once before, and so most 17788c2ecf20Sopenharmony_ci * of the steps taken there are un-needed here. 17798c2ecf20Sopenharmony_ci */ 17808c2ecf20Sopenharmony_cistatic void sym2_reset_workarounds(struct pci_dev *pdev) 17818c2ecf20Sopenharmony_ci{ 17828c2ecf20Sopenharmony_ci u_short status_reg; 17838c2ecf20Sopenharmony_ci struct sym_chip *chip; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci chip = sym_lookup_chip_table(pdev->device, pdev->revision); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* Work around for errant bit in 895A, in a fashion 17888c2ecf20Sopenharmony_ci * similar to what is done in sym_set_workarounds(). 17898c2ecf20Sopenharmony_ci */ 17908c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_STATUS, &status_reg); 17918c2ecf20Sopenharmony_ci if (!(chip->features & FE_66MHZ) && (status_reg & PCI_STATUS_66MHZ)) { 17928c2ecf20Sopenharmony_ci status_reg = PCI_STATUS_66MHZ; 17938c2ecf20Sopenharmony_ci pci_write_config_word(pdev, PCI_STATUS, status_reg); 17948c2ecf20Sopenharmony_ci pci_read_config_word(pdev, PCI_STATUS, &status_reg); 17958c2ecf20Sopenharmony_ci } 17968c2ecf20Sopenharmony_ci} 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci/** 17998c2ecf20Sopenharmony_ci * sym2_io_slot_reset() - called when the pci bus has been reset. 18008c2ecf20Sopenharmony_ci * @pdev: pointer to PCI device 18018c2ecf20Sopenharmony_ci * 18028c2ecf20Sopenharmony_ci * Restart the card from scratch. 18038c2ecf20Sopenharmony_ci */ 18048c2ecf20Sopenharmony_cistatic pci_ers_result_t sym2_io_slot_reset(struct pci_dev *pdev) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci struct Scsi_Host *shost = pci_get_drvdata(pdev); 18078c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: recovering from a PCI slot reset\n", 18108c2ecf20Sopenharmony_ci sym_name(np)); 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci if (pci_enable_device(pdev)) { 18138c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Unable to enable after PCI reset\n", 18148c2ecf20Sopenharmony_ci sym_name(np)); 18158c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 18168c2ecf20Sopenharmony_ci } 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci pci_set_master(pdev); 18198c2ecf20Sopenharmony_ci enable_irq(pdev->irq); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci /* If the chip can do Memory Write Invalidate, enable it */ 18228c2ecf20Sopenharmony_ci if (np->features & FE_WRIE) { 18238c2ecf20Sopenharmony_ci if (pci_set_mwi(pdev)) 18248c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci /* Perform work-arounds, analogous to sym_set_workarounds() */ 18288c2ecf20Sopenharmony_ci sym2_reset_workarounds(pdev); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* Perform host reset only on one instance of the card */ 18318c2ecf20Sopenharmony_ci if (PCI_FUNC(pdev->devfn) == 0) { 18328c2ecf20Sopenharmony_ci if (sym_reset_scsi_bus(np, 0)) { 18338c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: Unable to reset scsi host\n", 18348c2ecf20Sopenharmony_ci sym_name(np)); 18358c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 18368c2ecf20Sopenharmony_ci } 18378c2ecf20Sopenharmony_ci sym_start_up(shost, 1); 18388c2ecf20Sopenharmony_ci } 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 18418c2ecf20Sopenharmony_ci} 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci/** 18448c2ecf20Sopenharmony_ci * sym2_io_resume() - resume normal ops after PCI reset 18458c2ecf20Sopenharmony_ci * @pdev: pointer to PCI device 18468c2ecf20Sopenharmony_ci * 18478c2ecf20Sopenharmony_ci * Called when the error recovery driver tells us that its 18488c2ecf20Sopenharmony_ci * OK to resume normal operation. Use completion to allow 18498c2ecf20Sopenharmony_ci * halted scsi ops to resume. 18508c2ecf20Sopenharmony_ci */ 18518c2ecf20Sopenharmony_cistatic void sym2_io_resume(struct pci_dev *pdev) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci struct Scsi_Host *shost = pci_get_drvdata(pdev); 18548c2ecf20Sopenharmony_ci struct sym_data *sym_data = shost_priv(shost); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci spin_lock_irq(shost->host_lock); 18578c2ecf20Sopenharmony_ci if (sym_data->io_reset) 18588c2ecf20Sopenharmony_ci complete(sym_data->io_reset); 18598c2ecf20Sopenharmony_ci spin_unlock_irq(shost->host_lock); 18608c2ecf20Sopenharmony_ci} 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_cistatic void sym2_get_signalling(struct Scsi_Host *shost) 18638c2ecf20Sopenharmony_ci{ 18648c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 18658c2ecf20Sopenharmony_ci enum spi_signal_type type; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci switch (np->scsi_mode) { 18688c2ecf20Sopenharmony_ci case SMODE_SE: 18698c2ecf20Sopenharmony_ci type = SPI_SIGNAL_SE; 18708c2ecf20Sopenharmony_ci break; 18718c2ecf20Sopenharmony_ci case SMODE_LVD: 18728c2ecf20Sopenharmony_ci type = SPI_SIGNAL_LVD; 18738c2ecf20Sopenharmony_ci break; 18748c2ecf20Sopenharmony_ci case SMODE_HVD: 18758c2ecf20Sopenharmony_ci type = SPI_SIGNAL_HVD; 18768c2ecf20Sopenharmony_ci break; 18778c2ecf20Sopenharmony_ci default: 18788c2ecf20Sopenharmony_ci type = SPI_SIGNAL_UNKNOWN; 18798c2ecf20Sopenharmony_ci break; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci spi_signalling(shost) = type; 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_cistatic void sym2_set_offset(struct scsi_target *starget, int offset) 18858c2ecf20Sopenharmony_ci{ 18868c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 18878c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 18888c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[starget->id]; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci tp->tgoal.offset = offset; 18918c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 18928c2ecf20Sopenharmony_ci} 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_cistatic void sym2_set_period(struct scsi_target *starget, int period) 18958c2ecf20Sopenharmony_ci{ 18968c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 18978c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 18988c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[starget->id]; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci /* have to have DT for these transfers, but DT will also 19018c2ecf20Sopenharmony_ci * set width, so check that this is allowed */ 19028c2ecf20Sopenharmony_ci if (period <= np->minsync && spi_width(starget)) 19038c2ecf20Sopenharmony_ci tp->tgoal.dt = 1; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci tp->tgoal.period = period; 19068c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 19078c2ecf20Sopenharmony_ci} 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_cistatic void sym2_set_width(struct scsi_target *starget, int width) 19108c2ecf20Sopenharmony_ci{ 19118c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 19128c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 19138c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[starget->id]; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci /* It is illegal to have DT set on narrow transfers. If DT is 19168c2ecf20Sopenharmony_ci * clear, we must also clear IU and QAS. */ 19178c2ecf20Sopenharmony_ci if (width == 0) 19188c2ecf20Sopenharmony_ci tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci tp->tgoal.width = width; 19218c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic void sym2_set_dt(struct scsi_target *starget, int dt) 19258c2ecf20Sopenharmony_ci{ 19268c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 19278c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 19288c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[starget->id]; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci /* We must clear QAS and IU if DT is clear */ 19318c2ecf20Sopenharmony_ci if (dt) 19328c2ecf20Sopenharmony_ci tp->tgoal.dt = 1; 19338c2ecf20Sopenharmony_ci else 19348c2ecf20Sopenharmony_ci tp->tgoal.iu = tp->tgoal.dt = tp->tgoal.qas = 0; 19358c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 19368c2ecf20Sopenharmony_ci} 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci#if 0 19398c2ecf20Sopenharmony_cistatic void sym2_set_iu(struct scsi_target *starget, int iu) 19408c2ecf20Sopenharmony_ci{ 19418c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 19428c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 19438c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[starget->id]; 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci if (iu) 19468c2ecf20Sopenharmony_ci tp->tgoal.iu = tp->tgoal.dt = 1; 19478c2ecf20Sopenharmony_ci else 19488c2ecf20Sopenharmony_ci tp->tgoal.iu = 0; 19498c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 19508c2ecf20Sopenharmony_ci} 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_cistatic void sym2_set_qas(struct scsi_target *starget, int qas) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 19558c2ecf20Sopenharmony_ci struct sym_hcb *np = sym_get_hcb(shost); 19568c2ecf20Sopenharmony_ci struct sym_tcb *tp = &np->target[starget->id]; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci if (qas) 19598c2ecf20Sopenharmony_ci tp->tgoal.dt = tp->tgoal.qas = 1; 19608c2ecf20Sopenharmony_ci else 19618c2ecf20Sopenharmony_ci tp->tgoal.qas = 0; 19628c2ecf20Sopenharmony_ci tp->tgoal.check_nego = 1; 19638c2ecf20Sopenharmony_ci} 19648c2ecf20Sopenharmony_ci#endif 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_cistatic struct spi_function_template sym2_transport_functions = { 19678c2ecf20Sopenharmony_ci .set_offset = sym2_set_offset, 19688c2ecf20Sopenharmony_ci .show_offset = 1, 19698c2ecf20Sopenharmony_ci .set_period = sym2_set_period, 19708c2ecf20Sopenharmony_ci .show_period = 1, 19718c2ecf20Sopenharmony_ci .set_width = sym2_set_width, 19728c2ecf20Sopenharmony_ci .show_width = 1, 19738c2ecf20Sopenharmony_ci .set_dt = sym2_set_dt, 19748c2ecf20Sopenharmony_ci .show_dt = 1, 19758c2ecf20Sopenharmony_ci#if 0 19768c2ecf20Sopenharmony_ci .set_iu = sym2_set_iu, 19778c2ecf20Sopenharmony_ci .show_iu = 1, 19788c2ecf20Sopenharmony_ci .set_qas = sym2_set_qas, 19798c2ecf20Sopenharmony_ci .show_qas = 1, 19808c2ecf20Sopenharmony_ci#endif 19818c2ecf20Sopenharmony_ci .get_signalling = sym2_get_signalling, 19828c2ecf20Sopenharmony_ci}; 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_cistatic struct pci_device_id sym2_id_table[] = { 19858c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C810, 19868c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 19878c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C820, 19888c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */ 19898c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C825, 19908c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 19918c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C815, 19928c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 19938c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C810AP, 19948c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, /* new */ 19958c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C860, 19968c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 19978c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1510, 19988c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SCSI<<8, 0xffff00, 0UL }, 19998c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C896, 20008c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20018c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C895, 20028c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20038c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C885, 20048c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20058c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875, 20068c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20078c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C1510, 20088c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_SCSI<<8, 0xffff00, 0UL }, /* new */ 20098c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C895A, 20108c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20118c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C875A, 20128c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20138c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1010_33, 20148c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20158c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1010_66, 20168c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20178c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_NCR_53C875J, 20188c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 20198c2ecf20Sopenharmony_ci { 0, } 20208c2ecf20Sopenharmony_ci}; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, sym2_id_table); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_cistatic const struct pci_error_handlers sym2_err_handler = { 20258c2ecf20Sopenharmony_ci .error_detected = sym2_io_error_detected, 20268c2ecf20Sopenharmony_ci .mmio_enabled = sym2_io_slot_dump, 20278c2ecf20Sopenharmony_ci .slot_reset = sym2_io_slot_reset, 20288c2ecf20Sopenharmony_ci .resume = sym2_io_resume, 20298c2ecf20Sopenharmony_ci}; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_cistatic struct pci_driver sym2_driver = { 20328c2ecf20Sopenharmony_ci .name = NAME53C8XX, 20338c2ecf20Sopenharmony_ci .id_table = sym2_id_table, 20348c2ecf20Sopenharmony_ci .probe = sym2_probe, 20358c2ecf20Sopenharmony_ci .remove = sym2_remove, 20368c2ecf20Sopenharmony_ci .err_handler = &sym2_err_handler, 20378c2ecf20Sopenharmony_ci}; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_cistatic int __init sym2_init(void) 20408c2ecf20Sopenharmony_ci{ 20418c2ecf20Sopenharmony_ci int error; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci sym2_setup_params(); 20448c2ecf20Sopenharmony_ci sym2_transport_template = spi_attach_transport(&sym2_transport_functions); 20458c2ecf20Sopenharmony_ci if (!sym2_transport_template) 20468c2ecf20Sopenharmony_ci return -ENODEV; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci error = pci_register_driver(&sym2_driver); 20498c2ecf20Sopenharmony_ci if (error) 20508c2ecf20Sopenharmony_ci spi_release_transport(sym2_transport_template); 20518c2ecf20Sopenharmony_ci return error; 20528c2ecf20Sopenharmony_ci} 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_cistatic void __exit sym2_exit(void) 20558c2ecf20Sopenharmony_ci{ 20568c2ecf20Sopenharmony_ci pci_unregister_driver(&sym2_driver); 20578c2ecf20Sopenharmony_ci spi_release_transport(sym2_transport_template); 20588c2ecf20Sopenharmony_ci} 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_cimodule_init(sym2_init); 20618c2ecf20Sopenharmony_cimodule_exit(sym2_exit); 2062