18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Adaptec AIC79xx device driver for Linux. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * $Id: //depot/aic7xxx/linux/drivers/scsi/aic7xxx/aic79xx_osm.c#171 $ 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- 78c2ecf20Sopenharmony_ci * Copyright (c) 1994-2000 Justin T. Gibbs. 88c2ecf20Sopenharmony_ci * Copyright (c) 1997-1999 Doug Ledford 98c2ecf20Sopenharmony_ci * Copyright (c) 2000-2003 Adaptec Inc. 108c2ecf20Sopenharmony_ci * All rights reserved. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 138c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 148c2ecf20Sopenharmony_ci * are met: 158c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 168c2ecf20Sopenharmony_ci * notice, this list of conditions, and the following disclaimer, 178c2ecf20Sopenharmony_ci * without modification. 188c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce at minimum a disclaimer 198c2ecf20Sopenharmony_ci * substantially similar to the "NO WARRANTY" disclaimer below 208c2ecf20Sopenharmony_ci * ("Disclaimer") and any redistribution must be conditioned upon 218c2ecf20Sopenharmony_ci * including a substantially similar Disclaimer requirement for further 228c2ecf20Sopenharmony_ci * binary redistribution. 238c2ecf20Sopenharmony_ci * 3. Neither the names of the above-listed copyright holders nor the names 248c2ecf20Sopenharmony_ci * of any contributors may be used to endorse or promote products derived 258c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the 288c2ecf20Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free 298c2ecf20Sopenharmony_ci * Software Foundation. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * NO WARRANTY 328c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 338c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 348c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 358c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 368c2ecf20Sopenharmony_ci * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 378c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 388c2ecf20Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 398c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 408c2ecf20Sopenharmony_ci * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 418c2ecf20Sopenharmony_ci * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 428c2ecf20Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGES. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include "aic79xx_osm.h" 468c2ecf20Sopenharmony_ci#include "aic79xx_inline.h" 478c2ecf20Sopenharmony_ci#include <scsi/scsicam.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct scsi_transport_template *ahd_linux_transport_template = NULL; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <linux/init.h> /* __setup */ 528c2ecf20Sopenharmony_ci#include <linux/mm.h> /* For fetching system memory size */ 538c2ecf20Sopenharmony_ci#include <linux/blkdev.h> /* For block_size() */ 548c2ecf20Sopenharmony_ci#include <linux/delay.h> /* For ssleep/msleep */ 558c2ecf20Sopenharmony_ci#include <linux/device.h> 568c2ecf20Sopenharmony_ci#include <linux/slab.h> 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * Bucket size for counting good commands in between bad ones. 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci#define AHD_LINUX_ERR_THRESH 1000 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* 648c2ecf20Sopenharmony_ci * Set this to the delay in seconds after SCSI bus reset. 658c2ecf20Sopenharmony_ci * Note, we honor this only for the initial bus reset. 668c2ecf20Sopenharmony_ci * The scsi error recovery code performs its own bus settle 678c2ecf20Sopenharmony_ci * delay handling for error recovery actions. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci#ifdef CONFIG_AIC79XX_RESET_DELAY_MS 708c2ecf20Sopenharmony_ci#define AIC79XX_RESET_DELAY CONFIG_AIC79XX_RESET_DELAY_MS 718c2ecf20Sopenharmony_ci#else 728c2ecf20Sopenharmony_ci#define AIC79XX_RESET_DELAY 5000 738c2ecf20Sopenharmony_ci#endif 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* 768c2ecf20Sopenharmony_ci * To change the default number of tagged transactions allowed per-device, 778c2ecf20Sopenharmony_ci * add a line to the lilo.conf file like: 788c2ecf20Sopenharmony_ci * append="aic79xx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}" 798c2ecf20Sopenharmony_ci * which will result in the first four devices on the first two 808c2ecf20Sopenharmony_ci * controllers being set to a tagged queue depth of 32. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * The tag_commands is an array of 16 to allow for wide and twin adapters. 838c2ecf20Sopenharmony_ci * Twin adapters will use indexes 0-7 for channel 0, and indexes 8-15 848c2ecf20Sopenharmony_ci * for channel 1. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_citypedef struct { 878c2ecf20Sopenharmony_ci uint16_t tag_commands[16]; /* Allow for wide/twin adapters. */ 888c2ecf20Sopenharmony_ci} adapter_tag_info_t; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* 918c2ecf20Sopenharmony_ci * Modify this as you see fit for your system. 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * 0 tagged queuing disabled 948c2ecf20Sopenharmony_ci * 1 <= n <= 253 n == max tags ever dispatched. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * The driver will throttle the number of commands dispatched to a 978c2ecf20Sopenharmony_ci * device if it returns queue full. For devices with a fixed maximum 988c2ecf20Sopenharmony_ci * queue depth, the driver will eventually determine this depth and 998c2ecf20Sopenharmony_ci * lock it in (a console message is printed to indicate that a lock 1008c2ecf20Sopenharmony_ci * has occurred). On some devices, queue full is returned for a temporary 1018c2ecf20Sopenharmony_ci * resource shortage. These devices will return queue full at varying 1028c2ecf20Sopenharmony_ci * depths. The driver will throttle back when the queue fulls occur and 1038c2ecf20Sopenharmony_ci * attempt to slowly increase the depth over time as the device recovers 1048c2ecf20Sopenharmony_ci * from the resource shortage. 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * In this example, the first line will disable tagged queueing for all 1078c2ecf20Sopenharmony_ci * the devices on the first probed aic79xx adapter. 1088c2ecf20Sopenharmony_ci * 1098c2ecf20Sopenharmony_ci * The second line enables tagged queueing with 4 commands/LUN for IDs 1108c2ecf20Sopenharmony_ci * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the 1118c2ecf20Sopenharmony_ci * driver to attempt to use up to 64 tags for ID 1. 1128c2ecf20Sopenharmony_ci * 1138c2ecf20Sopenharmony_ci * The third line is the same as the first line. 1148c2ecf20Sopenharmony_ci * 1158c2ecf20Sopenharmony_ci * The fourth line disables tagged queueing for devices 0 and 3. It 1168c2ecf20Sopenharmony_ci * enables tagged queueing for the other IDs, with 16 commands/LUN 1178c2ecf20Sopenharmony_ci * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for 1188c2ecf20Sopenharmony_ci * IDs 2, 5-7, and 9-15. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* 1228c2ecf20Sopenharmony_ci * NOTE: The below structure is for reference only, the actual structure 1238c2ecf20Sopenharmony_ci * to modify in order to change things is just below this comment block. 1248c2ecf20Sopenharmony_ciadapter_tag_info_t aic79xx_tag_info[] = 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 1278c2ecf20Sopenharmony_ci {{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}}, 1288c2ecf20Sopenharmony_ci {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 1298c2ecf20Sopenharmony_ci {{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}} 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci*/ 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#ifdef CONFIG_AIC79XX_CMDS_PER_DEVICE 1348c2ecf20Sopenharmony_ci#define AIC79XX_CMDS_PER_DEVICE CONFIG_AIC79XX_CMDS_PER_DEVICE 1358c2ecf20Sopenharmony_ci#else 1368c2ecf20Sopenharmony_ci#define AIC79XX_CMDS_PER_DEVICE AHD_MAX_QUEUE 1378c2ecf20Sopenharmony_ci#endif 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define AIC79XX_CONFIGED_TAG_COMMANDS { \ 1408c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \ 1418c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \ 1428c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \ 1438c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \ 1448c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \ 1458c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \ 1468c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE, \ 1478c2ecf20Sopenharmony_ci AIC79XX_CMDS_PER_DEVICE, AIC79XX_CMDS_PER_DEVICE \ 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* 1518c2ecf20Sopenharmony_ci * By default, use the number of commands specified by 1528c2ecf20Sopenharmony_ci * the users kernel configuration. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic adapter_tag_info_t aic79xx_tag_info[] = 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1578c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1588c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1598c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1608c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1618c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1628c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1638c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1648c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1658c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1668c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1678c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1688c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1698c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1708c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS}, 1718c2ecf20Sopenharmony_ci {AIC79XX_CONFIGED_TAG_COMMANDS} 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * The I/O cell on the chip is very configurable in respect to its analog 1768c2ecf20Sopenharmony_ci * characteristics. Set the defaults here; they can be overriden with 1778c2ecf20Sopenharmony_ci * the proper insmod parameters. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistruct ahd_linux_iocell_opts 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci uint8_t precomp; 1828c2ecf20Sopenharmony_ci uint8_t slewrate; 1838c2ecf20Sopenharmony_ci uint8_t amplitude; 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci#define AIC79XX_DEFAULT_PRECOMP 0xFF 1868c2ecf20Sopenharmony_ci#define AIC79XX_DEFAULT_SLEWRATE 0xFF 1878c2ecf20Sopenharmony_ci#define AIC79XX_DEFAULT_AMPLITUDE 0xFF 1888c2ecf20Sopenharmony_ci#define AIC79XX_DEFAULT_IOOPTS \ 1898c2ecf20Sopenharmony_ci{ \ 1908c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_PRECOMP, \ 1918c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_SLEWRATE, \ 1928c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_AMPLITUDE \ 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci#define AIC79XX_PRECOMP_INDEX 0 1958c2ecf20Sopenharmony_ci#define AIC79XX_SLEWRATE_INDEX 1 1968c2ecf20Sopenharmony_ci#define AIC79XX_AMPLITUDE_INDEX 2 1978c2ecf20Sopenharmony_cistatic const struct ahd_linux_iocell_opts aic79xx_iocell_info[] = 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2008c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2018c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2028c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2038c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2048c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2058c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2068c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2078c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2088c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2098c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2108c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2118c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2128c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2138c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS, 2148c2ecf20Sopenharmony_ci AIC79XX_DEFAULT_IOOPTS 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* 2188c2ecf20Sopenharmony_ci * There should be a specific return value for this in scsi.h, but 2198c2ecf20Sopenharmony_ci * it seems that most drivers ignore it. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ci#define DID_UNDERFLOW DID_ERROR 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid 2248c2ecf20Sopenharmony_ciahd_print_path(struct ahd_softc *ahd, struct scb *scb) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci printk("(scsi%d:%c:%d:%d): ", 2278c2ecf20Sopenharmony_ci ahd->platform_data->host->host_no, 2288c2ecf20Sopenharmony_ci scb != NULL ? SCB_GET_CHANNEL(ahd, scb) : 'X', 2298c2ecf20Sopenharmony_ci scb != NULL ? SCB_GET_TARGET(ahd, scb) : -1, 2308c2ecf20Sopenharmony_ci scb != NULL ? SCB_GET_LUN(scb) : -1); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * XXX - these options apply unilaterally to _all_ adapters 2358c2ecf20Sopenharmony_ci * cards in the system. This should be fixed. Exceptions to this 2368c2ecf20Sopenharmony_ci * rule are noted in the comments. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* 2408c2ecf20Sopenharmony_ci * Skip the scsi bus reset. Non 0 make us skip the reset at startup. This 2418c2ecf20Sopenharmony_ci * has no effect on any later resets that might occur due to things like 2428c2ecf20Sopenharmony_ci * SCSI bus timeouts. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_cistatic uint32_t aic79xx_no_reset; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/* 2478c2ecf20Sopenharmony_ci * Should we force EXTENDED translation on a controller. 2488c2ecf20Sopenharmony_ci * 0 == Use whatever is in the SEEPROM or default to off 2498c2ecf20Sopenharmony_ci * 1 == Use whatever is in the SEEPROM or default to on 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic uint32_t aic79xx_extended; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* 2548c2ecf20Sopenharmony_ci * PCI bus parity checking of the Adaptec controllers. This is somewhat 2558c2ecf20Sopenharmony_ci * dubious at best. To my knowledge, this option has never actually 2568c2ecf20Sopenharmony_ci * solved a PCI parity problem, but on certain machines with broken PCI 2578c2ecf20Sopenharmony_ci * chipset configurations, it can generate tons of false error messages. 2588c2ecf20Sopenharmony_ci * It's included in the driver for completeness. 2598c2ecf20Sopenharmony_ci * 0 = Shut off PCI parity check 2608c2ecf20Sopenharmony_ci * non-0 = Enable PCI parity check 2618c2ecf20Sopenharmony_ci * 2628c2ecf20Sopenharmony_ci * NOTE: you can't actually pass -1 on the lilo prompt. So, to set this 2638c2ecf20Sopenharmony_ci * variable to -1 you would actually want to simply pass the variable 2648c2ecf20Sopenharmony_ci * name without a number. That will invert the 0 which will result in 2658c2ecf20Sopenharmony_ci * -1. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_cistatic uint32_t aic79xx_pci_parity = ~0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * There are lots of broken chipsets in the world. Some of them will 2718c2ecf20Sopenharmony_ci * violate the PCI spec when we issue byte sized memory writes to our 2728c2ecf20Sopenharmony_ci * controller. I/O mapped register access, if allowed by the given 2738c2ecf20Sopenharmony_ci * platform, will work in almost all cases. 2748c2ecf20Sopenharmony_ci */ 2758c2ecf20Sopenharmony_ciuint32_t aic79xx_allow_memio = ~0; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * So that we can set how long each device is given as a selection timeout. 2798c2ecf20Sopenharmony_ci * The table of values goes like this: 2808c2ecf20Sopenharmony_ci * 0 - 256ms 2818c2ecf20Sopenharmony_ci * 1 - 128ms 2828c2ecf20Sopenharmony_ci * 2 - 64ms 2838c2ecf20Sopenharmony_ci * 3 - 32ms 2848c2ecf20Sopenharmony_ci * We default to 256ms because some older devices need a longer time 2858c2ecf20Sopenharmony_ci * to respond to initial selection. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_cistatic uint32_t aic79xx_seltime; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * Certain devices do not perform any aging on commands. Should the 2918c2ecf20Sopenharmony_ci * device be saturated by commands in one portion of the disk, it is 2928c2ecf20Sopenharmony_ci * possible for transactions on far away sectors to never be serviced. 2938c2ecf20Sopenharmony_ci * To handle these devices, we can periodically send an ordered tag to 2948c2ecf20Sopenharmony_ci * force all outstanding transactions to be serviced prior to a new 2958c2ecf20Sopenharmony_ci * transaction. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_cistatic uint32_t aic79xx_periodic_otag; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* Some storage boxes are using an LSI chip which has a bug making it 3008c2ecf20Sopenharmony_ci * impossible to use aic79xx Rev B chip in 320 speeds. The following 3018c2ecf20Sopenharmony_ci * storage boxes have been reported to be buggy: 3028c2ecf20Sopenharmony_ci * EonStor 3U 16-Bay: U16U-G3A3 3038c2ecf20Sopenharmony_ci * EonStor 2U 12-Bay: U12U-G3A3 3048c2ecf20Sopenharmony_ci * SentinelRAID: 2500F R5 / R6 3058c2ecf20Sopenharmony_ci * SentinelRAID: 2500F R1 3068c2ecf20Sopenharmony_ci * SentinelRAID: 2500F/1500F 3078c2ecf20Sopenharmony_ci * SentinelRAID: 150F 3088c2ecf20Sopenharmony_ci * 3098c2ecf20Sopenharmony_ci * To get around this LSI bug, you can set your board to 160 mode 3108c2ecf20Sopenharmony_ci * or you can enable the SLOWCRC bit. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ciuint32_t aic79xx_slowcrc; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* 3158c2ecf20Sopenharmony_ci * Module information and settable options. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic char *aic79xx = NULL; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maintainer: Hannes Reinecke <hare@suse.de>"); 3208c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Adaptec AIC790X U320 SCSI Host Bus Adapter driver"); 3218c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 3228c2ecf20Sopenharmony_ciMODULE_VERSION(AIC79XX_DRIVER_VERSION); 3238c2ecf20Sopenharmony_cimodule_param(aic79xx, charp, 0444); 3248c2ecf20Sopenharmony_ciMODULE_PARM_DESC(aic79xx, 3258c2ecf20Sopenharmony_ci"period-delimited options string:\n" 3268c2ecf20Sopenharmony_ci" verbose Enable verbose/diagnostic logging\n" 3278c2ecf20Sopenharmony_ci" allow_memio Allow device registers to be memory mapped\n" 3288c2ecf20Sopenharmony_ci" debug Bitmask of debug values to enable\n" 3298c2ecf20Sopenharmony_ci" no_reset Suppress initial bus resets\n" 3308c2ecf20Sopenharmony_ci" extended Enable extended geometry on all controllers\n" 3318c2ecf20Sopenharmony_ci" periodic_otag Send an ordered tagged transaction\n" 3328c2ecf20Sopenharmony_ci" periodically to prevent tag starvation.\n" 3338c2ecf20Sopenharmony_ci" This may be required by some older disk\n" 3348c2ecf20Sopenharmony_ci" or drives/RAID arrays.\n" 3358c2ecf20Sopenharmony_ci" tag_info:<tag_str> Set per-target tag depth\n" 3368c2ecf20Sopenharmony_ci" global_tag_depth:<int> Global tag depth for all targets on all buses\n" 3378c2ecf20Sopenharmony_ci" slewrate:<slewrate_list>Set the signal slew rate (0-15).\n" 3388c2ecf20Sopenharmony_ci" precomp:<pcomp_list> Set the signal precompensation (0-7).\n" 3398c2ecf20Sopenharmony_ci" amplitude:<int> Set the signal amplitude (0-7).\n" 3408c2ecf20Sopenharmony_ci" seltime:<int> Selection Timeout:\n" 3418c2ecf20Sopenharmony_ci" (0/256ms,1/128ms,2/64ms,3/32ms)\n" 3428c2ecf20Sopenharmony_ci" slowcrc Turn on the SLOWCRC bit (Rev B only)\n" 3438c2ecf20Sopenharmony_ci"\n" 3448c2ecf20Sopenharmony_ci" Sample modprobe configuration file:\n" 3458c2ecf20Sopenharmony_ci" # Enable verbose logging\n" 3468c2ecf20Sopenharmony_ci" # Set tag depth on Controller 2/Target 2 to 10 tags\n" 3478c2ecf20Sopenharmony_ci" # Shorten the selection timeout to 128ms\n" 3488c2ecf20Sopenharmony_ci"\n" 3498c2ecf20Sopenharmony_ci" options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n" 3508c2ecf20Sopenharmony_ci); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void ahd_linux_handle_scsi_status(struct ahd_softc *, 3538c2ecf20Sopenharmony_ci struct scsi_device *, 3548c2ecf20Sopenharmony_ci struct scb *); 3558c2ecf20Sopenharmony_cistatic void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, 3568c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd); 3578c2ecf20Sopenharmony_cistatic int ahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd); 3588c2ecf20Sopenharmony_cistatic void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd); 3598c2ecf20Sopenharmony_cistatic u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd, 3608c2ecf20Sopenharmony_ci struct ahd_devinfo *devinfo); 3618c2ecf20Sopenharmony_cistatic void ahd_linux_device_queue_depth(struct scsi_device *); 3628c2ecf20Sopenharmony_cistatic int ahd_linux_run_command(struct ahd_softc*, 3638c2ecf20Sopenharmony_ci struct ahd_linux_device *, 3648c2ecf20Sopenharmony_ci struct scsi_cmnd *); 3658c2ecf20Sopenharmony_cistatic void ahd_linux_setup_tag_info_global(char *p); 3668c2ecf20Sopenharmony_cistatic int aic79xx_setup(char *c); 3678c2ecf20Sopenharmony_cistatic void ahd_freeze_simq(struct ahd_softc *ahd); 3688c2ecf20Sopenharmony_cistatic void ahd_release_simq(struct ahd_softc *ahd); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int ahd_linux_unit; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/************************** OS Utility Wrappers *******************************/ 3748c2ecf20Sopenharmony_civoid ahd_delay(long); 3758c2ecf20Sopenharmony_civoid 3768c2ecf20Sopenharmony_ciahd_delay(long usec) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * udelay on Linux can have problems for 3808c2ecf20Sopenharmony_ci * multi-millisecond waits. Wait at most 3818c2ecf20Sopenharmony_ci * 1024us per call. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci while (usec > 0) { 3848c2ecf20Sopenharmony_ci udelay(usec % 1024); 3858c2ecf20Sopenharmony_ci usec -= 1024; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/***************************** Low Level I/O **********************************/ 3918c2ecf20Sopenharmony_ciuint8_t ahd_inb(struct ahd_softc * ahd, long port); 3928c2ecf20Sopenharmony_civoid ahd_outb(struct ahd_softc * ahd, long port, uint8_t val); 3938c2ecf20Sopenharmony_civoid ahd_outw_atomic(struct ahd_softc * ahd, 3948c2ecf20Sopenharmony_ci long port, uint16_t val); 3958c2ecf20Sopenharmony_civoid ahd_outsb(struct ahd_softc * ahd, long port, 3968c2ecf20Sopenharmony_ci uint8_t *, int count); 3978c2ecf20Sopenharmony_civoid ahd_insb(struct ahd_softc * ahd, long port, 3988c2ecf20Sopenharmony_ci uint8_t *, int count); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ciuint8_t 4018c2ecf20Sopenharmony_ciahd_inb(struct ahd_softc * ahd, long port) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci uint8_t x; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (ahd->tags[0] == BUS_SPACE_MEMIO) { 4068c2ecf20Sopenharmony_ci x = readb(ahd->bshs[0].maddr + port); 4078c2ecf20Sopenharmony_ci } else { 4088c2ecf20Sopenharmony_ci x = inb(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF)); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci mb(); 4118c2ecf20Sopenharmony_ci return (x); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci#if 0 /* unused */ 4158c2ecf20Sopenharmony_cistatic uint16_t 4168c2ecf20Sopenharmony_ciahd_inw_atomic(struct ahd_softc * ahd, long port) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci uint8_t x; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (ahd->tags[0] == BUS_SPACE_MEMIO) { 4218c2ecf20Sopenharmony_ci x = readw(ahd->bshs[0].maddr + port); 4228c2ecf20Sopenharmony_ci } else { 4238c2ecf20Sopenharmony_ci x = inw(ahd->bshs[(port) >> 8].ioport + ((port) & 0xFF)); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci mb(); 4268c2ecf20Sopenharmony_ci return (x); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci#endif 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_civoid 4318c2ecf20Sopenharmony_ciahd_outb(struct ahd_softc * ahd, long port, uint8_t val) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci if (ahd->tags[0] == BUS_SPACE_MEMIO) { 4348c2ecf20Sopenharmony_ci writeb(val, ahd->bshs[0].maddr + port); 4358c2ecf20Sopenharmony_ci } else { 4368c2ecf20Sopenharmony_ci outb(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF)); 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci mb(); 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_civoid 4428c2ecf20Sopenharmony_ciahd_outw_atomic(struct ahd_softc * ahd, long port, uint16_t val) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci if (ahd->tags[0] == BUS_SPACE_MEMIO) { 4458c2ecf20Sopenharmony_ci writew(val, ahd->bshs[0].maddr + port); 4468c2ecf20Sopenharmony_ci } else { 4478c2ecf20Sopenharmony_ci outw(val, ahd->bshs[(port) >> 8].ioport + (port & 0xFF)); 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci mb(); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_civoid 4538c2ecf20Sopenharmony_ciahd_outsb(struct ahd_softc * ahd, long port, uint8_t *array, int count) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci int i; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * There is probably a more efficient way to do this on Linux 4598c2ecf20Sopenharmony_ci * but we don't use this for anything speed critical and this 4608c2ecf20Sopenharmony_ci * should work. 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 4638c2ecf20Sopenharmony_ci ahd_outb(ahd, port, *array++); 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_civoid 4678c2ecf20Sopenharmony_ciahd_insb(struct ahd_softc * ahd, long port, uint8_t *array, int count) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci int i; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * There is probably a more efficient way to do this on Linux 4738c2ecf20Sopenharmony_ci * but we don't use this for anything speed critical and this 4748c2ecf20Sopenharmony_ci * should work. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 4778c2ecf20Sopenharmony_ci *array++ = ahd_inb(ahd, port); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/******************************* PCI Routines *********************************/ 4818c2ecf20Sopenharmony_ciuint32_t 4828c2ecf20Sopenharmony_ciahd_pci_read_config(ahd_dev_softc_t pci, int reg, int width) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci switch (width) { 4858c2ecf20Sopenharmony_ci case 1: 4868c2ecf20Sopenharmony_ci { 4878c2ecf20Sopenharmony_ci uint8_t retval; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci pci_read_config_byte(pci, reg, &retval); 4908c2ecf20Sopenharmony_ci return (retval); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci case 2: 4938c2ecf20Sopenharmony_ci { 4948c2ecf20Sopenharmony_ci uint16_t retval; 4958c2ecf20Sopenharmony_ci pci_read_config_word(pci, reg, &retval); 4968c2ecf20Sopenharmony_ci return (retval); 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci case 4: 4998c2ecf20Sopenharmony_ci { 5008c2ecf20Sopenharmony_ci uint32_t retval; 5018c2ecf20Sopenharmony_ci pci_read_config_dword(pci, reg, &retval); 5028c2ecf20Sopenharmony_ci return (retval); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci default: 5058c2ecf20Sopenharmony_ci panic("ahd_pci_read_config: Read size too big"); 5068c2ecf20Sopenharmony_ci /* NOTREACHED */ 5078c2ecf20Sopenharmony_ci return (0); 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_civoid 5128c2ecf20Sopenharmony_ciahd_pci_write_config(ahd_dev_softc_t pci, int reg, uint32_t value, int width) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci switch (width) { 5158c2ecf20Sopenharmony_ci case 1: 5168c2ecf20Sopenharmony_ci pci_write_config_byte(pci, reg, value); 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci case 2: 5198c2ecf20Sopenharmony_ci pci_write_config_word(pci, reg, value); 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case 4: 5228c2ecf20Sopenharmony_ci pci_write_config_dword(pci, reg, value); 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci default: 5258c2ecf20Sopenharmony_ci panic("ahd_pci_write_config: Write size too big"); 5268c2ecf20Sopenharmony_ci /* NOTREACHED */ 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci/****************************** Inlines ***************************************/ 5318c2ecf20Sopenharmony_cistatic void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic void 5348c2ecf20Sopenharmony_ciahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci cmd = scb->io_ctx; 5398c2ecf20Sopenharmony_ci ahd_sync_sglist(ahd, scb, BUS_DMASYNC_POSTWRITE); 5408c2ecf20Sopenharmony_ci scsi_dma_unmap(cmd); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/******************************** Macros **************************************/ 5448c2ecf20Sopenharmony_ci#define BUILD_SCSIID(ahd, cmd) \ 5458c2ecf20Sopenharmony_ci (((scmd_id(cmd) << TID_SHIFT) & TID) | (ahd)->our_id) 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* 5488c2ecf20Sopenharmony_ci * Return a string describing the driver. 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_cistatic const char * 5518c2ecf20Sopenharmony_ciahd_linux_info(struct Scsi_Host *host) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci static char buffer[512]; 5548c2ecf20Sopenharmony_ci char ahd_info[256]; 5558c2ecf20Sopenharmony_ci char *bp; 5568c2ecf20Sopenharmony_ci struct ahd_softc *ahd; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci bp = &buffer[0]; 5598c2ecf20Sopenharmony_ci ahd = *(struct ahd_softc **)host->hostdata; 5608c2ecf20Sopenharmony_ci memset(bp, 0, sizeof(buffer)); 5618c2ecf20Sopenharmony_ci strcpy(bp, "Adaptec AIC79XX PCI-X SCSI HBA DRIVER, Rev " AIC79XX_DRIVER_VERSION "\n" 5628c2ecf20Sopenharmony_ci " <"); 5638c2ecf20Sopenharmony_ci strcat(bp, ahd->description); 5648c2ecf20Sopenharmony_ci strcat(bp, ">\n" 5658c2ecf20Sopenharmony_ci " "); 5668c2ecf20Sopenharmony_ci ahd_controller_info(ahd, ahd_info); 5678c2ecf20Sopenharmony_ci strcat(bp, ahd_info); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return (bp); 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/* 5738c2ecf20Sopenharmony_ci * Queue an SCB to the controller. 5748c2ecf20Sopenharmony_ci */ 5758c2ecf20Sopenharmony_cistatic int 5768c2ecf20Sopenharmony_ciahd_linux_queue_lck(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *)) 5778c2ecf20Sopenharmony_ci{ 5788c2ecf20Sopenharmony_ci struct ahd_softc *ahd; 5798c2ecf20Sopenharmony_ci struct ahd_linux_device *dev = scsi_transport_device_data(cmd->device); 5808c2ecf20Sopenharmony_ci int rtn = SCSI_MLQUEUE_HOST_BUSY; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci ahd = *(struct ahd_softc **)cmd->device->host->hostdata; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci cmd->scsi_done = scsi_done; 5858c2ecf20Sopenharmony_ci cmd->result = CAM_REQ_INPROG << 16; 5868c2ecf20Sopenharmony_ci rtn = ahd_linux_run_command(ahd, dev, cmd); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return rtn; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(ahd_linux_queue) 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic struct scsi_target ** 5948c2ecf20Sopenharmony_ciahd_linux_target_in_softc(struct scsi_target *starget) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci struct ahd_softc *ahd = 5978c2ecf20Sopenharmony_ci *((struct ahd_softc **)dev_to_shost(&starget->dev)->hostdata); 5988c2ecf20Sopenharmony_ci unsigned int target_offset; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci target_offset = starget->id; 6018c2ecf20Sopenharmony_ci if (starget->channel != 0) 6028c2ecf20Sopenharmony_ci target_offset += 8; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return &ahd->platform_data->starget[target_offset]; 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic int 6088c2ecf20Sopenharmony_ciahd_linux_target_alloc(struct scsi_target *starget) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct ahd_softc *ahd = 6118c2ecf20Sopenharmony_ci *((struct ahd_softc **)dev_to_shost(&starget->dev)->hostdata); 6128c2ecf20Sopenharmony_ci struct seeprom_config *sc = ahd->seep_config; 6138c2ecf20Sopenharmony_ci unsigned long flags; 6148c2ecf20Sopenharmony_ci struct scsi_target **ahd_targp = ahd_linux_target_in_softc(starget); 6158c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 6168c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo; 6178c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 6188c2ecf20Sopenharmony_ci char channel = starget->channel + 'A'; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci BUG_ON(*ahd_targp != NULL); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci *ahd_targp = starget; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (sc) { 6278c2ecf20Sopenharmony_ci int flags = sc->device_flags[starget->id]; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, 6308c2ecf20Sopenharmony_ci starget->id, &tstate); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if ((flags & CFPACKETIZED) == 0) { 6338c2ecf20Sopenharmony_ci /* don't negotiate packetized (IU) transfers */ 6348c2ecf20Sopenharmony_ci spi_max_iu(starget) = 0; 6358c2ecf20Sopenharmony_ci } else { 6368c2ecf20Sopenharmony_ci if ((ahd->features & AHD_RTI) == 0) 6378c2ecf20Sopenharmony_ci spi_rti(starget) = 0; 6388c2ecf20Sopenharmony_ci } 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci if ((flags & CFQAS) == 0) 6418c2ecf20Sopenharmony_ci spi_max_qas(starget) = 0; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* Transinfo values have been set to BIOS settings */ 6448c2ecf20Sopenharmony_ci spi_max_width(starget) = (flags & CFWIDEB) ? 1 : 0; 6458c2ecf20Sopenharmony_ci spi_min_period(starget) = tinfo->user.period; 6468c2ecf20Sopenharmony_ci spi_max_offset(starget) = tinfo->user.offset; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id, 6508c2ecf20Sopenharmony_ci starget->id, &tstate); 6518c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, ahd->our_id, starget->id, 6528c2ecf20Sopenharmony_ci CAM_LUN_WILDCARD, channel, 6538c2ecf20Sopenharmony_ci ROLE_INITIATOR); 6548c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, 0, 0, 0, 6558c2ecf20Sopenharmony_ci AHD_TRANS_GOAL, /*paused*/FALSE); 6568c2ecf20Sopenharmony_ci ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, 6578c2ecf20Sopenharmony_ci AHD_TRANS_GOAL, /*paused*/FALSE); 6588c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return 0; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic void 6648c2ecf20Sopenharmony_ciahd_linux_target_destroy(struct scsi_target *starget) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct scsi_target **ahd_targp = ahd_linux_target_in_softc(starget); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci *ahd_targp = NULL; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic int 6728c2ecf20Sopenharmony_ciahd_linux_slave_alloc(struct scsi_device *sdev) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct ahd_softc *ahd = 6758c2ecf20Sopenharmony_ci *((struct ahd_softc **)sdev->host->hostdata); 6768c2ecf20Sopenharmony_ci struct ahd_linux_device *dev; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (bootverbose) 6798c2ecf20Sopenharmony_ci printk("%s: Slave Alloc %d\n", ahd_name(ahd), sdev->id); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci dev = scsi_transport_device_data(sdev); 6828c2ecf20Sopenharmony_ci memset(dev, 0, sizeof(*dev)); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* 6858c2ecf20Sopenharmony_ci * We start out life using untagged 6868c2ecf20Sopenharmony_ci * transactions of which we allow one. 6878c2ecf20Sopenharmony_ci */ 6888c2ecf20Sopenharmony_ci dev->openings = 1; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci /* 6918c2ecf20Sopenharmony_ci * Set maxtags to 0. This will be changed if we 6928c2ecf20Sopenharmony_ci * later determine that we are dealing with 6938c2ecf20Sopenharmony_ci * a tagged queuing capable device. 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_ci dev->maxtags = 0; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci return (0); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic int 7018c2ecf20Sopenharmony_ciahd_linux_slave_configure(struct scsi_device *sdev) 7028c2ecf20Sopenharmony_ci{ 7038c2ecf20Sopenharmony_ci if (bootverbose) 7048c2ecf20Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "Slave Configure\n"); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci ahd_linux_device_queue_depth(sdev); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* Initial Domain Validation */ 7098c2ecf20Sopenharmony_ci if (!spi_initial_dv(sdev->sdev_target)) 7108c2ecf20Sopenharmony_ci spi_dv_device(sdev); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci#if defined(__i386__) 7168c2ecf20Sopenharmony_ci/* 7178c2ecf20Sopenharmony_ci * Return the disk geometry for the given SCSI device. 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_cistatic int 7208c2ecf20Sopenharmony_ciahd_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev, 7218c2ecf20Sopenharmony_ci sector_t capacity, int geom[]) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci int heads; 7248c2ecf20Sopenharmony_ci int sectors; 7258c2ecf20Sopenharmony_ci int cylinders; 7268c2ecf20Sopenharmony_ci int extended; 7278c2ecf20Sopenharmony_ci struct ahd_softc *ahd; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci ahd = *((struct ahd_softc **)sdev->host->hostdata); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (scsi_partsize(bdev, capacity, geom)) 7328c2ecf20Sopenharmony_ci return 0; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci heads = 64; 7358c2ecf20Sopenharmony_ci sectors = 32; 7368c2ecf20Sopenharmony_ci cylinders = aic_sector_div(capacity, heads, sectors); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci if (aic79xx_extended != 0) 7398c2ecf20Sopenharmony_ci extended = 1; 7408c2ecf20Sopenharmony_ci else 7418c2ecf20Sopenharmony_ci extended = (ahd->flags & AHD_EXTENDED_TRANS_A) != 0; 7428c2ecf20Sopenharmony_ci if (extended && cylinders >= 1024) { 7438c2ecf20Sopenharmony_ci heads = 255; 7448c2ecf20Sopenharmony_ci sectors = 63; 7458c2ecf20Sopenharmony_ci cylinders = aic_sector_div(capacity, heads, sectors); 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci geom[0] = heads; 7488c2ecf20Sopenharmony_ci geom[1] = sectors; 7498c2ecf20Sopenharmony_ci geom[2] = cylinders; 7508c2ecf20Sopenharmony_ci return (0); 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci#endif 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci/* 7558c2ecf20Sopenharmony_ci * Abort the current SCSI command(s). 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_cistatic int 7588c2ecf20Sopenharmony_ciahd_linux_abort(struct scsi_cmnd *cmd) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci int error; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci error = ahd_linux_queue_abort_cmd(cmd); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return error; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/* 7688c2ecf20Sopenharmony_ci * Attempt to send a target reset message to the device that timed out. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_cistatic int 7718c2ecf20Sopenharmony_ciahd_linux_dev_reset(struct scsi_cmnd *cmd) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct ahd_softc *ahd; 7748c2ecf20Sopenharmony_ci struct ahd_linux_device *dev; 7758c2ecf20Sopenharmony_ci struct scb *reset_scb; 7768c2ecf20Sopenharmony_ci u_int cdb_byte; 7778c2ecf20Sopenharmony_ci int retval = SUCCESS; 7788c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo; 7798c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 7808c2ecf20Sopenharmony_ci unsigned long flags; 7818c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci reset_scb = NULL; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci ahd = *(struct ahd_softc **)cmd->device->host->hostdata; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, 7888c2ecf20Sopenharmony_ci "Attempting to queue a TARGET RESET message:"); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci printk("CDB:"); 7918c2ecf20Sopenharmony_ci for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++) 7928c2ecf20Sopenharmony_ci printk(" 0x%x", cmd->cmnd[cdb_byte]); 7938c2ecf20Sopenharmony_ci printk("\n"); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* 7968c2ecf20Sopenharmony_ci * Determine if we currently own this command. 7978c2ecf20Sopenharmony_ci */ 7988c2ecf20Sopenharmony_ci dev = scsi_transport_device_data(cmd->device); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (dev == NULL) { 8018c2ecf20Sopenharmony_ci /* 8028c2ecf20Sopenharmony_ci * No target device for this command exists, 8038c2ecf20Sopenharmony_ci * so we must not still own the command. 8048c2ecf20Sopenharmony_ci */ 8058c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "Is not an active device\n"); 8068c2ecf20Sopenharmony_ci return SUCCESS; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci /* 8108c2ecf20Sopenharmony_ci * Generate us a new SCB 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci reset_scb = ahd_get_scb(ahd, AHD_NEVER_COL_IDX); 8138c2ecf20Sopenharmony_ci if (!reset_scb) { 8148c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "No SCB available\n"); 8158c2ecf20Sopenharmony_ci return FAILED; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, 8198c2ecf20Sopenharmony_ci cmd->device->id, &tstate); 8208c2ecf20Sopenharmony_ci reset_scb->io_ctx = cmd; 8218c2ecf20Sopenharmony_ci reset_scb->platform_data->dev = dev; 8228c2ecf20Sopenharmony_ci reset_scb->sg_count = 0; 8238c2ecf20Sopenharmony_ci ahd_set_residual(reset_scb, 0); 8248c2ecf20Sopenharmony_ci ahd_set_sense_residual(reset_scb, 0); 8258c2ecf20Sopenharmony_ci reset_scb->platform_data->xfer_len = 0; 8268c2ecf20Sopenharmony_ci reset_scb->hscb->control = 0; 8278c2ecf20Sopenharmony_ci reset_scb->hscb->scsiid = BUILD_SCSIID(ahd,cmd); 8288c2ecf20Sopenharmony_ci reset_scb->hscb->lun = cmd->device->lun; 8298c2ecf20Sopenharmony_ci reset_scb->hscb->cdb_len = 0; 8308c2ecf20Sopenharmony_ci reset_scb->hscb->task_management = SIU_TASKMGMT_LUN_RESET; 8318c2ecf20Sopenharmony_ci reset_scb->flags |= SCB_DEVICE_RESET|SCB_RECOVERY_SCB|SCB_ACTIVE; 8328c2ecf20Sopenharmony_ci if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { 8338c2ecf20Sopenharmony_ci reset_scb->flags |= SCB_PACKETIZED; 8348c2ecf20Sopenharmony_ci } else { 8358c2ecf20Sopenharmony_ci reset_scb->hscb->control |= MK_MESSAGE; 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci dev->openings--; 8388c2ecf20Sopenharmony_ci dev->active++; 8398c2ecf20Sopenharmony_ci dev->commands_issued++; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci LIST_INSERT_HEAD(&ahd->pending_scbs, reset_scb, pending_links); 8448c2ecf20Sopenharmony_ci ahd_queue_scb(ahd, reset_scb); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci ahd->platform_data->eh_done = &done; 8478c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci printk("%s: Device reset code sleeping\n", ahd_name(ahd)); 8508c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&done, 5 * HZ)) { 8518c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 8528c2ecf20Sopenharmony_ci ahd->platform_data->eh_done = NULL; 8538c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 8548c2ecf20Sopenharmony_ci printk("%s: Device reset timer expired (active %d)\n", 8558c2ecf20Sopenharmony_ci ahd_name(ahd), dev->active); 8568c2ecf20Sopenharmony_ci retval = FAILED; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci printk("%s: Device reset returning 0x%x\n", ahd_name(ahd), retval); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci return (retval); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci/* 8648c2ecf20Sopenharmony_ci * Reset the SCSI bus. 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_cistatic int 8678c2ecf20Sopenharmony_ciahd_linux_bus_reset(struct scsi_cmnd *cmd) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci struct ahd_softc *ahd; 8708c2ecf20Sopenharmony_ci int found; 8718c2ecf20Sopenharmony_ci unsigned long flags; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci ahd = *(struct ahd_softc **)cmd->device->host->hostdata; 8748c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 8758c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) 8768c2ecf20Sopenharmony_ci printk("%s: Bus reset called for cmd %p\n", 8778c2ecf20Sopenharmony_ci ahd_name(ahd), cmd); 8788c2ecf20Sopenharmony_ci#endif 8798c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci found = ahd_reset_channel(ahd, scmd_channel(cmd) + 'A', 8828c2ecf20Sopenharmony_ci /*initiate reset*/TRUE); 8838c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci if (bootverbose) 8868c2ecf20Sopenharmony_ci printk("%s: SCSI bus reset delivered. " 8878c2ecf20Sopenharmony_ci "%d SCBs aborted.\n", ahd_name(ahd), found); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci return (SUCCESS); 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistruct scsi_host_template aic79xx_driver_template = { 8938c2ecf20Sopenharmony_ci .module = THIS_MODULE, 8948c2ecf20Sopenharmony_ci .name = "aic79xx", 8958c2ecf20Sopenharmony_ci .proc_name = "aic79xx", 8968c2ecf20Sopenharmony_ci .show_info = ahd_linux_show_info, 8978c2ecf20Sopenharmony_ci .write_info = ahd_proc_write_seeprom, 8988c2ecf20Sopenharmony_ci .info = ahd_linux_info, 8998c2ecf20Sopenharmony_ci .queuecommand = ahd_linux_queue, 9008c2ecf20Sopenharmony_ci .eh_abort_handler = ahd_linux_abort, 9018c2ecf20Sopenharmony_ci .eh_device_reset_handler = ahd_linux_dev_reset, 9028c2ecf20Sopenharmony_ci .eh_bus_reset_handler = ahd_linux_bus_reset, 9038c2ecf20Sopenharmony_ci#if defined(__i386__) 9048c2ecf20Sopenharmony_ci .bios_param = ahd_linux_biosparam, 9058c2ecf20Sopenharmony_ci#endif 9068c2ecf20Sopenharmony_ci .can_queue = AHD_MAX_QUEUE, 9078c2ecf20Sopenharmony_ci .this_id = -1, 9088c2ecf20Sopenharmony_ci .max_sectors = 8192, 9098c2ecf20Sopenharmony_ci .cmd_per_lun = 2, 9108c2ecf20Sopenharmony_ci .slave_alloc = ahd_linux_slave_alloc, 9118c2ecf20Sopenharmony_ci .slave_configure = ahd_linux_slave_configure, 9128c2ecf20Sopenharmony_ci .target_alloc = ahd_linux_target_alloc, 9138c2ecf20Sopenharmony_ci .target_destroy = ahd_linux_target_destroy, 9148c2ecf20Sopenharmony_ci}; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/******************************** Bus DMA *************************************/ 9178c2ecf20Sopenharmony_ciint 9188c2ecf20Sopenharmony_ciahd_dma_tag_create(struct ahd_softc *ahd, bus_dma_tag_t parent, 9198c2ecf20Sopenharmony_ci bus_size_t alignment, bus_size_t boundary, 9208c2ecf20Sopenharmony_ci dma_addr_t lowaddr, dma_addr_t highaddr, 9218c2ecf20Sopenharmony_ci bus_dma_filter_t *filter, void *filterarg, 9228c2ecf20Sopenharmony_ci bus_size_t maxsize, int nsegments, 9238c2ecf20Sopenharmony_ci bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci bus_dma_tag_t dmat; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci dmat = kmalloc(sizeof(*dmat), GFP_ATOMIC); 9288c2ecf20Sopenharmony_ci if (dmat == NULL) 9298c2ecf20Sopenharmony_ci return (ENOMEM); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* 9328c2ecf20Sopenharmony_ci * Linux is very simplistic about DMA memory. For now don't 9338c2ecf20Sopenharmony_ci * maintain all specification information. Once Linux supplies 9348c2ecf20Sopenharmony_ci * better facilities for doing these operations, or the 9358c2ecf20Sopenharmony_ci * needs of this particular driver change, we might need to do 9368c2ecf20Sopenharmony_ci * more here. 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_ci dmat->alignment = alignment; 9398c2ecf20Sopenharmony_ci dmat->boundary = boundary; 9408c2ecf20Sopenharmony_ci dmat->maxsize = maxsize; 9418c2ecf20Sopenharmony_ci *ret_tag = dmat; 9428c2ecf20Sopenharmony_ci return (0); 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_civoid 9468c2ecf20Sopenharmony_ciahd_dma_tag_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat) 9478c2ecf20Sopenharmony_ci{ 9488c2ecf20Sopenharmony_ci kfree(dmat); 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ciint 9528c2ecf20Sopenharmony_ciahd_dmamem_alloc(struct ahd_softc *ahd, bus_dma_tag_t dmat, void** vaddr, 9538c2ecf20Sopenharmony_ci int flags, bus_dmamap_t *mapp) 9548c2ecf20Sopenharmony_ci{ 9558c2ecf20Sopenharmony_ci *vaddr = dma_alloc_coherent(&ahd->dev_softc->dev, dmat->maxsize, mapp, 9568c2ecf20Sopenharmony_ci GFP_ATOMIC); 9578c2ecf20Sopenharmony_ci if (*vaddr == NULL) 9588c2ecf20Sopenharmony_ci return (ENOMEM); 9598c2ecf20Sopenharmony_ci return(0); 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_civoid 9638c2ecf20Sopenharmony_ciahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat, 9648c2ecf20Sopenharmony_ci void* vaddr, bus_dmamap_t map) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci dma_free_coherent(&ahd->dev_softc->dev, dmat->maxsize, vaddr, map); 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ciint 9708c2ecf20Sopenharmony_ciahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map, 9718c2ecf20Sopenharmony_ci void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb, 9728c2ecf20Sopenharmony_ci void *cb_arg, int flags) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci /* 9758c2ecf20Sopenharmony_ci * Assume for now that this will only be used during 9768c2ecf20Sopenharmony_ci * initialization and not for per-transaction buffer mapping. 9778c2ecf20Sopenharmony_ci */ 9788c2ecf20Sopenharmony_ci bus_dma_segment_t stack_sg; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci stack_sg.ds_addr = map; 9818c2ecf20Sopenharmony_ci stack_sg.ds_len = dmat->maxsize; 9828c2ecf20Sopenharmony_ci cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0); 9838c2ecf20Sopenharmony_ci return (0); 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_civoid 9878c2ecf20Sopenharmony_ciahd_dmamap_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ciint 9928c2ecf20Sopenharmony_ciahd_dmamap_unload(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci /* Nothing to do */ 9958c2ecf20Sopenharmony_ci return (0); 9968c2ecf20Sopenharmony_ci} 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci/********************* Platform Dependent Functions ***************************/ 9998c2ecf20Sopenharmony_cistatic void 10008c2ecf20Sopenharmony_ciahd_linux_setup_iocell_info(u_long index, int instance, int targ, int32_t value) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if ((instance >= 0) 10048c2ecf20Sopenharmony_ci && (instance < ARRAY_SIZE(aic79xx_iocell_info))) { 10058c2ecf20Sopenharmony_ci uint8_t *iocell_info; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci iocell_info = (uint8_t*)&aic79xx_iocell_info[instance]; 10088c2ecf20Sopenharmony_ci iocell_info[index] = value & 0xFFFF; 10098c2ecf20Sopenharmony_ci if (bootverbose) 10108c2ecf20Sopenharmony_ci printk("iocell[%d:%ld] = %d\n", instance, index, value); 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic void 10158c2ecf20Sopenharmony_ciahd_linux_setup_tag_info_global(char *p) 10168c2ecf20Sopenharmony_ci{ 10178c2ecf20Sopenharmony_ci int tags, i, j; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci tags = simple_strtoul(p + 1, NULL, 0) & 0xff; 10208c2ecf20Sopenharmony_ci printk("Setting Global Tags= %d\n", tags); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(aic79xx_tag_info); i++) { 10238c2ecf20Sopenharmony_ci for (j = 0; j < AHD_NUM_TARGETS; j++) { 10248c2ecf20Sopenharmony_ci aic79xx_tag_info[i].tag_commands[j] = tags; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_cistatic void 10308c2ecf20Sopenharmony_ciahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value) 10318c2ecf20Sopenharmony_ci{ 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if ((instance >= 0) && (targ >= 0) 10348c2ecf20Sopenharmony_ci && (instance < ARRAY_SIZE(aic79xx_tag_info)) 10358c2ecf20Sopenharmony_ci && (targ < AHD_NUM_TARGETS)) { 10368c2ecf20Sopenharmony_ci aic79xx_tag_info[instance].tag_commands[targ] = value & 0x1FF; 10378c2ecf20Sopenharmony_ci if (bootverbose) 10388c2ecf20Sopenharmony_ci printk("tag_info[%d:%d] = %d\n", instance, targ, value); 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cistatic char * 10438c2ecf20Sopenharmony_ciahd_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, 10448c2ecf20Sopenharmony_ci void (*callback)(u_long, int, int, int32_t), 10458c2ecf20Sopenharmony_ci u_long callback_arg) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci char *tok_end; 10488c2ecf20Sopenharmony_ci char *tok_end2; 10498c2ecf20Sopenharmony_ci int i; 10508c2ecf20Sopenharmony_ci int instance; 10518c2ecf20Sopenharmony_ci int targ; 10528c2ecf20Sopenharmony_ci int done; 10538c2ecf20Sopenharmony_ci char tok_list[] = {'.', ',', '{', '}', '\0'}; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* All options use a ':' name/arg separator */ 10568c2ecf20Sopenharmony_ci if (*opt_arg != ':') 10578c2ecf20Sopenharmony_ci return (opt_arg); 10588c2ecf20Sopenharmony_ci opt_arg++; 10598c2ecf20Sopenharmony_ci instance = -1; 10608c2ecf20Sopenharmony_ci targ = -1; 10618c2ecf20Sopenharmony_ci done = FALSE; 10628c2ecf20Sopenharmony_ci /* 10638c2ecf20Sopenharmony_ci * Restore separator that may be in 10648c2ecf20Sopenharmony_ci * the middle of our option argument. 10658c2ecf20Sopenharmony_ci */ 10668c2ecf20Sopenharmony_ci tok_end = strchr(opt_arg, '\0'); 10678c2ecf20Sopenharmony_ci if (tok_end < end) 10688c2ecf20Sopenharmony_ci *tok_end = ','; 10698c2ecf20Sopenharmony_ci while (!done) { 10708c2ecf20Sopenharmony_ci switch (*opt_arg) { 10718c2ecf20Sopenharmony_ci case '{': 10728c2ecf20Sopenharmony_ci if (instance == -1) { 10738c2ecf20Sopenharmony_ci instance = 0; 10748c2ecf20Sopenharmony_ci } else { 10758c2ecf20Sopenharmony_ci if (depth > 1) { 10768c2ecf20Sopenharmony_ci if (targ == -1) 10778c2ecf20Sopenharmony_ci targ = 0; 10788c2ecf20Sopenharmony_ci } else { 10798c2ecf20Sopenharmony_ci printk("Malformed Option %s\n", 10808c2ecf20Sopenharmony_ci opt_name); 10818c2ecf20Sopenharmony_ci done = TRUE; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci opt_arg++; 10858c2ecf20Sopenharmony_ci break; 10868c2ecf20Sopenharmony_ci case '}': 10878c2ecf20Sopenharmony_ci if (targ != -1) 10888c2ecf20Sopenharmony_ci targ = -1; 10898c2ecf20Sopenharmony_ci else if (instance != -1) 10908c2ecf20Sopenharmony_ci instance = -1; 10918c2ecf20Sopenharmony_ci opt_arg++; 10928c2ecf20Sopenharmony_ci break; 10938c2ecf20Sopenharmony_ci case ',': 10948c2ecf20Sopenharmony_ci case '.': 10958c2ecf20Sopenharmony_ci if (instance == -1) 10968c2ecf20Sopenharmony_ci done = TRUE; 10978c2ecf20Sopenharmony_ci else if (targ >= 0) 10988c2ecf20Sopenharmony_ci targ++; 10998c2ecf20Sopenharmony_ci else if (instance >= 0) 11008c2ecf20Sopenharmony_ci instance++; 11018c2ecf20Sopenharmony_ci opt_arg++; 11028c2ecf20Sopenharmony_ci break; 11038c2ecf20Sopenharmony_ci case '\0': 11048c2ecf20Sopenharmony_ci done = TRUE; 11058c2ecf20Sopenharmony_ci break; 11068c2ecf20Sopenharmony_ci default: 11078c2ecf20Sopenharmony_ci tok_end = end; 11088c2ecf20Sopenharmony_ci for (i = 0; tok_list[i]; i++) { 11098c2ecf20Sopenharmony_ci tok_end2 = strchr(opt_arg, tok_list[i]); 11108c2ecf20Sopenharmony_ci if ((tok_end2) && (tok_end2 < tok_end)) 11118c2ecf20Sopenharmony_ci tok_end = tok_end2; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci callback(callback_arg, instance, targ, 11148c2ecf20Sopenharmony_ci simple_strtol(opt_arg, NULL, 0)); 11158c2ecf20Sopenharmony_ci opt_arg = tok_end; 11168c2ecf20Sopenharmony_ci break; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci return (opt_arg); 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci/* 11238c2ecf20Sopenharmony_ci * Handle Linux boot parameters. This routine allows for assigning a value 11248c2ecf20Sopenharmony_ci * to a parameter with a ':' between the parameter and the value. 11258c2ecf20Sopenharmony_ci * ie. aic79xx=stpwlev:1,extended 11268c2ecf20Sopenharmony_ci */ 11278c2ecf20Sopenharmony_cistatic int 11288c2ecf20Sopenharmony_ciaic79xx_setup(char *s) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci int i, n; 11318c2ecf20Sopenharmony_ci char *p; 11328c2ecf20Sopenharmony_ci char *end; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci static const struct { 11358c2ecf20Sopenharmony_ci const char *name; 11368c2ecf20Sopenharmony_ci uint32_t *flag; 11378c2ecf20Sopenharmony_ci } options[] = { 11388c2ecf20Sopenharmony_ci { "extended", &aic79xx_extended }, 11398c2ecf20Sopenharmony_ci { "no_reset", &aic79xx_no_reset }, 11408c2ecf20Sopenharmony_ci { "verbose", &aic79xx_verbose }, 11418c2ecf20Sopenharmony_ci { "allow_memio", &aic79xx_allow_memio}, 11428c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 11438c2ecf20Sopenharmony_ci { "debug", &ahd_debug }, 11448c2ecf20Sopenharmony_ci#endif 11458c2ecf20Sopenharmony_ci { "periodic_otag", &aic79xx_periodic_otag }, 11468c2ecf20Sopenharmony_ci { "pci_parity", &aic79xx_pci_parity }, 11478c2ecf20Sopenharmony_ci { "seltime", &aic79xx_seltime }, 11488c2ecf20Sopenharmony_ci { "tag_info", NULL }, 11498c2ecf20Sopenharmony_ci { "global_tag_depth", NULL}, 11508c2ecf20Sopenharmony_ci { "slewrate", NULL }, 11518c2ecf20Sopenharmony_ci { "precomp", NULL }, 11528c2ecf20Sopenharmony_ci { "amplitude", NULL }, 11538c2ecf20Sopenharmony_ci { "slowcrc", &aic79xx_slowcrc }, 11548c2ecf20Sopenharmony_ci }; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci end = strchr(s, '\0'); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* 11598c2ecf20Sopenharmony_ci * XXX ia64 gcc isn't smart enough to know that ARRAY_SIZE 11608c2ecf20Sopenharmony_ci * will never be 0 in this case. 11618c2ecf20Sopenharmony_ci */ 11628c2ecf20Sopenharmony_ci n = 0; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci while ((p = strsep(&s, ",.")) != NULL) { 11658c2ecf20Sopenharmony_ci if (*p == '\0') 11668c2ecf20Sopenharmony_ci continue; 11678c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(options); i++) { 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci n = strlen(options[i].name); 11708c2ecf20Sopenharmony_ci if (strncmp(options[i].name, p, n) == 0) 11718c2ecf20Sopenharmony_ci break; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(options)) 11748c2ecf20Sopenharmony_ci continue; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (strncmp(p, "global_tag_depth", n) == 0) { 11778c2ecf20Sopenharmony_ci ahd_linux_setup_tag_info_global(p + n); 11788c2ecf20Sopenharmony_ci } else if (strncmp(p, "tag_info", n) == 0) { 11798c2ecf20Sopenharmony_ci s = ahd_parse_brace_option("tag_info", p + n, end, 11808c2ecf20Sopenharmony_ci 2, ahd_linux_setup_tag_info, 0); 11818c2ecf20Sopenharmony_ci } else if (strncmp(p, "slewrate", n) == 0) { 11828c2ecf20Sopenharmony_ci s = ahd_parse_brace_option("slewrate", 11838c2ecf20Sopenharmony_ci p + n, end, 1, ahd_linux_setup_iocell_info, 11848c2ecf20Sopenharmony_ci AIC79XX_SLEWRATE_INDEX); 11858c2ecf20Sopenharmony_ci } else if (strncmp(p, "precomp", n) == 0) { 11868c2ecf20Sopenharmony_ci s = ahd_parse_brace_option("precomp", 11878c2ecf20Sopenharmony_ci p + n, end, 1, ahd_linux_setup_iocell_info, 11888c2ecf20Sopenharmony_ci AIC79XX_PRECOMP_INDEX); 11898c2ecf20Sopenharmony_ci } else if (strncmp(p, "amplitude", n) == 0) { 11908c2ecf20Sopenharmony_ci s = ahd_parse_brace_option("amplitude", 11918c2ecf20Sopenharmony_ci p + n, end, 1, ahd_linux_setup_iocell_info, 11928c2ecf20Sopenharmony_ci AIC79XX_AMPLITUDE_INDEX); 11938c2ecf20Sopenharmony_ci } else if (p[n] == ':') { 11948c2ecf20Sopenharmony_ci *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0); 11958c2ecf20Sopenharmony_ci } else if (!strncmp(p, "verbose", n)) { 11968c2ecf20Sopenharmony_ci *(options[i].flag) = 1; 11978c2ecf20Sopenharmony_ci } else { 11988c2ecf20Sopenharmony_ci *(options[i].flag) ^= 0xFFFFFFFF; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci return 1; 12028c2ecf20Sopenharmony_ci} 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci__setup("aic79xx=", aic79xx_setup); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ciuint32_t aic79xx_verbose; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ciint 12098c2ecf20Sopenharmony_ciahd_linux_register_host(struct ahd_softc *ahd, struct scsi_host_template *template) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci char buf[80]; 12128c2ecf20Sopenharmony_ci struct Scsi_Host *host; 12138c2ecf20Sopenharmony_ci char *new_name; 12148c2ecf20Sopenharmony_ci u_long s; 12158c2ecf20Sopenharmony_ci int retval; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci template->name = ahd->description; 12188c2ecf20Sopenharmony_ci host = scsi_host_alloc(template, sizeof(struct ahd_softc *)); 12198c2ecf20Sopenharmony_ci if (host == NULL) 12208c2ecf20Sopenharmony_ci return (ENOMEM); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci *((struct ahd_softc **)host->hostdata) = ahd; 12238c2ecf20Sopenharmony_ci ahd->platform_data->host = host; 12248c2ecf20Sopenharmony_ci host->can_queue = AHD_MAX_QUEUE; 12258c2ecf20Sopenharmony_ci host->cmd_per_lun = 2; 12268c2ecf20Sopenharmony_ci host->sg_tablesize = AHD_NSEG; 12278c2ecf20Sopenharmony_ci host->this_id = ahd->our_id; 12288c2ecf20Sopenharmony_ci host->irq = ahd->platform_data->irq; 12298c2ecf20Sopenharmony_ci host->max_id = (ahd->features & AHD_WIDE) ? 16 : 8; 12308c2ecf20Sopenharmony_ci host->max_lun = AHD_NUM_LUNS; 12318c2ecf20Sopenharmony_ci host->max_channel = 0; 12328c2ecf20Sopenharmony_ci host->sg_tablesize = AHD_NSEG; 12338c2ecf20Sopenharmony_ci ahd_lock(ahd, &s); 12348c2ecf20Sopenharmony_ci ahd_set_unit(ahd, ahd_linux_unit++); 12358c2ecf20Sopenharmony_ci ahd_unlock(ahd, &s); 12368c2ecf20Sopenharmony_ci sprintf(buf, "scsi%d", host->host_no); 12378c2ecf20Sopenharmony_ci new_name = kmalloc(strlen(buf) + 1, GFP_ATOMIC); 12388c2ecf20Sopenharmony_ci if (new_name != NULL) { 12398c2ecf20Sopenharmony_ci strcpy(new_name, buf); 12408c2ecf20Sopenharmony_ci ahd_set_name(ahd, new_name); 12418c2ecf20Sopenharmony_ci } 12428c2ecf20Sopenharmony_ci host->unique_id = ahd->unit; 12438c2ecf20Sopenharmony_ci ahd_linux_initialize_scsi_bus(ahd); 12448c2ecf20Sopenharmony_ci ahd_intr_enable(ahd, TRUE); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci host->transportt = ahd_linux_transport_template; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci retval = scsi_add_host(host, &ahd->dev_softc->dev); 12498c2ecf20Sopenharmony_ci if (retval) { 12508c2ecf20Sopenharmony_ci printk(KERN_WARNING "aic79xx: scsi_add_host failed\n"); 12518c2ecf20Sopenharmony_ci scsi_host_put(host); 12528c2ecf20Sopenharmony_ci return retval; 12538c2ecf20Sopenharmony_ci } 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci scsi_scan_host(host); 12568c2ecf20Sopenharmony_ci return 0; 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci/* 12608c2ecf20Sopenharmony_ci * Place the SCSI bus into a known state by either resetting it, 12618c2ecf20Sopenharmony_ci * or forcing transfer negotiations on the next command to any 12628c2ecf20Sopenharmony_ci * target. 12638c2ecf20Sopenharmony_ci */ 12648c2ecf20Sopenharmony_cistatic void 12658c2ecf20Sopenharmony_ciahd_linux_initialize_scsi_bus(struct ahd_softc *ahd) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci u_int target_id; 12688c2ecf20Sopenharmony_ci u_int numtarg; 12698c2ecf20Sopenharmony_ci unsigned long s; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci target_id = 0; 12728c2ecf20Sopenharmony_ci numtarg = 0; 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci if (aic79xx_no_reset != 0) 12758c2ecf20Sopenharmony_ci ahd->flags &= ~AHD_RESET_BUS_A; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if ((ahd->flags & AHD_RESET_BUS_A) != 0) 12788c2ecf20Sopenharmony_ci ahd_reset_channel(ahd, 'A', /*initiate_reset*/TRUE); 12798c2ecf20Sopenharmony_ci else 12808c2ecf20Sopenharmony_ci numtarg = (ahd->features & AHD_WIDE) ? 16 : 8; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci ahd_lock(ahd, &s); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci /* 12858c2ecf20Sopenharmony_ci * Force negotiation to async for all targets that 12868c2ecf20Sopenharmony_ci * will not see an initial bus reset. 12878c2ecf20Sopenharmony_ci */ 12888c2ecf20Sopenharmony_ci for (; target_id < numtarg; target_id++) { 12898c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 12908c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo; 12918c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, 12948c2ecf20Sopenharmony_ci target_id, &tstate); 12958c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, ahd->our_id, target_id, 12968c2ecf20Sopenharmony_ci CAM_LUN_WILDCARD, 'A', ROLE_INITIATOR); 12978c2ecf20Sopenharmony_ci ahd_update_neg_request(ahd, &devinfo, tstate, 12988c2ecf20Sopenharmony_ci tinfo, AHD_NEG_ALWAYS); 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci ahd_unlock(ahd, &s); 13018c2ecf20Sopenharmony_ci /* Give the bus some time to recover */ 13028c2ecf20Sopenharmony_ci if ((ahd->flags & AHD_RESET_BUS_A) != 0) { 13038c2ecf20Sopenharmony_ci ahd_freeze_simq(ahd); 13048c2ecf20Sopenharmony_ci msleep(AIC79XX_RESET_DELAY); 13058c2ecf20Sopenharmony_ci ahd_release_simq(ahd); 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci} 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ciint 13108c2ecf20Sopenharmony_ciahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci ahd->platform_data = 13138c2ecf20Sopenharmony_ci kzalloc(sizeof(struct ahd_platform_data), GFP_ATOMIC); 13148c2ecf20Sopenharmony_ci if (ahd->platform_data == NULL) 13158c2ecf20Sopenharmony_ci return (ENOMEM); 13168c2ecf20Sopenharmony_ci ahd->platform_data->irq = AHD_LINUX_NOIRQ; 13178c2ecf20Sopenharmony_ci ahd_lockinit(ahd); 13188c2ecf20Sopenharmony_ci ahd->seltime = (aic79xx_seltime & 0x3) << 4; 13198c2ecf20Sopenharmony_ci return (0); 13208c2ecf20Sopenharmony_ci} 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_civoid 13238c2ecf20Sopenharmony_ciahd_platform_free(struct ahd_softc *ahd) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci struct scsi_target *starget; 13268c2ecf20Sopenharmony_ci int i; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (ahd->platform_data != NULL) { 13298c2ecf20Sopenharmony_ci /* destroy all of the device and target objects */ 13308c2ecf20Sopenharmony_ci for (i = 0; i < AHD_NUM_TARGETS; i++) { 13318c2ecf20Sopenharmony_ci starget = ahd->platform_data->starget[i]; 13328c2ecf20Sopenharmony_ci if (starget != NULL) { 13338c2ecf20Sopenharmony_ci ahd->platform_data->starget[i] = NULL; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci if (ahd->platform_data->irq != AHD_LINUX_NOIRQ) 13388c2ecf20Sopenharmony_ci free_irq(ahd->platform_data->irq, ahd); 13398c2ecf20Sopenharmony_ci if (ahd->tags[0] == BUS_SPACE_PIO 13408c2ecf20Sopenharmony_ci && ahd->bshs[0].ioport != 0) 13418c2ecf20Sopenharmony_ci release_region(ahd->bshs[0].ioport, 256); 13428c2ecf20Sopenharmony_ci if (ahd->tags[1] == BUS_SPACE_PIO 13438c2ecf20Sopenharmony_ci && ahd->bshs[1].ioport != 0) 13448c2ecf20Sopenharmony_ci release_region(ahd->bshs[1].ioport, 256); 13458c2ecf20Sopenharmony_ci if (ahd->tags[0] == BUS_SPACE_MEMIO 13468c2ecf20Sopenharmony_ci && ahd->bshs[0].maddr != NULL) { 13478c2ecf20Sopenharmony_ci iounmap(ahd->bshs[0].maddr); 13488c2ecf20Sopenharmony_ci release_mem_region(ahd->platform_data->mem_busaddr, 13498c2ecf20Sopenharmony_ci 0x1000); 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci if (ahd->platform_data->host) 13528c2ecf20Sopenharmony_ci scsi_host_put(ahd->platform_data->host); 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci kfree(ahd->platform_data); 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_civoid 13598c2ecf20Sopenharmony_ciahd_platform_init(struct ahd_softc *ahd) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci /* 13628c2ecf20Sopenharmony_ci * Lookup and commit any modified IO Cell options. 13638c2ecf20Sopenharmony_ci */ 13648c2ecf20Sopenharmony_ci if (ahd->unit < ARRAY_SIZE(aic79xx_iocell_info)) { 13658c2ecf20Sopenharmony_ci const struct ahd_linux_iocell_opts *iocell_opts; 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci iocell_opts = &aic79xx_iocell_info[ahd->unit]; 13688c2ecf20Sopenharmony_ci if (iocell_opts->precomp != AIC79XX_DEFAULT_PRECOMP) 13698c2ecf20Sopenharmony_ci AHD_SET_PRECOMP(ahd, iocell_opts->precomp); 13708c2ecf20Sopenharmony_ci if (iocell_opts->slewrate != AIC79XX_DEFAULT_SLEWRATE) 13718c2ecf20Sopenharmony_ci AHD_SET_SLEWRATE(ahd, iocell_opts->slewrate); 13728c2ecf20Sopenharmony_ci if (iocell_opts->amplitude != AIC79XX_DEFAULT_AMPLITUDE) 13738c2ecf20Sopenharmony_ci AHD_SET_AMPLITUDE(ahd, iocell_opts->amplitude); 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_civoid 13798c2ecf20Sopenharmony_ciahd_platform_freeze_devq(struct ahd_softc *ahd, struct scb *scb) 13808c2ecf20Sopenharmony_ci{ 13818c2ecf20Sopenharmony_ci ahd_platform_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), 13828c2ecf20Sopenharmony_ci SCB_GET_CHANNEL(ahd, scb), 13838c2ecf20Sopenharmony_ci SCB_GET_LUN(scb), SCB_LIST_NULL, 13848c2ecf20Sopenharmony_ci ROLE_UNKNOWN, CAM_REQUEUE_REQ); 13858c2ecf20Sopenharmony_ci} 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_civoid 13888c2ecf20Sopenharmony_ciahd_platform_set_tags(struct ahd_softc *ahd, struct scsi_device *sdev, 13898c2ecf20Sopenharmony_ci struct ahd_devinfo *devinfo, ahd_queue_alg alg) 13908c2ecf20Sopenharmony_ci{ 13918c2ecf20Sopenharmony_ci struct ahd_linux_device *dev; 13928c2ecf20Sopenharmony_ci int was_queuing; 13938c2ecf20Sopenharmony_ci int now_queuing; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci if (sdev == NULL) 13968c2ecf20Sopenharmony_ci return; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci dev = scsi_transport_device_data(sdev); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (dev == NULL) 14018c2ecf20Sopenharmony_ci return; 14028c2ecf20Sopenharmony_ci was_queuing = dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED); 14038c2ecf20Sopenharmony_ci switch (alg) { 14048c2ecf20Sopenharmony_ci default: 14058c2ecf20Sopenharmony_ci case AHD_QUEUE_NONE: 14068c2ecf20Sopenharmony_ci now_queuing = 0; 14078c2ecf20Sopenharmony_ci break; 14088c2ecf20Sopenharmony_ci case AHD_QUEUE_BASIC: 14098c2ecf20Sopenharmony_ci now_queuing = AHD_DEV_Q_BASIC; 14108c2ecf20Sopenharmony_ci break; 14118c2ecf20Sopenharmony_ci case AHD_QUEUE_TAGGED: 14128c2ecf20Sopenharmony_ci now_queuing = AHD_DEV_Q_TAGGED; 14138c2ecf20Sopenharmony_ci break; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) == 0 14168c2ecf20Sopenharmony_ci && (was_queuing != now_queuing) 14178c2ecf20Sopenharmony_ci && (dev->active != 0)) { 14188c2ecf20Sopenharmony_ci dev->flags |= AHD_DEV_FREEZE_TIL_EMPTY; 14198c2ecf20Sopenharmony_ci dev->qfrozen++; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci dev->flags &= ~(AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED|AHD_DEV_PERIODIC_OTAG); 14238c2ecf20Sopenharmony_ci if (now_queuing) { 14248c2ecf20Sopenharmony_ci u_int usertags; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci usertags = ahd_linux_user_tagdepth(ahd, devinfo); 14278c2ecf20Sopenharmony_ci if (!was_queuing) { 14288c2ecf20Sopenharmony_ci /* 14298c2ecf20Sopenharmony_ci * Start out aggressively and allow our 14308c2ecf20Sopenharmony_ci * dynamic queue depth algorithm to take 14318c2ecf20Sopenharmony_ci * care of the rest. 14328c2ecf20Sopenharmony_ci */ 14338c2ecf20Sopenharmony_ci dev->maxtags = usertags; 14348c2ecf20Sopenharmony_ci dev->openings = dev->maxtags - dev->active; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci if (dev->maxtags == 0) { 14378c2ecf20Sopenharmony_ci /* 14388c2ecf20Sopenharmony_ci * Queueing is disabled by the user. 14398c2ecf20Sopenharmony_ci */ 14408c2ecf20Sopenharmony_ci dev->openings = 1; 14418c2ecf20Sopenharmony_ci } else if (alg == AHD_QUEUE_TAGGED) { 14428c2ecf20Sopenharmony_ci dev->flags |= AHD_DEV_Q_TAGGED; 14438c2ecf20Sopenharmony_ci if (aic79xx_periodic_otag != 0) 14448c2ecf20Sopenharmony_ci dev->flags |= AHD_DEV_PERIODIC_OTAG; 14458c2ecf20Sopenharmony_ci } else 14468c2ecf20Sopenharmony_ci dev->flags |= AHD_DEV_Q_BASIC; 14478c2ecf20Sopenharmony_ci } else { 14488c2ecf20Sopenharmony_ci /* We can only have one opening. */ 14498c2ecf20Sopenharmony_ci dev->maxtags = 0; 14508c2ecf20Sopenharmony_ci dev->openings = 1 - dev->active; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) { 14548c2ecf20Sopenharmony_ci case AHD_DEV_Q_BASIC: 14558c2ecf20Sopenharmony_ci case AHD_DEV_Q_TAGGED: 14568c2ecf20Sopenharmony_ci scsi_change_queue_depth(sdev, 14578c2ecf20Sopenharmony_ci dev->openings + dev->active); 14588c2ecf20Sopenharmony_ci break; 14598c2ecf20Sopenharmony_ci default: 14608c2ecf20Sopenharmony_ci /* 14618c2ecf20Sopenharmony_ci * We allow the OS to queue 2 untagged transactions to 14628c2ecf20Sopenharmony_ci * us at any time even though we can only execute them 14638c2ecf20Sopenharmony_ci * serially on the controller/device. This should 14648c2ecf20Sopenharmony_ci * remove some latency. 14658c2ecf20Sopenharmony_ci */ 14668c2ecf20Sopenharmony_ci scsi_change_queue_depth(sdev, 1); 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci} 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ciint 14728c2ecf20Sopenharmony_ciahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel, 14738c2ecf20Sopenharmony_ci int lun, u_int tag, role_t role, uint32_t status) 14748c2ecf20Sopenharmony_ci{ 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_cistatic u_int 14798c2ecf20Sopenharmony_ciahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) 14808c2ecf20Sopenharmony_ci{ 14818c2ecf20Sopenharmony_ci static int warned_user; 14828c2ecf20Sopenharmony_ci u_int tags; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci tags = 0; 14858c2ecf20Sopenharmony_ci if ((ahd->user_discenable & devinfo->target_mask) != 0) { 14868c2ecf20Sopenharmony_ci if (ahd->unit >= ARRAY_SIZE(aic79xx_tag_info)) { 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (warned_user == 0) { 14898c2ecf20Sopenharmony_ci printk(KERN_WARNING 14908c2ecf20Sopenharmony_ci"aic79xx: WARNING: Insufficient tag_info instances\n" 14918c2ecf20Sopenharmony_ci"aic79xx: for installed controllers. Using defaults\n" 14928c2ecf20Sopenharmony_ci"aic79xx: Please update the aic79xx_tag_info array in\n" 14938c2ecf20Sopenharmony_ci"aic79xx: the aic79xx_osm.c source file.\n"); 14948c2ecf20Sopenharmony_ci warned_user++; 14958c2ecf20Sopenharmony_ci } 14968c2ecf20Sopenharmony_ci tags = AHD_MAX_QUEUE; 14978c2ecf20Sopenharmony_ci } else { 14988c2ecf20Sopenharmony_ci adapter_tag_info_t *tag_info; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci tag_info = &aic79xx_tag_info[ahd->unit]; 15018c2ecf20Sopenharmony_ci tags = tag_info->tag_commands[devinfo->target_offset]; 15028c2ecf20Sopenharmony_ci if (tags > AHD_MAX_QUEUE) 15038c2ecf20Sopenharmony_ci tags = AHD_MAX_QUEUE; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci } 15068c2ecf20Sopenharmony_ci return (tags); 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci/* 15108c2ecf20Sopenharmony_ci * Determines the queue depth for a given device. 15118c2ecf20Sopenharmony_ci */ 15128c2ecf20Sopenharmony_cistatic void 15138c2ecf20Sopenharmony_ciahd_linux_device_queue_depth(struct scsi_device *sdev) 15148c2ecf20Sopenharmony_ci{ 15158c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 15168c2ecf20Sopenharmony_ci u_int tags; 15178c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)sdev->host->hostdata); 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, 15208c2ecf20Sopenharmony_ci ahd->our_id, 15218c2ecf20Sopenharmony_ci sdev->sdev_target->id, sdev->lun, 15228c2ecf20Sopenharmony_ci sdev->sdev_target->channel == 0 ? 'A' : 'B', 15238c2ecf20Sopenharmony_ci ROLE_INITIATOR); 15248c2ecf20Sopenharmony_ci tags = ahd_linux_user_tagdepth(ahd, &devinfo); 15258c2ecf20Sopenharmony_ci if (tags != 0 && sdev->tagged_supported != 0) { 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci ahd_platform_set_tags(ahd, sdev, &devinfo, AHD_QUEUE_TAGGED); 15288c2ecf20Sopenharmony_ci ahd_send_async(ahd, devinfo.channel, devinfo.target, 15298c2ecf20Sopenharmony_ci devinfo.lun, AC_TRANSFER_NEG); 15308c2ecf20Sopenharmony_ci ahd_print_devinfo(ahd, &devinfo); 15318c2ecf20Sopenharmony_ci printk("Tagged Queuing enabled. Depth %d\n", tags); 15328c2ecf20Sopenharmony_ci } else { 15338c2ecf20Sopenharmony_ci ahd_platform_set_tags(ahd, sdev, &devinfo, AHD_QUEUE_NONE); 15348c2ecf20Sopenharmony_ci ahd_send_async(ahd, devinfo.channel, devinfo.target, 15358c2ecf20Sopenharmony_ci devinfo.lun, AC_TRANSFER_NEG); 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci} 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_cistatic int 15408c2ecf20Sopenharmony_ciahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev, 15418c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd) 15428c2ecf20Sopenharmony_ci{ 15438c2ecf20Sopenharmony_ci struct scb *scb; 15448c2ecf20Sopenharmony_ci struct hardware_scb *hscb; 15458c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo; 15468c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 15478c2ecf20Sopenharmony_ci u_int col_idx; 15488c2ecf20Sopenharmony_ci uint16_t mask; 15498c2ecf20Sopenharmony_ci unsigned long flags; 15508c2ecf20Sopenharmony_ci int nseg; 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci nseg = scsi_dma_map(cmd); 15538c2ecf20Sopenharmony_ci if (nseg < 0) 15548c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci /* 15598c2ecf20Sopenharmony_ci * Get an scb to use. 15608c2ecf20Sopenharmony_ci */ 15618c2ecf20Sopenharmony_ci tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, 15628c2ecf20Sopenharmony_ci cmd->device->id, &tstate); 15638c2ecf20Sopenharmony_ci if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) == 0 15648c2ecf20Sopenharmony_ci || (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { 15658c2ecf20Sopenharmony_ci col_idx = AHD_NEVER_COL_IDX; 15668c2ecf20Sopenharmony_ci } else { 15678c2ecf20Sopenharmony_ci col_idx = AHD_BUILD_COL_IDX(cmd->device->id, 15688c2ecf20Sopenharmony_ci cmd->device->lun); 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci if ((scb = ahd_get_scb(ahd, col_idx)) == NULL) { 15718c2ecf20Sopenharmony_ci ahd->flags |= AHD_RESOURCE_SHORTAGE; 15728c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 15738c2ecf20Sopenharmony_ci scsi_dma_unmap(cmd); 15748c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci scb->io_ctx = cmd; 15788c2ecf20Sopenharmony_ci scb->platform_data->dev = dev; 15798c2ecf20Sopenharmony_ci hscb = scb->hscb; 15808c2ecf20Sopenharmony_ci cmd->host_scribble = (char *)scb; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci /* 15838c2ecf20Sopenharmony_ci * Fill out basics of the HSCB. 15848c2ecf20Sopenharmony_ci */ 15858c2ecf20Sopenharmony_ci hscb->control = 0; 15868c2ecf20Sopenharmony_ci hscb->scsiid = BUILD_SCSIID(ahd, cmd); 15878c2ecf20Sopenharmony_ci hscb->lun = cmd->device->lun; 15888c2ecf20Sopenharmony_ci scb->hscb->task_management = 0; 15898c2ecf20Sopenharmony_ci mask = SCB_GET_TARGET_MASK(ahd, scb); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if ((ahd->user_discenable & mask) != 0) 15928c2ecf20Sopenharmony_ci hscb->control |= DISCENB; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) 15958c2ecf20Sopenharmony_ci scb->flags |= SCB_PACKETIZED; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci if ((tstate->auto_negotiate & mask) != 0) { 15988c2ecf20Sopenharmony_ci scb->flags |= SCB_AUTO_NEGOTIATE; 15998c2ecf20Sopenharmony_ci scb->hscb->control |= MK_MESSAGE; 16008c2ecf20Sopenharmony_ci } 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) { 16038c2ecf20Sopenharmony_ci if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH 16048c2ecf20Sopenharmony_ci && (dev->flags & AHD_DEV_Q_TAGGED) != 0) { 16058c2ecf20Sopenharmony_ci hscb->control |= MSG_ORDERED_TASK; 16068c2ecf20Sopenharmony_ci dev->commands_since_idle_or_otag = 0; 16078c2ecf20Sopenharmony_ci } else { 16088c2ecf20Sopenharmony_ci hscb->control |= MSG_SIMPLE_TASK; 16098c2ecf20Sopenharmony_ci } 16108c2ecf20Sopenharmony_ci } 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci hscb->cdb_len = cmd->cmd_len; 16138c2ecf20Sopenharmony_ci memcpy(hscb->shared_data.idata.cdb, cmd->cmnd, hscb->cdb_len); 16148c2ecf20Sopenharmony_ci 16158c2ecf20Sopenharmony_ci scb->platform_data->xfer_len = 0; 16168c2ecf20Sopenharmony_ci ahd_set_residual(scb, 0); 16178c2ecf20Sopenharmony_ci ahd_set_sense_residual(scb, 0); 16188c2ecf20Sopenharmony_ci scb->sg_count = 0; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (nseg > 0) { 16218c2ecf20Sopenharmony_ci void *sg = scb->sg_list; 16228c2ecf20Sopenharmony_ci struct scatterlist *cur_seg; 16238c2ecf20Sopenharmony_ci int i; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci scb->platform_data->xfer_len = 0; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci scsi_for_each_sg(cmd, cur_seg, nseg, i) { 16288c2ecf20Sopenharmony_ci dma_addr_t addr; 16298c2ecf20Sopenharmony_ci bus_size_t len; 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci addr = sg_dma_address(cur_seg); 16328c2ecf20Sopenharmony_ci len = sg_dma_len(cur_seg); 16338c2ecf20Sopenharmony_ci scb->platform_data->xfer_len += len; 16348c2ecf20Sopenharmony_ci sg = ahd_sg_setup(ahd, scb, sg, addr, len, 16358c2ecf20Sopenharmony_ci i == (nseg - 1)); 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links); 16408c2ecf20Sopenharmony_ci dev->openings--; 16418c2ecf20Sopenharmony_ci dev->active++; 16428c2ecf20Sopenharmony_ci dev->commands_issued++; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if ((dev->flags & AHD_DEV_PERIODIC_OTAG) != 0) 16458c2ecf20Sopenharmony_ci dev->commands_since_idle_or_otag++; 16468c2ecf20Sopenharmony_ci scb->flags |= SCB_ACTIVE; 16478c2ecf20Sopenharmony_ci ahd_queue_scb(ahd, scb); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci return 0; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci/* 16558c2ecf20Sopenharmony_ci * SCSI controller interrupt handler. 16568c2ecf20Sopenharmony_ci */ 16578c2ecf20Sopenharmony_ciirqreturn_t 16588c2ecf20Sopenharmony_ciahd_linux_isr(int irq, void *dev_id) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci struct ahd_softc *ahd; 16618c2ecf20Sopenharmony_ci u_long flags; 16628c2ecf20Sopenharmony_ci int ours; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci ahd = (struct ahd_softc *) dev_id; 16658c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 16668c2ecf20Sopenharmony_ci ours = ahd_intr(ahd); 16678c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 16688c2ecf20Sopenharmony_ci return IRQ_RETVAL(ours); 16698c2ecf20Sopenharmony_ci} 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_civoid 16728c2ecf20Sopenharmony_ciahd_send_async(struct ahd_softc *ahd, char channel, 16738c2ecf20Sopenharmony_ci u_int target, u_int lun, ac_code code) 16748c2ecf20Sopenharmony_ci{ 16758c2ecf20Sopenharmony_ci switch (code) { 16768c2ecf20Sopenharmony_ci case AC_TRANSFER_NEG: 16778c2ecf20Sopenharmony_ci { 16788c2ecf20Sopenharmony_ci struct scsi_target *starget; 16798c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo; 16808c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 16818c2ecf20Sopenharmony_ci unsigned int target_ppr_options; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci BUG_ON(target == CAM_TARGET_WILDCARD); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id, 16868c2ecf20Sopenharmony_ci target, &tstate); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* 16898c2ecf20Sopenharmony_ci * Don't bother reporting results while 16908c2ecf20Sopenharmony_ci * negotiations are still pending. 16918c2ecf20Sopenharmony_ci */ 16928c2ecf20Sopenharmony_ci if (tinfo->curr.period != tinfo->goal.period 16938c2ecf20Sopenharmony_ci || tinfo->curr.width != tinfo->goal.width 16948c2ecf20Sopenharmony_ci || tinfo->curr.offset != tinfo->goal.offset 16958c2ecf20Sopenharmony_ci || tinfo->curr.ppr_options != tinfo->goal.ppr_options) 16968c2ecf20Sopenharmony_ci if (bootverbose == 0) 16978c2ecf20Sopenharmony_ci break; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci /* 17008c2ecf20Sopenharmony_ci * Don't bother reporting results that 17018c2ecf20Sopenharmony_ci * are identical to those last reported. 17028c2ecf20Sopenharmony_ci */ 17038c2ecf20Sopenharmony_ci starget = ahd->platform_data->starget[target]; 17048c2ecf20Sopenharmony_ci if (starget == NULL) 17058c2ecf20Sopenharmony_ci break; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci target_ppr_options = 17088c2ecf20Sopenharmony_ci (spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0) 17098c2ecf20Sopenharmony_ci + (spi_qas(starget) ? MSG_EXT_PPR_QAS_REQ : 0) 17108c2ecf20Sopenharmony_ci + (spi_iu(starget) ? MSG_EXT_PPR_IU_REQ : 0) 17118c2ecf20Sopenharmony_ci + (spi_rd_strm(starget) ? MSG_EXT_PPR_RD_STRM : 0) 17128c2ecf20Sopenharmony_ci + (spi_pcomp_en(starget) ? MSG_EXT_PPR_PCOMP_EN : 0) 17138c2ecf20Sopenharmony_ci + (spi_rti(starget) ? MSG_EXT_PPR_RTI : 0) 17148c2ecf20Sopenharmony_ci + (spi_wr_flow(starget) ? MSG_EXT_PPR_WR_FLOW : 0) 17158c2ecf20Sopenharmony_ci + (spi_hold_mcs(starget) ? MSG_EXT_PPR_HOLD_MCS : 0); 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci if (tinfo->curr.period == spi_period(starget) 17188c2ecf20Sopenharmony_ci && tinfo->curr.width == spi_width(starget) 17198c2ecf20Sopenharmony_ci && tinfo->curr.offset == spi_offset(starget) 17208c2ecf20Sopenharmony_ci && tinfo->curr.ppr_options == target_ppr_options) 17218c2ecf20Sopenharmony_ci if (bootverbose == 0) 17228c2ecf20Sopenharmony_ci break; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci spi_period(starget) = tinfo->curr.period; 17258c2ecf20Sopenharmony_ci spi_width(starget) = tinfo->curr.width; 17268c2ecf20Sopenharmony_ci spi_offset(starget) = tinfo->curr.offset; 17278c2ecf20Sopenharmony_ci spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ ? 1 : 0; 17288c2ecf20Sopenharmony_ci spi_qas(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ ? 1 : 0; 17298c2ecf20Sopenharmony_ci spi_iu(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ ? 1 : 0; 17308c2ecf20Sopenharmony_ci spi_rd_strm(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_RD_STRM ? 1 : 0; 17318c2ecf20Sopenharmony_ci spi_pcomp_en(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_PCOMP_EN ? 1 : 0; 17328c2ecf20Sopenharmony_ci spi_rti(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_RTI ? 1 : 0; 17338c2ecf20Sopenharmony_ci spi_wr_flow(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_WR_FLOW ? 1 : 0; 17348c2ecf20Sopenharmony_ci spi_hold_mcs(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_HOLD_MCS ? 1 : 0; 17358c2ecf20Sopenharmony_ci spi_display_xfer_agreement(starget); 17368c2ecf20Sopenharmony_ci break; 17378c2ecf20Sopenharmony_ci } 17388c2ecf20Sopenharmony_ci case AC_SENT_BDR: 17398c2ecf20Sopenharmony_ci { 17408c2ecf20Sopenharmony_ci WARN_ON(lun != CAM_LUN_WILDCARD); 17418c2ecf20Sopenharmony_ci scsi_report_device_reset(ahd->platform_data->host, 17428c2ecf20Sopenharmony_ci channel - 'A', target); 17438c2ecf20Sopenharmony_ci break; 17448c2ecf20Sopenharmony_ci } 17458c2ecf20Sopenharmony_ci case AC_BUS_RESET: 17468c2ecf20Sopenharmony_ci if (ahd->platform_data->host != NULL) { 17478c2ecf20Sopenharmony_ci scsi_report_bus_reset(ahd->platform_data->host, 17488c2ecf20Sopenharmony_ci channel - 'A'); 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci break; 17518c2ecf20Sopenharmony_ci default: 17528c2ecf20Sopenharmony_ci panic("ahd_send_async: Unexpected async event"); 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci/* 17578c2ecf20Sopenharmony_ci * Calls the higher level scsi done function and frees the scb. 17588c2ecf20Sopenharmony_ci */ 17598c2ecf20Sopenharmony_civoid 17608c2ecf20Sopenharmony_ciahd_done(struct ahd_softc *ahd, struct scb *scb) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd; 17638c2ecf20Sopenharmony_ci struct ahd_linux_device *dev; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if ((scb->flags & SCB_ACTIVE) == 0) { 17668c2ecf20Sopenharmony_ci printk("SCB %d done'd twice\n", SCB_GET_TAG(scb)); 17678c2ecf20Sopenharmony_ci ahd_dump_card_state(ahd); 17688c2ecf20Sopenharmony_ci panic("Stopping for safety"); 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci LIST_REMOVE(scb, pending_links); 17718c2ecf20Sopenharmony_ci cmd = scb->io_ctx; 17728c2ecf20Sopenharmony_ci dev = scb->platform_data->dev; 17738c2ecf20Sopenharmony_ci dev->active--; 17748c2ecf20Sopenharmony_ci dev->openings++; 17758c2ecf20Sopenharmony_ci if ((cmd->result & (CAM_DEV_QFRZN << 16)) != 0) { 17768c2ecf20Sopenharmony_ci cmd->result &= ~(CAM_DEV_QFRZN << 16); 17778c2ecf20Sopenharmony_ci dev->qfrozen--; 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci ahd_linux_unmap_scb(ahd, scb); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci /* 17828c2ecf20Sopenharmony_ci * Guard against stale sense data. 17838c2ecf20Sopenharmony_ci * The Linux mid-layer assumes that sense 17848c2ecf20Sopenharmony_ci * was retrieved anytime the first byte of 17858c2ecf20Sopenharmony_ci * the sense buffer looks "sane". 17868c2ecf20Sopenharmony_ci */ 17878c2ecf20Sopenharmony_ci cmd->sense_buffer[0] = 0; 17888c2ecf20Sopenharmony_ci if (ahd_get_transaction_status(scb) == CAM_REQ_INPROG) { 17898c2ecf20Sopenharmony_ci#ifdef AHD_REPORT_UNDERFLOWS 17908c2ecf20Sopenharmony_ci uint32_t amount_xferred; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci amount_xferred = 17938c2ecf20Sopenharmony_ci ahd_get_transfer_length(scb) - ahd_get_residual(scb); 17948c2ecf20Sopenharmony_ci#endif 17958c2ecf20Sopenharmony_ci if ((scb->flags & SCB_TRANSMISSION_ERROR) != 0) { 17968c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 17978c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_MISC) != 0) { 17988c2ecf20Sopenharmony_ci ahd_print_path(ahd, scb); 17998c2ecf20Sopenharmony_ci printk("Set CAM_UNCOR_PARITY\n"); 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci#endif 18028c2ecf20Sopenharmony_ci ahd_set_transaction_status(scb, CAM_UNCOR_PARITY); 18038c2ecf20Sopenharmony_ci#ifdef AHD_REPORT_UNDERFLOWS 18048c2ecf20Sopenharmony_ci /* 18058c2ecf20Sopenharmony_ci * This code is disabled by default as some 18068c2ecf20Sopenharmony_ci * clients of the SCSI system do not properly 18078c2ecf20Sopenharmony_ci * initialize the underflow parameter. This 18088c2ecf20Sopenharmony_ci * results in spurious termination of commands 18098c2ecf20Sopenharmony_ci * that complete as expected (e.g. underflow is 18108c2ecf20Sopenharmony_ci * allowed as command can return variable amounts 18118c2ecf20Sopenharmony_ci * of data. 18128c2ecf20Sopenharmony_ci */ 18138c2ecf20Sopenharmony_ci } else if (amount_xferred < scb->io_ctx->underflow) { 18148c2ecf20Sopenharmony_ci u_int i; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci ahd_print_path(ahd, scb); 18178c2ecf20Sopenharmony_ci printk("CDB:"); 18188c2ecf20Sopenharmony_ci for (i = 0; i < scb->io_ctx->cmd_len; i++) 18198c2ecf20Sopenharmony_ci printk(" 0x%x", scb->io_ctx->cmnd[i]); 18208c2ecf20Sopenharmony_ci printk("\n"); 18218c2ecf20Sopenharmony_ci ahd_print_path(ahd, scb); 18228c2ecf20Sopenharmony_ci printk("Saw underflow (%ld of %ld bytes). " 18238c2ecf20Sopenharmony_ci "Treated as error\n", 18248c2ecf20Sopenharmony_ci ahd_get_residual(scb), 18258c2ecf20Sopenharmony_ci ahd_get_transfer_length(scb)); 18268c2ecf20Sopenharmony_ci ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR); 18278c2ecf20Sopenharmony_ci#endif 18288c2ecf20Sopenharmony_ci } else { 18298c2ecf20Sopenharmony_ci ahd_set_transaction_status(scb, CAM_REQ_CMP); 18308c2ecf20Sopenharmony_ci } 18318c2ecf20Sopenharmony_ci } else if (ahd_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) { 18328c2ecf20Sopenharmony_ci ahd_linux_handle_scsi_status(ahd, cmd->device, scb); 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci if (dev->openings == 1 18368c2ecf20Sopenharmony_ci && ahd_get_transaction_status(scb) == CAM_REQ_CMP 18378c2ecf20Sopenharmony_ci && ahd_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL) 18388c2ecf20Sopenharmony_ci dev->tag_success_count++; 18398c2ecf20Sopenharmony_ci /* 18408c2ecf20Sopenharmony_ci * Some devices deal with temporary internal resource 18418c2ecf20Sopenharmony_ci * shortages by returning queue full. When the queue 18428c2ecf20Sopenharmony_ci * full occurrs, we throttle back. Slowly try to get 18438c2ecf20Sopenharmony_ci * back to our previous queue depth. 18448c2ecf20Sopenharmony_ci */ 18458c2ecf20Sopenharmony_ci if ((dev->openings + dev->active) < dev->maxtags 18468c2ecf20Sopenharmony_ci && dev->tag_success_count > AHD_TAG_SUCCESS_INTERVAL) { 18478c2ecf20Sopenharmony_ci dev->tag_success_count = 0; 18488c2ecf20Sopenharmony_ci dev->openings++; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci if (dev->active == 0) 18528c2ecf20Sopenharmony_ci dev->commands_since_idle_or_otag = 0; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci if ((scb->flags & SCB_RECOVERY_SCB) != 0) { 18558c2ecf20Sopenharmony_ci printk("Recovery SCB completes\n"); 18568c2ecf20Sopenharmony_ci if (ahd_get_transaction_status(scb) == CAM_BDR_SENT 18578c2ecf20Sopenharmony_ci || ahd_get_transaction_status(scb) == CAM_REQ_ABORTED) 18588c2ecf20Sopenharmony_ci ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT); 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci if (ahd->platform_data->eh_done) 18618c2ecf20Sopenharmony_ci complete(ahd->platform_data->eh_done); 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci ahd_free_scb(ahd, scb); 18658c2ecf20Sopenharmony_ci ahd_linux_queue_cmd_complete(ahd, cmd); 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistatic void 18698c2ecf20Sopenharmony_ciahd_linux_handle_scsi_status(struct ahd_softc *ahd, 18708c2ecf20Sopenharmony_ci struct scsi_device *sdev, struct scb *scb) 18718c2ecf20Sopenharmony_ci{ 18728c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 18738c2ecf20Sopenharmony_ci struct ahd_linux_device *dev = scsi_transport_device_data(sdev); 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, 18768c2ecf20Sopenharmony_ci ahd->our_id, 18778c2ecf20Sopenharmony_ci sdev->sdev_target->id, sdev->lun, 18788c2ecf20Sopenharmony_ci sdev->sdev_target->channel == 0 ? 'A' : 'B', 18798c2ecf20Sopenharmony_ci ROLE_INITIATOR); 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci /* 18828c2ecf20Sopenharmony_ci * We don't currently trust the mid-layer to 18838c2ecf20Sopenharmony_ci * properly deal with queue full or busy. So, 18848c2ecf20Sopenharmony_ci * when one occurs, we tell the mid-layer to 18858c2ecf20Sopenharmony_ci * unconditionally requeue the command to us 18868c2ecf20Sopenharmony_ci * so that we can retry it ourselves. We also 18878c2ecf20Sopenharmony_ci * implement our own throttling mechanism so 18888c2ecf20Sopenharmony_ci * we don't clobber the device with too many 18898c2ecf20Sopenharmony_ci * commands. 18908c2ecf20Sopenharmony_ci */ 18918c2ecf20Sopenharmony_ci switch (ahd_get_scsi_status(scb)) { 18928c2ecf20Sopenharmony_ci default: 18938c2ecf20Sopenharmony_ci break; 18948c2ecf20Sopenharmony_ci case SCSI_STATUS_CHECK_COND: 18958c2ecf20Sopenharmony_ci case SCSI_STATUS_CMD_TERMINATED: 18968c2ecf20Sopenharmony_ci { 18978c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci /* 19008c2ecf20Sopenharmony_ci * Copy sense information to the OS's cmd 19018c2ecf20Sopenharmony_ci * structure if it is available. 19028c2ecf20Sopenharmony_ci */ 19038c2ecf20Sopenharmony_ci cmd = scb->io_ctx; 19048c2ecf20Sopenharmony_ci if ((scb->flags & (SCB_SENSE|SCB_PKT_SENSE)) != 0) { 19058c2ecf20Sopenharmony_ci struct scsi_status_iu_header *siu; 19068c2ecf20Sopenharmony_ci u_int sense_size; 19078c2ecf20Sopenharmony_ci u_int sense_offset; 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci if (scb->flags & SCB_SENSE) { 19108c2ecf20Sopenharmony_ci sense_size = min(sizeof(struct scsi_sense_data) 19118c2ecf20Sopenharmony_ci - ahd_get_sense_residual(scb), 19128c2ecf20Sopenharmony_ci (u_long)SCSI_SENSE_BUFFERSIZE); 19138c2ecf20Sopenharmony_ci sense_offset = 0; 19148c2ecf20Sopenharmony_ci } else { 19158c2ecf20Sopenharmony_ci /* 19168c2ecf20Sopenharmony_ci * Copy only the sense data into the provided 19178c2ecf20Sopenharmony_ci * buffer. 19188c2ecf20Sopenharmony_ci */ 19198c2ecf20Sopenharmony_ci siu = (struct scsi_status_iu_header *) 19208c2ecf20Sopenharmony_ci scb->sense_data; 19218c2ecf20Sopenharmony_ci sense_size = min_t(size_t, 19228c2ecf20Sopenharmony_ci scsi_4btoul(siu->sense_length), 19238c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE); 19248c2ecf20Sopenharmony_ci sense_offset = SIU_SENSE_OFFSET(siu); 19258c2ecf20Sopenharmony_ci } 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); 19288c2ecf20Sopenharmony_ci memcpy(cmd->sense_buffer, 19298c2ecf20Sopenharmony_ci ahd_get_sense_buf(ahd, scb) 19308c2ecf20Sopenharmony_ci + sense_offset, sense_size); 19318c2ecf20Sopenharmony_ci cmd->result |= (DRIVER_SENSE << 24); 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 19348c2ecf20Sopenharmony_ci if (ahd_debug & AHD_SHOW_SENSE) { 19358c2ecf20Sopenharmony_ci int i; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci printk("Copied %d bytes of sense data at %d:", 19388c2ecf20Sopenharmony_ci sense_size, sense_offset); 19398c2ecf20Sopenharmony_ci for (i = 0; i < sense_size; i++) { 19408c2ecf20Sopenharmony_ci if ((i & 0xF) == 0) 19418c2ecf20Sopenharmony_ci printk("\n"); 19428c2ecf20Sopenharmony_ci printk("0x%x ", cmd->sense_buffer[i]); 19438c2ecf20Sopenharmony_ci } 19448c2ecf20Sopenharmony_ci printk("\n"); 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci#endif 19478c2ecf20Sopenharmony_ci } 19488c2ecf20Sopenharmony_ci break; 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci case SCSI_STATUS_QUEUE_FULL: 19518c2ecf20Sopenharmony_ci /* 19528c2ecf20Sopenharmony_ci * By the time the core driver has returned this 19538c2ecf20Sopenharmony_ci * command, all other commands that were queued 19548c2ecf20Sopenharmony_ci * to us but not the device have been returned. 19558c2ecf20Sopenharmony_ci * This ensures that dev->active is equal to 19568c2ecf20Sopenharmony_ci * the number of commands actually queued to 19578c2ecf20Sopenharmony_ci * the device. 19588c2ecf20Sopenharmony_ci */ 19598c2ecf20Sopenharmony_ci dev->tag_success_count = 0; 19608c2ecf20Sopenharmony_ci if (dev->active != 0) { 19618c2ecf20Sopenharmony_ci /* 19628c2ecf20Sopenharmony_ci * Drop our opening count to the number 19638c2ecf20Sopenharmony_ci * of commands currently outstanding. 19648c2ecf20Sopenharmony_ci */ 19658c2ecf20Sopenharmony_ci dev->openings = 0; 19668c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 19678c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_QFULL) != 0) { 19688c2ecf20Sopenharmony_ci ahd_print_path(ahd, scb); 19698c2ecf20Sopenharmony_ci printk("Dropping tag count to %d\n", 19708c2ecf20Sopenharmony_ci dev->active); 19718c2ecf20Sopenharmony_ci } 19728c2ecf20Sopenharmony_ci#endif 19738c2ecf20Sopenharmony_ci if (dev->active == dev->tags_on_last_queuefull) { 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci dev->last_queuefull_same_count++; 19768c2ecf20Sopenharmony_ci /* 19778c2ecf20Sopenharmony_ci * If we repeatedly see a queue full 19788c2ecf20Sopenharmony_ci * at the same queue depth, this 19798c2ecf20Sopenharmony_ci * device has a fixed number of tag 19808c2ecf20Sopenharmony_ci * slots. Lock in this tag depth 19818c2ecf20Sopenharmony_ci * so we stop seeing queue fulls from 19828c2ecf20Sopenharmony_ci * this device. 19838c2ecf20Sopenharmony_ci */ 19848c2ecf20Sopenharmony_ci if (dev->last_queuefull_same_count 19858c2ecf20Sopenharmony_ci == AHD_LOCK_TAGS_COUNT) { 19868c2ecf20Sopenharmony_ci dev->maxtags = dev->active; 19878c2ecf20Sopenharmony_ci ahd_print_path(ahd, scb); 19888c2ecf20Sopenharmony_ci printk("Locking max tag count at %d\n", 19898c2ecf20Sopenharmony_ci dev->active); 19908c2ecf20Sopenharmony_ci } 19918c2ecf20Sopenharmony_ci } else { 19928c2ecf20Sopenharmony_ci dev->tags_on_last_queuefull = dev->active; 19938c2ecf20Sopenharmony_ci dev->last_queuefull_same_count = 0; 19948c2ecf20Sopenharmony_ci } 19958c2ecf20Sopenharmony_ci ahd_set_transaction_status(scb, CAM_REQUEUE_REQ); 19968c2ecf20Sopenharmony_ci ahd_set_scsi_status(scb, SCSI_STATUS_OK); 19978c2ecf20Sopenharmony_ci ahd_platform_set_tags(ahd, sdev, &devinfo, 19988c2ecf20Sopenharmony_ci (dev->flags & AHD_DEV_Q_BASIC) 19998c2ecf20Sopenharmony_ci ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED); 20008c2ecf20Sopenharmony_ci break; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci /* 20038c2ecf20Sopenharmony_ci * Drop down to a single opening, and treat this 20048c2ecf20Sopenharmony_ci * as if the target returned BUSY SCSI status. 20058c2ecf20Sopenharmony_ci */ 20068c2ecf20Sopenharmony_ci dev->openings = 1; 20078c2ecf20Sopenharmony_ci ahd_platform_set_tags(ahd, sdev, &devinfo, 20088c2ecf20Sopenharmony_ci (dev->flags & AHD_DEV_Q_BASIC) 20098c2ecf20Sopenharmony_ci ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED); 20108c2ecf20Sopenharmony_ci ahd_set_scsi_status(scb, SCSI_STATUS_BUSY); 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_cistatic void 20158c2ecf20Sopenharmony_ciahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd) 20168c2ecf20Sopenharmony_ci{ 20178c2ecf20Sopenharmony_ci int status; 20188c2ecf20Sopenharmony_ci int new_status = DID_OK; 20198c2ecf20Sopenharmony_ci int do_fallback = 0; 20208c2ecf20Sopenharmony_ci int scsi_status; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci /* 20238c2ecf20Sopenharmony_ci * Map CAM error codes into Linux Error codes. We 20248c2ecf20Sopenharmony_ci * avoid the conversion so that the DV code has the 20258c2ecf20Sopenharmony_ci * full error information available when making 20268c2ecf20Sopenharmony_ci * state change decisions. 20278c2ecf20Sopenharmony_ci */ 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci status = ahd_cmd_get_transaction_status(cmd); 20308c2ecf20Sopenharmony_ci switch (status) { 20318c2ecf20Sopenharmony_ci case CAM_REQ_INPROG: 20328c2ecf20Sopenharmony_ci case CAM_REQ_CMP: 20338c2ecf20Sopenharmony_ci new_status = DID_OK; 20348c2ecf20Sopenharmony_ci break; 20358c2ecf20Sopenharmony_ci case CAM_AUTOSENSE_FAIL: 20368c2ecf20Sopenharmony_ci new_status = DID_ERROR; 20378c2ecf20Sopenharmony_ci fallthrough; 20388c2ecf20Sopenharmony_ci case CAM_SCSI_STATUS_ERROR: 20398c2ecf20Sopenharmony_ci scsi_status = ahd_cmd_get_scsi_status(cmd); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci switch(scsi_status) { 20428c2ecf20Sopenharmony_ci case SCSI_STATUS_CMD_TERMINATED: 20438c2ecf20Sopenharmony_ci case SCSI_STATUS_CHECK_COND: 20448c2ecf20Sopenharmony_ci if ((cmd->result >> 24) != DRIVER_SENSE) { 20458c2ecf20Sopenharmony_ci do_fallback = 1; 20468c2ecf20Sopenharmony_ci } else { 20478c2ecf20Sopenharmony_ci struct scsi_sense_data *sense; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci sense = (struct scsi_sense_data *) 20508c2ecf20Sopenharmony_ci cmd->sense_buffer; 20518c2ecf20Sopenharmony_ci if (sense->extra_len >= 5 && 20528c2ecf20Sopenharmony_ci (sense->add_sense_code == 0x47 20538c2ecf20Sopenharmony_ci || sense->add_sense_code == 0x48)) 20548c2ecf20Sopenharmony_ci do_fallback = 1; 20558c2ecf20Sopenharmony_ci } 20568c2ecf20Sopenharmony_ci break; 20578c2ecf20Sopenharmony_ci default: 20588c2ecf20Sopenharmony_ci break; 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci break; 20618c2ecf20Sopenharmony_ci case CAM_REQ_ABORTED: 20628c2ecf20Sopenharmony_ci new_status = DID_ABORT; 20638c2ecf20Sopenharmony_ci break; 20648c2ecf20Sopenharmony_ci case CAM_BUSY: 20658c2ecf20Sopenharmony_ci new_status = DID_BUS_BUSY; 20668c2ecf20Sopenharmony_ci break; 20678c2ecf20Sopenharmony_ci case CAM_REQ_INVALID: 20688c2ecf20Sopenharmony_ci case CAM_PATH_INVALID: 20698c2ecf20Sopenharmony_ci new_status = DID_BAD_TARGET; 20708c2ecf20Sopenharmony_ci break; 20718c2ecf20Sopenharmony_ci case CAM_SEL_TIMEOUT: 20728c2ecf20Sopenharmony_ci new_status = DID_NO_CONNECT; 20738c2ecf20Sopenharmony_ci break; 20748c2ecf20Sopenharmony_ci case CAM_SCSI_BUS_RESET: 20758c2ecf20Sopenharmony_ci case CAM_BDR_SENT: 20768c2ecf20Sopenharmony_ci new_status = DID_RESET; 20778c2ecf20Sopenharmony_ci break; 20788c2ecf20Sopenharmony_ci case CAM_UNCOR_PARITY: 20798c2ecf20Sopenharmony_ci new_status = DID_PARITY; 20808c2ecf20Sopenharmony_ci do_fallback = 1; 20818c2ecf20Sopenharmony_ci break; 20828c2ecf20Sopenharmony_ci case CAM_CMD_TIMEOUT: 20838c2ecf20Sopenharmony_ci new_status = DID_TIME_OUT; 20848c2ecf20Sopenharmony_ci do_fallback = 1; 20858c2ecf20Sopenharmony_ci break; 20868c2ecf20Sopenharmony_ci case CAM_REQ_CMP_ERR: 20878c2ecf20Sopenharmony_ci case CAM_UNEXP_BUSFREE: 20888c2ecf20Sopenharmony_ci case CAM_DATA_RUN_ERR: 20898c2ecf20Sopenharmony_ci new_status = DID_ERROR; 20908c2ecf20Sopenharmony_ci do_fallback = 1; 20918c2ecf20Sopenharmony_ci break; 20928c2ecf20Sopenharmony_ci case CAM_UA_ABORT: 20938c2ecf20Sopenharmony_ci case CAM_NO_HBA: 20948c2ecf20Sopenharmony_ci case CAM_SEQUENCE_FAIL: 20958c2ecf20Sopenharmony_ci case CAM_CCB_LEN_ERR: 20968c2ecf20Sopenharmony_ci case CAM_PROVIDE_FAIL: 20978c2ecf20Sopenharmony_ci case CAM_REQ_TERMIO: 20988c2ecf20Sopenharmony_ci case CAM_UNREC_HBA_ERROR: 20998c2ecf20Sopenharmony_ci case CAM_REQ_TOO_BIG: 21008c2ecf20Sopenharmony_ci new_status = DID_ERROR; 21018c2ecf20Sopenharmony_ci break; 21028c2ecf20Sopenharmony_ci case CAM_REQUEUE_REQ: 21038c2ecf20Sopenharmony_ci new_status = DID_REQUEUE; 21048c2ecf20Sopenharmony_ci break; 21058c2ecf20Sopenharmony_ci default: 21068c2ecf20Sopenharmony_ci /* We should never get here */ 21078c2ecf20Sopenharmony_ci new_status = DID_ERROR; 21088c2ecf20Sopenharmony_ci break; 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci if (do_fallback) { 21128c2ecf20Sopenharmony_ci printk("%s: device overrun (status %x) on %d:%d:%d\n", 21138c2ecf20Sopenharmony_ci ahd_name(ahd), status, cmd->device->channel, 21148c2ecf20Sopenharmony_ci cmd->device->id, (u8)cmd->device->lun); 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci ahd_cmd_set_transaction_status(cmd, new_status); 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci cmd->scsi_done(cmd); 21208c2ecf20Sopenharmony_ci} 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_cistatic void 21238c2ecf20Sopenharmony_ciahd_freeze_simq(struct ahd_softc *ahd) 21248c2ecf20Sopenharmony_ci{ 21258c2ecf20Sopenharmony_ci scsi_block_requests(ahd->platform_data->host); 21268c2ecf20Sopenharmony_ci} 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_cistatic void 21298c2ecf20Sopenharmony_ciahd_release_simq(struct ahd_softc *ahd) 21308c2ecf20Sopenharmony_ci{ 21318c2ecf20Sopenharmony_ci scsi_unblock_requests(ahd->platform_data->host); 21328c2ecf20Sopenharmony_ci} 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_cistatic int 21358c2ecf20Sopenharmony_ciahd_linux_queue_abort_cmd(struct scsi_cmnd *cmd) 21368c2ecf20Sopenharmony_ci{ 21378c2ecf20Sopenharmony_ci struct ahd_softc *ahd; 21388c2ecf20Sopenharmony_ci struct ahd_linux_device *dev; 21398c2ecf20Sopenharmony_ci struct scb *pending_scb; 21408c2ecf20Sopenharmony_ci u_int saved_scbptr; 21418c2ecf20Sopenharmony_ci u_int active_scbptr; 21428c2ecf20Sopenharmony_ci u_int last_phase; 21438c2ecf20Sopenharmony_ci u_int saved_scsiid; 21448c2ecf20Sopenharmony_ci u_int cdb_byte; 21458c2ecf20Sopenharmony_ci int retval = SUCCESS; 21468c2ecf20Sopenharmony_ci int was_paused; 21478c2ecf20Sopenharmony_ci int paused; 21488c2ecf20Sopenharmony_ci int wait; 21498c2ecf20Sopenharmony_ci int disconnected; 21508c2ecf20Sopenharmony_ci ahd_mode_state saved_modes; 21518c2ecf20Sopenharmony_ci unsigned long flags; 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci pending_scb = NULL; 21548c2ecf20Sopenharmony_ci paused = FALSE; 21558c2ecf20Sopenharmony_ci wait = FALSE; 21568c2ecf20Sopenharmony_ci ahd = *(struct ahd_softc **)cmd->device->host->hostdata; 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, 21598c2ecf20Sopenharmony_ci "Attempting to queue an ABORT message:"); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci printk("CDB:"); 21628c2ecf20Sopenharmony_ci for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++) 21638c2ecf20Sopenharmony_ci printk(" 0x%x", cmd->cmnd[cdb_byte]); 21648c2ecf20Sopenharmony_ci printk("\n"); 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci /* 21698c2ecf20Sopenharmony_ci * First determine if we currently own this command. 21708c2ecf20Sopenharmony_ci * Start by searching the device queue. If not found 21718c2ecf20Sopenharmony_ci * there, check the pending_scb list. If not found 21728c2ecf20Sopenharmony_ci * at all, and the system wanted us to just abort the 21738c2ecf20Sopenharmony_ci * command, return success. 21748c2ecf20Sopenharmony_ci */ 21758c2ecf20Sopenharmony_ci dev = scsi_transport_device_data(cmd->device); 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci if (dev == NULL) { 21788c2ecf20Sopenharmony_ci /* 21798c2ecf20Sopenharmony_ci * No target device for this command exists, 21808c2ecf20Sopenharmony_ci * so we must not still own the command. 21818c2ecf20Sopenharmony_ci */ 21828c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "Is not an active device\n"); 21838c2ecf20Sopenharmony_ci goto done; 21848c2ecf20Sopenharmony_ci } 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci /* 21878c2ecf20Sopenharmony_ci * See if we can find a matching cmd in the pending list. 21888c2ecf20Sopenharmony_ci */ 21898c2ecf20Sopenharmony_ci LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { 21908c2ecf20Sopenharmony_ci if (pending_scb->io_ctx == cmd) 21918c2ecf20Sopenharmony_ci break; 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci if (pending_scb == NULL) { 21958c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "Command not found\n"); 21968c2ecf20Sopenharmony_ci goto done; 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) { 22008c2ecf20Sopenharmony_ci /* 22018c2ecf20Sopenharmony_ci * We can't queue two recovery actions using the same SCB 22028c2ecf20Sopenharmony_ci */ 22038c2ecf20Sopenharmony_ci retval = FAILED; 22048c2ecf20Sopenharmony_ci goto done; 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci /* 22088c2ecf20Sopenharmony_ci * Ensure that the card doesn't do anything 22098c2ecf20Sopenharmony_ci * behind our back. Also make sure that we 22108c2ecf20Sopenharmony_ci * didn't "just" miss an interrupt that would 22118c2ecf20Sopenharmony_ci * affect this cmd. 22128c2ecf20Sopenharmony_ci */ 22138c2ecf20Sopenharmony_ci was_paused = ahd_is_paused(ahd); 22148c2ecf20Sopenharmony_ci ahd_pause_and_flushwork(ahd); 22158c2ecf20Sopenharmony_ci paused = TRUE; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if ((pending_scb->flags & SCB_ACTIVE) == 0) { 22188c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "Command already completed\n"); 22198c2ecf20Sopenharmony_ci goto done; 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci printk("%s: At time of recovery, card was %spaused\n", 22238c2ecf20Sopenharmony_ci ahd_name(ahd), was_paused ? "" : "not "); 22248c2ecf20Sopenharmony_ci ahd_dump_card_state(ahd); 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci disconnected = TRUE; 22278c2ecf20Sopenharmony_ci if (ahd_search_qinfifo(ahd, cmd->device->id, 22288c2ecf20Sopenharmony_ci cmd->device->channel + 'A', 22298c2ecf20Sopenharmony_ci cmd->device->lun, 22308c2ecf20Sopenharmony_ci pending_scb->hscb->tag, 22318c2ecf20Sopenharmony_ci ROLE_INITIATOR, CAM_REQ_ABORTED, 22328c2ecf20Sopenharmony_ci SEARCH_COMPLETE) > 0) { 22338c2ecf20Sopenharmony_ci printk("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", 22348c2ecf20Sopenharmony_ci ahd_name(ahd), cmd->device->channel, 22358c2ecf20Sopenharmony_ci cmd->device->id, (u8)cmd->device->lun); 22368c2ecf20Sopenharmony_ci goto done; 22378c2ecf20Sopenharmony_ci } 22388c2ecf20Sopenharmony_ci 22398c2ecf20Sopenharmony_ci saved_modes = ahd_save_modes(ahd); 22408c2ecf20Sopenharmony_ci ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); 22418c2ecf20Sopenharmony_ci last_phase = ahd_inb(ahd, LASTPHASE); 22428c2ecf20Sopenharmony_ci saved_scbptr = ahd_get_scbptr(ahd); 22438c2ecf20Sopenharmony_ci active_scbptr = saved_scbptr; 22448c2ecf20Sopenharmony_ci if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) { 22458c2ecf20Sopenharmony_ci struct scb *bus_scb; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci bus_scb = ahd_lookup_scb(ahd, active_scbptr); 22488c2ecf20Sopenharmony_ci if (bus_scb == pending_scb) 22498c2ecf20Sopenharmony_ci disconnected = FALSE; 22508c2ecf20Sopenharmony_ci } 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci /* 22538c2ecf20Sopenharmony_ci * At this point, pending_scb is the scb associated with the 22548c2ecf20Sopenharmony_ci * passed in command. That command is currently active on the 22558c2ecf20Sopenharmony_ci * bus or is in the disconnected state. 22568c2ecf20Sopenharmony_ci */ 22578c2ecf20Sopenharmony_ci saved_scsiid = ahd_inb(ahd, SAVED_SCSIID); 22588c2ecf20Sopenharmony_ci if (last_phase != P_BUSFREE 22598c2ecf20Sopenharmony_ci && SCB_GET_TAG(pending_scb) == active_scbptr) { 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci /* 22628c2ecf20Sopenharmony_ci * We're active on the bus, so assert ATN 22638c2ecf20Sopenharmony_ci * and hope that the target responds. 22648c2ecf20Sopenharmony_ci */ 22658c2ecf20Sopenharmony_ci pending_scb = ahd_lookup_scb(ahd, active_scbptr); 22668c2ecf20Sopenharmony_ci pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; 22678c2ecf20Sopenharmony_ci ahd_outb(ahd, MSG_OUT, HOST_MSG); 22688c2ecf20Sopenharmony_ci ahd_outb(ahd, SCSISIGO, last_phase|ATNO); 22698c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "Device is active, asserting ATN\n"); 22708c2ecf20Sopenharmony_ci wait = TRUE; 22718c2ecf20Sopenharmony_ci } else if (disconnected) { 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci /* 22748c2ecf20Sopenharmony_ci * Actually re-queue this SCB in an attempt 22758c2ecf20Sopenharmony_ci * to select the device before it reconnects. 22768c2ecf20Sopenharmony_ci */ 22778c2ecf20Sopenharmony_ci pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; 22788c2ecf20Sopenharmony_ci ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb)); 22798c2ecf20Sopenharmony_ci pending_scb->hscb->cdb_len = 0; 22808c2ecf20Sopenharmony_ci pending_scb->hscb->task_attribute = 0; 22818c2ecf20Sopenharmony_ci pending_scb->hscb->task_management = SIU_TASKMGMT_ABORT_TASK; 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci if ((pending_scb->flags & SCB_PACKETIZED) != 0) { 22848c2ecf20Sopenharmony_ci /* 22858c2ecf20Sopenharmony_ci * Mark the SCB has having an outstanding 22868c2ecf20Sopenharmony_ci * task management function. Should the command 22878c2ecf20Sopenharmony_ci * complete normally before the task management 22888c2ecf20Sopenharmony_ci * function can be sent, the host will be notified 22898c2ecf20Sopenharmony_ci * to abort our requeued SCB. 22908c2ecf20Sopenharmony_ci */ 22918c2ecf20Sopenharmony_ci ahd_outb(ahd, SCB_TASK_MANAGEMENT, 22928c2ecf20Sopenharmony_ci pending_scb->hscb->task_management); 22938c2ecf20Sopenharmony_ci } else { 22948c2ecf20Sopenharmony_ci /* 22958c2ecf20Sopenharmony_ci * If non-packetized, set the MK_MESSAGE control 22968c2ecf20Sopenharmony_ci * bit indicating that we desire to send a message. 22978c2ecf20Sopenharmony_ci * We also set the disconnected flag since there is 22988c2ecf20Sopenharmony_ci * no guarantee that our SCB control byte matches 22998c2ecf20Sopenharmony_ci * the version on the card. We don't want the 23008c2ecf20Sopenharmony_ci * sequencer to abort the command thinking an 23018c2ecf20Sopenharmony_ci * unsolicited reselection occurred. 23028c2ecf20Sopenharmony_ci */ 23038c2ecf20Sopenharmony_ci pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci /* 23068c2ecf20Sopenharmony_ci * The sequencer will never re-reference the 23078c2ecf20Sopenharmony_ci * in-core SCB. To make sure we are notified 23088c2ecf20Sopenharmony_ci * during reselection, set the MK_MESSAGE flag in 23098c2ecf20Sopenharmony_ci * the card's copy of the SCB. 23108c2ecf20Sopenharmony_ci */ 23118c2ecf20Sopenharmony_ci ahd_outb(ahd, SCB_CONTROL, 23128c2ecf20Sopenharmony_ci ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE); 23138c2ecf20Sopenharmony_ci } 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci /* 23168c2ecf20Sopenharmony_ci * Clear out any entries in the QINFIFO first 23178c2ecf20Sopenharmony_ci * so we are the next SCB for this target 23188c2ecf20Sopenharmony_ci * to run. 23198c2ecf20Sopenharmony_ci */ 23208c2ecf20Sopenharmony_ci ahd_search_qinfifo(ahd, cmd->device->id, 23218c2ecf20Sopenharmony_ci cmd->device->channel + 'A', cmd->device->lun, 23228c2ecf20Sopenharmony_ci SCB_LIST_NULL, ROLE_INITIATOR, 23238c2ecf20Sopenharmony_ci CAM_REQUEUE_REQ, SEARCH_COMPLETE); 23248c2ecf20Sopenharmony_ci ahd_qinfifo_requeue_tail(ahd, pending_scb); 23258c2ecf20Sopenharmony_ci ahd_set_scbptr(ahd, saved_scbptr); 23268c2ecf20Sopenharmony_ci ahd_print_path(ahd, pending_scb); 23278c2ecf20Sopenharmony_ci printk("Device is disconnected, re-queuing SCB\n"); 23288c2ecf20Sopenharmony_ci wait = TRUE; 23298c2ecf20Sopenharmony_ci } else { 23308c2ecf20Sopenharmony_ci scmd_printk(KERN_INFO, cmd, "Unable to deliver message\n"); 23318c2ecf20Sopenharmony_ci retval = FAILED; 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci ahd_restore_modes(ahd, saved_modes); 23368c2ecf20Sopenharmony_cidone: 23378c2ecf20Sopenharmony_ci if (paused) 23388c2ecf20Sopenharmony_ci ahd_unpause(ahd); 23398c2ecf20Sopenharmony_ci if (wait) { 23408c2ecf20Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci ahd->platform_data->eh_done = &done; 23438c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci printk("%s: Recovery code sleeping\n", ahd_name(ahd)); 23468c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&done, 5 * HZ)) { 23478c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 23488c2ecf20Sopenharmony_ci ahd->platform_data->eh_done = NULL; 23498c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 23508c2ecf20Sopenharmony_ci printk("%s: Timer Expired (active %d)\n", 23518c2ecf20Sopenharmony_ci ahd_name(ahd), dev->active); 23528c2ecf20Sopenharmony_ci retval = FAILED; 23538c2ecf20Sopenharmony_ci } 23548c2ecf20Sopenharmony_ci printk("Recovery code awake\n"); 23558c2ecf20Sopenharmony_ci } else 23568c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci if (retval != SUCCESS) 23598c2ecf20Sopenharmony_ci printk("%s: Command abort returning 0x%x\n", 23608c2ecf20Sopenharmony_ci ahd_name(ahd), retval); 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci return retval; 23638c2ecf20Sopenharmony_ci} 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_cistatic void ahd_linux_set_width(struct scsi_target *starget, int width) 23668c2ecf20Sopenharmony_ci{ 23678c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 23688c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 23698c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 23708c2ecf20Sopenharmony_ci unsigned long flags; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 23738c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 23748c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 23758c2ecf20Sopenharmony_ci ahd_set_width(ahd, &devinfo, width, AHD_TRANS_GOAL, FALSE); 23768c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 23778c2ecf20Sopenharmony_ci} 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cistatic void ahd_linux_set_period(struct scsi_target *starget, int period) 23808c2ecf20Sopenharmony_ci{ 23818c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 23828c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 23838c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 23848c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 23858c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 23868c2ecf20Sopenharmony_ci starget->channel + 'A', 23878c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 23888c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 23898c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options; 23908c2ecf20Sopenharmony_ci unsigned int dt; 23918c2ecf20Sopenharmony_ci unsigned long flags; 23928c2ecf20Sopenharmony_ci unsigned long offset = tinfo->goal.offset; 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 23958c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 23968c2ecf20Sopenharmony_ci printk("%s: set period to %d\n", ahd_name(ahd), period); 23978c2ecf20Sopenharmony_ci#endif 23988c2ecf20Sopenharmony_ci if (offset == 0) 23998c2ecf20Sopenharmony_ci offset = MAX_OFFSET; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci if (period < 8) 24028c2ecf20Sopenharmony_ci period = 8; 24038c2ecf20Sopenharmony_ci if (period < 10) { 24048c2ecf20Sopenharmony_ci if (spi_max_width(starget)) { 24058c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_DT_REQ; 24068c2ecf20Sopenharmony_ci if (period == 8) 24078c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_IU_REQ; 24088c2ecf20Sopenharmony_ci } else 24098c2ecf20Sopenharmony_ci period = 10; 24108c2ecf20Sopenharmony_ci } 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci dt = ppr_options & MSG_EXT_PPR_DT_REQ; 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 24158c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci /* all PPR requests apart from QAS require wide transfers */ 24188c2ecf20Sopenharmony_ci if (ppr_options & ~MSG_EXT_PPR_QAS_REQ) { 24198c2ecf20Sopenharmony_ci if (spi_width(starget) == 0) 24208c2ecf20Sopenharmony_ci ppr_options &= MSG_EXT_PPR_QAS_REQ; 24218c2ecf20Sopenharmony_ci } 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 24248c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 24278c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, offset, 24288c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 24298c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 24308c2ecf20Sopenharmony_ci} 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_cistatic void ahd_linux_set_offset(struct scsi_target *starget, int offset) 24338c2ecf20Sopenharmony_ci{ 24348c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 24358c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 24368c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 24378c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 24388c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 24398c2ecf20Sopenharmony_ci starget->channel + 'A', 24408c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 24418c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 24428c2ecf20Sopenharmony_ci unsigned int ppr_options = 0; 24438c2ecf20Sopenharmony_ci unsigned int period = 0; 24448c2ecf20Sopenharmony_ci unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; 24458c2ecf20Sopenharmony_ci unsigned long flags; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 24488c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 24498c2ecf20Sopenharmony_ci printk("%s: set offset to %d\n", ahd_name(ahd), offset); 24508c2ecf20Sopenharmony_ci#endif 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 24538c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 24548c2ecf20Sopenharmony_ci if (offset != 0) { 24558c2ecf20Sopenharmony_ci period = tinfo->goal.period; 24568c2ecf20Sopenharmony_ci ppr_options = tinfo->goal.ppr_options; 24578c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 24588c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 24598c2ecf20Sopenharmony_ci } 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 24628c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, offset, ppr_options, 24638c2ecf20Sopenharmony_ci AHD_TRANS_GOAL, FALSE); 24648c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 24658c2ecf20Sopenharmony_ci} 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_cistatic void ahd_linux_set_dt(struct scsi_target *starget, int dt) 24688c2ecf20Sopenharmony_ci{ 24698c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 24708c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 24718c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 24728c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 24738c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 24748c2ecf20Sopenharmony_ci starget->channel + 'A', 24758c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 24768c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 24778c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 24788c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_DT_REQ; 24798c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 24808c2ecf20Sopenharmony_ci unsigned int width = tinfo->goal.width; 24818c2ecf20Sopenharmony_ci unsigned long flags; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 24848c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 24858c2ecf20Sopenharmony_ci printk("%s: %s DT\n", ahd_name(ahd), 24868c2ecf20Sopenharmony_ci dt ? "enabling" : "disabling"); 24878c2ecf20Sopenharmony_ci#endif 24888c2ecf20Sopenharmony_ci if (dt && spi_max_width(starget)) { 24898c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_DT_REQ; 24908c2ecf20Sopenharmony_ci if (!width) 24918c2ecf20Sopenharmony_ci ahd_linux_set_width(starget, 1); 24928c2ecf20Sopenharmony_ci } else { 24938c2ecf20Sopenharmony_ci if (period <= 9) 24948c2ecf20Sopenharmony_ci period = 10; /* If resetting DT, period must be >= 25ns */ 24958c2ecf20Sopenharmony_ci /* IU is invalid without DT set */ 24968c2ecf20Sopenharmony_ci ppr_options &= ~MSG_EXT_PPR_IU_REQ; 24978c2ecf20Sopenharmony_ci } 24988c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 24998c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 25008c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 25018c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 25048c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 25058c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 25068c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 25078c2ecf20Sopenharmony_ci} 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_cistatic void ahd_linux_set_qas(struct scsi_target *starget, int qas) 25108c2ecf20Sopenharmony_ci{ 25118c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 25128c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 25138c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 25148c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 25158c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 25168c2ecf20Sopenharmony_ci starget->channel + 'A', 25178c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 25188c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 25198c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 25208c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_QAS_REQ; 25218c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 25228c2ecf20Sopenharmony_ci unsigned int dt; 25238c2ecf20Sopenharmony_ci unsigned long flags; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 25268c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 25278c2ecf20Sopenharmony_ci printk("%s: %s QAS\n", ahd_name(ahd), 25288c2ecf20Sopenharmony_ci qas ? "enabling" : "disabling"); 25298c2ecf20Sopenharmony_ci#endif 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_ci if (qas) { 25328c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_QAS_REQ; 25338c2ecf20Sopenharmony_ci } 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci dt = ppr_options & MSG_EXT_PPR_DT_REQ; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 25388c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 25398c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 25408c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 25438c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 25448c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 25458c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 25468c2ecf20Sopenharmony_ci} 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_cistatic void ahd_linux_set_iu(struct scsi_target *starget, int iu) 25498c2ecf20Sopenharmony_ci{ 25508c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 25518c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 25528c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 25538c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 25548c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 25558c2ecf20Sopenharmony_ci starget->channel + 'A', 25568c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 25578c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 25588c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 25598c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_IU_REQ; 25608c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 25618c2ecf20Sopenharmony_ci unsigned int dt; 25628c2ecf20Sopenharmony_ci unsigned long flags; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 25658c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 25668c2ecf20Sopenharmony_ci printk("%s: %s IU\n", ahd_name(ahd), 25678c2ecf20Sopenharmony_ci iu ? "enabling" : "disabling"); 25688c2ecf20Sopenharmony_ci#endif 25698c2ecf20Sopenharmony_ci 25708c2ecf20Sopenharmony_ci if (iu && spi_max_width(starget)) { 25718c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_IU_REQ; 25728c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_DT_REQ; /* IU requires DT */ 25738c2ecf20Sopenharmony_ci } 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci dt = ppr_options & MSG_EXT_PPR_DT_REQ; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 25788c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 25798c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 25808c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 25838c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 25848c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 25858c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 25868c2ecf20Sopenharmony_ci} 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_cistatic void ahd_linux_set_rd_strm(struct scsi_target *starget, int rdstrm) 25898c2ecf20Sopenharmony_ci{ 25908c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 25918c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 25928c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 25938c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 25948c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 25958c2ecf20Sopenharmony_ci starget->channel + 'A', 25968c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 25978c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 25988c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 25998c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_RD_STRM; 26008c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 26018c2ecf20Sopenharmony_ci unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; 26028c2ecf20Sopenharmony_ci unsigned long flags; 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 26058c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 26068c2ecf20Sopenharmony_ci printk("%s: %s Read Streaming\n", ahd_name(ahd), 26078c2ecf20Sopenharmony_ci rdstrm ? "enabling" : "disabling"); 26088c2ecf20Sopenharmony_ci#endif 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_ci if (rdstrm && spi_max_width(starget)) 26118c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_RD_STRM; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 26148c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 26158c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 26168c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 26198c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 26208c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 26218c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 26228c2ecf20Sopenharmony_ci} 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_cistatic void ahd_linux_set_wr_flow(struct scsi_target *starget, int wrflow) 26258c2ecf20Sopenharmony_ci{ 26268c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 26278c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 26288c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 26298c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 26308c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 26318c2ecf20Sopenharmony_ci starget->channel + 'A', 26328c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 26338c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 26348c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 26358c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_WR_FLOW; 26368c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 26378c2ecf20Sopenharmony_ci unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; 26388c2ecf20Sopenharmony_ci unsigned long flags; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 26418c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 26428c2ecf20Sopenharmony_ci printk("%s: %s Write Flow Control\n", ahd_name(ahd), 26438c2ecf20Sopenharmony_ci wrflow ? "enabling" : "disabling"); 26448c2ecf20Sopenharmony_ci#endif 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci if (wrflow && spi_max_width(starget)) 26478c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_WR_FLOW; 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 26508c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 26518c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 26528c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 26558c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 26568c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 26578c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 26588c2ecf20Sopenharmony_ci} 26598c2ecf20Sopenharmony_ci 26608c2ecf20Sopenharmony_cistatic void ahd_linux_set_rti(struct scsi_target *starget, int rti) 26618c2ecf20Sopenharmony_ci{ 26628c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 26638c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 26648c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 26658c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 26668c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 26678c2ecf20Sopenharmony_ci starget->channel + 'A', 26688c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 26698c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 26708c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 26718c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_RTI; 26728c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 26738c2ecf20Sopenharmony_ci unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; 26748c2ecf20Sopenharmony_ci unsigned long flags; 26758c2ecf20Sopenharmony_ci 26768c2ecf20Sopenharmony_ci if ((ahd->features & AHD_RTI) == 0) { 26778c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 26788c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 26798c2ecf20Sopenharmony_ci printk("%s: RTI not available\n", ahd_name(ahd)); 26808c2ecf20Sopenharmony_ci#endif 26818c2ecf20Sopenharmony_ci return; 26828c2ecf20Sopenharmony_ci } 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 26858c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 26868c2ecf20Sopenharmony_ci printk("%s: %s RTI\n", ahd_name(ahd), 26878c2ecf20Sopenharmony_ci rti ? "enabling" : "disabling"); 26888c2ecf20Sopenharmony_ci#endif 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci if (rti && spi_max_width(starget)) 26918c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_RTI; 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 26948c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 26958c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 26968c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 26998c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 27008c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 27018c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 27028c2ecf20Sopenharmony_ci} 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_cistatic void ahd_linux_set_pcomp_en(struct scsi_target *starget, int pcomp) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 27078c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 27088c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 27098c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 27108c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 27118c2ecf20Sopenharmony_ci starget->channel + 'A', 27128c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 27138c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 27148c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 27158c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_PCOMP_EN; 27168c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 27178c2ecf20Sopenharmony_ci unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; 27188c2ecf20Sopenharmony_ci unsigned long flags; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci#ifdef AHD_DEBUG 27218c2ecf20Sopenharmony_ci if ((ahd_debug & AHD_SHOW_DV) != 0) 27228c2ecf20Sopenharmony_ci printk("%s: %s Precompensation\n", ahd_name(ahd), 27238c2ecf20Sopenharmony_ci pcomp ? "Enable" : "Disable"); 27248c2ecf20Sopenharmony_ci#endif 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci if (pcomp && spi_max_width(starget)) { 27278c2ecf20Sopenharmony_ci uint8_t precomp; 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci if (ahd->unit < ARRAY_SIZE(aic79xx_iocell_info)) { 27308c2ecf20Sopenharmony_ci const struct ahd_linux_iocell_opts *iocell_opts; 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci iocell_opts = &aic79xx_iocell_info[ahd->unit]; 27338c2ecf20Sopenharmony_ci precomp = iocell_opts->precomp; 27348c2ecf20Sopenharmony_ci } else { 27358c2ecf20Sopenharmony_ci precomp = AIC79XX_DEFAULT_PRECOMP; 27368c2ecf20Sopenharmony_ci } 27378c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_PCOMP_EN; 27388c2ecf20Sopenharmony_ci AHD_SET_PRECOMP(ahd, precomp); 27398c2ecf20Sopenharmony_ci } else { 27408c2ecf20Sopenharmony_ci AHD_SET_PRECOMP(ahd, 0); 27418c2ecf20Sopenharmony_ci } 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 27448c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 27458c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 27468c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 27498c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 27508c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 27518c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 27528c2ecf20Sopenharmony_ci} 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_cistatic void ahd_linux_set_hold_mcs(struct scsi_target *starget, int hold) 27558c2ecf20Sopenharmony_ci{ 27568c2ecf20Sopenharmony_ci struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); 27578c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); 27588c2ecf20Sopenharmony_ci struct ahd_tmode_tstate *tstate; 27598c2ecf20Sopenharmony_ci struct ahd_initiator_tinfo *tinfo 27608c2ecf20Sopenharmony_ci = ahd_fetch_transinfo(ahd, 27618c2ecf20Sopenharmony_ci starget->channel + 'A', 27628c2ecf20Sopenharmony_ci shost->this_id, starget->id, &tstate); 27638c2ecf20Sopenharmony_ci struct ahd_devinfo devinfo; 27648c2ecf20Sopenharmony_ci unsigned int ppr_options = tinfo->goal.ppr_options 27658c2ecf20Sopenharmony_ci & ~MSG_EXT_PPR_HOLD_MCS; 27668c2ecf20Sopenharmony_ci unsigned int period = tinfo->goal.period; 27678c2ecf20Sopenharmony_ci unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; 27688c2ecf20Sopenharmony_ci unsigned long flags; 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_ci if (hold && spi_max_width(starget)) 27718c2ecf20Sopenharmony_ci ppr_options |= MSG_EXT_PPR_HOLD_MCS; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, 27748c2ecf20Sopenharmony_ci starget->channel + 'A', ROLE_INITIATOR); 27758c2ecf20Sopenharmony_ci ahd_find_syncrate(ahd, &period, &ppr_options, 27768c2ecf20Sopenharmony_ci dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 27798c2ecf20Sopenharmony_ci ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, 27808c2ecf20Sopenharmony_ci ppr_options, AHD_TRANS_GOAL, FALSE); 27818c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 27828c2ecf20Sopenharmony_ci} 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_cistatic void ahd_linux_get_signalling(struct Scsi_Host *shost) 27858c2ecf20Sopenharmony_ci{ 27868c2ecf20Sopenharmony_ci struct ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata; 27878c2ecf20Sopenharmony_ci unsigned long flags; 27888c2ecf20Sopenharmony_ci u8 mode; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci ahd_lock(ahd, &flags); 27918c2ecf20Sopenharmony_ci ahd_pause(ahd); 27928c2ecf20Sopenharmony_ci mode = ahd_inb(ahd, SBLKCTL); 27938c2ecf20Sopenharmony_ci ahd_unpause(ahd); 27948c2ecf20Sopenharmony_ci ahd_unlock(ahd, &flags); 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci if (mode & ENAB40) 27978c2ecf20Sopenharmony_ci spi_signalling(shost) = SPI_SIGNAL_LVD; 27988c2ecf20Sopenharmony_ci else if (mode & ENAB20) 27998c2ecf20Sopenharmony_ci spi_signalling(shost) = SPI_SIGNAL_SE; 28008c2ecf20Sopenharmony_ci else 28018c2ecf20Sopenharmony_ci spi_signalling(shost) = SPI_SIGNAL_UNKNOWN; 28028c2ecf20Sopenharmony_ci} 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_cistatic struct spi_function_template ahd_linux_transport_functions = { 28058c2ecf20Sopenharmony_ci .set_offset = ahd_linux_set_offset, 28068c2ecf20Sopenharmony_ci .show_offset = 1, 28078c2ecf20Sopenharmony_ci .set_period = ahd_linux_set_period, 28088c2ecf20Sopenharmony_ci .show_period = 1, 28098c2ecf20Sopenharmony_ci .set_width = ahd_linux_set_width, 28108c2ecf20Sopenharmony_ci .show_width = 1, 28118c2ecf20Sopenharmony_ci .set_dt = ahd_linux_set_dt, 28128c2ecf20Sopenharmony_ci .show_dt = 1, 28138c2ecf20Sopenharmony_ci .set_iu = ahd_linux_set_iu, 28148c2ecf20Sopenharmony_ci .show_iu = 1, 28158c2ecf20Sopenharmony_ci .set_qas = ahd_linux_set_qas, 28168c2ecf20Sopenharmony_ci .show_qas = 1, 28178c2ecf20Sopenharmony_ci .set_rd_strm = ahd_linux_set_rd_strm, 28188c2ecf20Sopenharmony_ci .show_rd_strm = 1, 28198c2ecf20Sopenharmony_ci .set_wr_flow = ahd_linux_set_wr_flow, 28208c2ecf20Sopenharmony_ci .show_wr_flow = 1, 28218c2ecf20Sopenharmony_ci .set_rti = ahd_linux_set_rti, 28228c2ecf20Sopenharmony_ci .show_rti = 1, 28238c2ecf20Sopenharmony_ci .set_pcomp_en = ahd_linux_set_pcomp_en, 28248c2ecf20Sopenharmony_ci .show_pcomp_en = 1, 28258c2ecf20Sopenharmony_ci .set_hold_mcs = ahd_linux_set_hold_mcs, 28268c2ecf20Sopenharmony_ci .show_hold_mcs = 1, 28278c2ecf20Sopenharmony_ci .get_signalling = ahd_linux_get_signalling, 28288c2ecf20Sopenharmony_ci}; 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_cistatic int __init 28318c2ecf20Sopenharmony_ciahd_linux_init(void) 28328c2ecf20Sopenharmony_ci{ 28338c2ecf20Sopenharmony_ci int error = 0; 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci /* 28368c2ecf20Sopenharmony_ci * If we've been passed any parameters, process them now. 28378c2ecf20Sopenharmony_ci */ 28388c2ecf20Sopenharmony_ci if (aic79xx) 28398c2ecf20Sopenharmony_ci aic79xx_setup(aic79xx); 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_ci ahd_linux_transport_template = 28428c2ecf20Sopenharmony_ci spi_attach_transport(&ahd_linux_transport_functions); 28438c2ecf20Sopenharmony_ci if (!ahd_linux_transport_template) 28448c2ecf20Sopenharmony_ci return -ENODEV; 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci scsi_transport_reserve_device(ahd_linux_transport_template, 28478c2ecf20Sopenharmony_ci sizeof(struct ahd_linux_device)); 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci error = ahd_linux_pci_init(); 28508c2ecf20Sopenharmony_ci if (error) 28518c2ecf20Sopenharmony_ci spi_release_transport(ahd_linux_transport_template); 28528c2ecf20Sopenharmony_ci return error; 28538c2ecf20Sopenharmony_ci} 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_cistatic void __exit 28568c2ecf20Sopenharmony_ciahd_linux_exit(void) 28578c2ecf20Sopenharmony_ci{ 28588c2ecf20Sopenharmony_ci ahd_linux_pci_exit(); 28598c2ecf20Sopenharmony_ci spi_release_transport(ahd_linux_transport_template); 28608c2ecf20Sopenharmony_ci} 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_cimodule_init(ahd_linux_init); 28638c2ecf20Sopenharmony_cimodule_exit(ahd_linux_exit); 2864