aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/qlogicfas408.c
diff options
context:
space:
mode:
authorLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
committerLibravatar Linus Torvalds <torvalds@linux-foundation.org>2023-02-21 18:24:12 -0800
commit5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch)
treecc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/scsi/qlogicfas408.c
downloadlinux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz
linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski: "Core: - Add dedicated kmem_cache for typical/small skb->head, avoid having to access struct page at kfree time, and improve memory use. - Introduce sysctl to set default RPS configuration for new netdevs. - Define Netlink protocol specification format which can be used to describe messages used by each family and auto-generate parsers. Add tools for generating kernel data structures and uAPI headers. - Expose all net/core sysctls inside netns. - Remove 4s sleep in netpoll if carrier is instantly detected on boot. - Add configurable limit of MDB entries per port, and port-vlan. - Continue populating drop reasons throughout the stack. - Retire a handful of legacy Qdiscs and classifiers. Protocols: - Support IPv4 big TCP (TSO frames larger than 64kB). - Add IP_LOCAL_PORT_RANGE socket option, to control local port range on socket by socket basis. - Track and report in procfs number of MPTCP sockets used. - Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path manager. - IPv6: don't check net.ipv6.route.max_size and rely on garbage collection to free memory (similarly to IPv4). - Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986). - ICMP: add per-rate limit counters. - Add support for user scanning requests in ieee802154. - Remove static WEP support. - Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate reporting. - WiFi 7 EHT channel puncturing support (client & AP). BPF: - Add a rbtree data structure following the "next-gen data structure" precedent set by recently added linked list, that is, by using kfunc + kptr instead of adding a new BPF map type. - Expose XDP hints via kfuncs with initial support for RX hash and timestamp metadata. - Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to better support decap on GRE tunnel devices not operating in collect metadata. - Improve x86 JIT's codegen for PROBE_MEM runtime error checks. - Remove the need for trace_printk_lock for bpf_trace_printk and bpf_trace_vprintk helpers. - Extend libbpf's bpf_tracing.h support for tracing arguments of kprobes/uprobes and syscall as a special case. - Significantly reduce the search time for module symbols by livepatch and BPF. - Enable cpumasks to be used as kptrs, which is useful for tracing programs tracking which tasks end up running on which CPUs in different time intervals. - Add support for BPF trampoline on s390x and riscv64. - Add capability to export the XDP features supported by the NIC. - Add __bpf_kfunc tag for marking kernel functions as kfuncs. - Add cgroup.memory=nobpf kernel parameter option to disable BPF memory accounting for container environments. Netfilter: - Remove the CLUSTERIP target. It has been marked as obsolete for years, and we still have WARN splats wrt races of the out-of-band /proc interface installed by this target. - Add 'destroy' commands to nf_tables. They are identical to the existing 'delete' commands, but do not return an error if the referenced object (set, chain, rule...) did not exist. Driver API: - Improve cpumask_local_spread() locality to help NICs set the right IRQ affinity on AMD platforms. - Separate C22 and C45 MDIO bus transactions more clearly. - Introduce new DCB table to control DSCP rewrite on egress. - Support configuration of Physical Layer Collision Avoidance (PLCA) Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of shared medium Ethernet. - Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing preemption of low priority frames by high priority frames. - Add support for controlling MACSec offload using netlink SET. - Rework devlink instance refcounts to allow registration and de-registration under the instance lock. Split the code into multiple files, drop some of the unnecessarily granular locks and factor out common parts of netlink operation handling. - Add TX frame aggregation parameters (for USB drivers). - Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning messages with notifications for debug. - Allow offloading of UDP NEW connections via act_ct. - Add support for per action HW stats in TC. - Support hardware miss to TC action (continue processing in SW from a specific point in the action chain). - Warn if old Wireless Extension user space interface is used with modern cfg80211/mac80211 drivers. Do not support Wireless Extensions for Wi-Fi 7 devices at all. Everyone should switch to using nl80211 interface instead. - Improve the CAN bit timing configuration. Use extack to return error messages directly to user space, update the SJW handling, including the definition of a new default value that will benefit CAN-FD controllers, by increasing their oscillator tolerance. New hardware / drivers: - Ethernet: - nVidia BlueField-3 support (control traffic driver) - Ethernet support for imx93 SoCs - Motorcomm yt8531 gigabit Ethernet PHY - onsemi NCN26000 10BASE-T1S PHY (with support for PLCA) - Microchip LAN8841 PHY (incl. cable diagnostics and PTP) - Amlogic gxl MDIO mux - WiFi: - RealTek RTL8188EU (rtl8xxxu) - Qualcomm Wi-Fi 7 devices (ath12k) - CAN: - Renesas R-Car V4H Drivers: - Bluetooth: - Set Per Platform Antenna Gain (PPAG) for Intel controllers. - Ethernet NICs: - Intel (1G, igc): - support TSN / Qbv / packet scheduling features of i226 model - Intel (100G, ice): - use GNSS subsystem instead of TTY - multi-buffer XDP support - extend support for GPIO pins to E823 devices - nVidia/Mellanox: - update the shared buffer configuration on PFC commands - implement PTP adjphase function for HW offset control - TC support for Geneve and GRE with VF tunnel offload - more efficient crypto key management method - multi-port eswitch support - Netronome/Corigine: - add DCB IEEE support - support IPsec offloading for NFP3800 - Freescale/NXP (enetc): - support XDP_REDIRECT for XDP non-linear buffers - improve reconfig, avoid link flap and waiting for idle - support MAC Merge layer - Other NICs: - sfc/ef100: add basic devlink support for ef100 - ionic: rx_push mode operation (writing descriptors via MMIO) - bnxt: use the auxiliary bus abstraction for RDMA - r8169: disable ASPM and reset bus in case of tx timeout - cpsw: support QSGMII mode for J721e CPSW9G - cpts: support pulse-per-second output - ngbe: add an mdio bus driver - usbnet: optimize usbnet_bh() by avoiding unnecessary queuing - r8152: handle devices with FW with NCM support - amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation - virtio-net: support multi buffer XDP - virtio/vsock: replace virtio_vsock_pkt with sk_buff - tsnep: XDP support - Ethernet high-speed switches: - nVidia/Mellanox (mlxsw): - add support for latency TLV (in FW control messages) - Microchip (sparx5): - separate explicit and implicit traffic forwarding rules, make the implicit rules always active - add support for egress DSCP rewrite - IS0 VCAP support (Ingress Classification) - IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS etc.) - ES2 VCAP support (Egress Access Control) - support for Per-Stream Filtering and Policing (802.1Q, 8.6.5.1) - Ethernet embedded switches: - Marvell (mv88e6xxx): - add MAB (port auth) offload support - enable PTP receive for mv88e6390 - NXP (ocelot): - support MAC Merge layer - support for the the vsc7512 internal copper phys - Microchip: - lan9303: convert to PHYLINK - lan966x: support TC flower filter statistics - lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x - lan937x: support Credit Based Shaper configuration - ksz9477: support Energy Efficient Ethernet - other: - qca8k: convert to regmap read/write API, use bulk operations - rswitch: Improve TX timestamp accuracy - Intel WiFi (iwlwifi): - EHT (Wi-Fi 7) rate reporting - STEP equalizer support: transfer some STEP (connection to radio on platforms with integrated wifi) related parameters from the BIOS to the firmware. - Qualcomm 802.11ax WiFi (ath11k): - IPQ5018 support - Fine Timing Measurement (FTM) responder role support - channel 177 support - MediaTek WiFi (mt76): - per-PHY LED support - mt7996: EHT (Wi-Fi 7) support - Wireless Ethernet Dispatch (WED) reset support - switch to using page pool allocator - RealTek WiFi (rtw89): - support new version of Bluetooth co-existance - Mobile: - rmnet: support TX aggregation" * tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits) page_pool: add a comment explaining the fragment counter usage net: ethtool: fix __ethtool_dev_mm_supported() implementation ethtool: pse-pd: Fix double word in comments xsk: add linux/vmalloc.h to xsk.c sefltests: netdevsim: wait for devlink instance after netns removal selftest: fib_tests: Always cleanup before exit net/mlx5e: Align IPsec ASO result memory to be as required by hardware net/mlx5e: TC, Set CT miss to the specific ct action instance net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG net/mlx5: Refactor tc miss handling to a single function net/mlx5: Kconfig: Make tc offload depend on tc skb extension net/sched: flower: Support hardware miss to tc action net/sched: flower: Move filter handle initialization earlier net/sched: cls_api: Support hardware miss to tc action net/sched: Rename user cookie and act cookie sfc: fix builds without CONFIG_RTC_LIB sfc: clean up some inconsistent indentings net/mlx4_en: Introduce flexible array to silence overflow warning net: lan966x: Fix possible deadlock inside PTP net/ulp: Remove redundant ->clone() test in inet_clone_ulp(). ...
Diffstat (limited to 'drivers/scsi/qlogicfas408.c')
-rw-r--r--drivers/scsi/qlogicfas408.c644
1 files changed, 644 insertions, 0 deletions
diff --git a/drivers/scsi/qlogicfas408.c b/drivers/scsi/qlogicfas408.c
new file mode 100644
index 000000000..3e065d5fc
--- /dev/null
+++ b/drivers/scsi/qlogicfas408.c
@@ -0,0 +1,644 @@
+/*----------------------------------------------------------------*/
+/*
+ Qlogic linux driver - work in progress. No Warranty express or implied.
+ Use at your own risk. Support Tort Reform so you won't have to read all
+ these silly disclaimers.
+
+ Copyright 1994, Tom Zerucha.
+ tz@execpc.com
+
+ Additional Code, and much appreciated help by
+ Michael A. Griffith
+ grif@cs.ucr.edu
+
+ Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+ help respectively, and for suffering through my foolishness during the
+ debugging process.
+
+ Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
+ (you can reference it, but it is incomplete and inaccurate in places)
+
+ Version 0.46 1/30/97 - kernel 1.2.0+
+
+ Functions as standalone, loadable, and PCMCIA driver, the latter from
+ Dave Hinds' PCMCIA package.
+
+ Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5
+ SCSI driver cleanup and audit. This driver still needs work on the
+ following
+ - Non terminating hardware waits
+ - Some layering violations with its pcmcia stub
+
+ Redistributable under terms of the GNU General Public License
+
+ For the avoidance of doubt the "preferred form" of this code is one which
+ is in an open non patent encumbered format. Where cryptographic key signing
+ forms part of the process of creating an executable the information
+ including keys needed to generate an equivalently functional executable
+ are deemed to be part of the source code.
+
+*/
+
+#include <linux/module.h>
+#include <linux/blkdev.h> /* to get disk capacity */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include "qlogicfas408.h"
+
+/*----------------------------------------------------------------*/
+static int qlcfg5 = (XTALFREQ << 5); /* 15625/512 */
+static int qlcfg6 = SYNCXFRPD;
+static int qlcfg7 = SYNCOFFST;
+static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
+static int qlcfg9 = ((XTALFREQ + 4) / 5);
+static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
+
+/*----------------------------------------------------------------*/
+
+/*----------------------------------------------------------------*/
+/* local functions */
+/*----------------------------------------------------------------*/
+
+/* error recovery - reset everything */
+
+static void ql_zap(struct qlogicfas408_priv *priv)
+{
+ int x;
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+
+ x = inb(qbase + 0xd);
+ REG0;
+ outb(3, qbase + 3); /* reset SCSI */
+ outb(2, qbase + 3); /* reset chip */
+ if (x & 0x80)
+ REG1;
+}
+
+/*
+ * Do a pseudo-dma tranfer
+ */
+
+static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request,
+ int reqlen)
+{
+ int j;
+ int qbase = priv->qbase;
+ j = 0;
+ if (phase & 1) { /* in */
+#if QL_TURBO_PDMA
+ rtrc(4)
+ /* empty fifo in large chunks */
+ if (reqlen >= 128 && (inb(qbase + 8) & 2)) { /* full */
+ insl(qbase + 4, request, 32);
+ reqlen -= 128;
+ request += 128;
+ }
+ while (reqlen >= 84 && !(j & 0xc0)) /* 2/3 */
+ if ((j = inb(qbase + 8)) & 4)
+ {
+ insl(qbase + 4, request, 21);
+ reqlen -= 84;
+ request += 84;
+ }
+ if (reqlen >= 44 && (inb(qbase + 8) & 8)) { /* 1/3 */
+ insl(qbase + 4, request, 11);
+ reqlen -= 44;
+ request += 44;
+ }
+#endif
+ /* until both empty and int (or until reclen is 0) */
+ rtrc(7)
+ j = 0;
+ while (reqlen && !((j & 0x10) && (j & 0xc0)))
+ {
+ /* while bytes to receive and not empty */
+ j &= 0xc0;
+ while (reqlen && !((j = inb(qbase + 8)) & 0x10))
+ {
+ *request++ = inb(qbase + 4);
+ reqlen--;
+ }
+ if (j & 0x10)
+ j = inb(qbase + 8);
+
+ }
+ } else { /* out */
+#if QL_TURBO_PDMA
+ rtrc(4)
+ if (reqlen >= 128 && inb(qbase + 8) & 0x10) { /* empty */
+ outsl(qbase + 4, request, 32);
+ reqlen -= 128;
+ request += 128;
+ }
+ while (reqlen >= 84 && !(j & 0xc0)) /* 1/3 */
+ if (!((j = inb(qbase + 8)) & 8)) {
+ outsl(qbase + 4, request, 21);
+ reqlen -= 84;
+ request += 84;
+ }
+ if (reqlen >= 40 && !(inb(qbase + 8) & 4)) { /* 2/3 */
+ outsl(qbase + 4, request, 10);
+ reqlen -= 40;
+ request += 40;
+ }
+#endif
+ /* until full and int (or until reclen is 0) */
+ rtrc(7)
+ j = 0;
+ while (reqlen && !((j & 2) && (j & 0xc0))) {
+ /* while bytes to send and not full */
+ while (reqlen && !((j = inb(qbase + 8)) & 2))
+ {
+ outb(*request++, qbase + 4);
+ reqlen--;
+ }
+ if (j & 2)
+ j = inb(qbase + 8);
+ }
+ }
+ /* maybe return reqlen */
+ return inb(qbase + 8) & 0xc0;
+}
+
+/*
+ * Wait for interrupt flag (polled - not real hardware interrupt)
+ */
+
+static int ql_wai(struct qlogicfas408_priv *priv)
+{
+ int k;
+ int qbase = priv->qbase;
+ unsigned long i;
+
+ k = 0;
+ i = jiffies + WATCHDOG;
+ while (time_before(jiffies, i) && !priv->qabort &&
+ !((k = inb(qbase + 4)) & 0xe0)) {
+ barrier();
+ cpu_relax();
+ }
+ if (time_after_eq(jiffies, i))
+ return (DID_TIME_OUT);
+ if (priv->qabort)
+ return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
+ if (k & 0x60)
+ ql_zap(priv);
+ if (k & 0x20)
+ return (DID_PARITY);
+ if (k & 0x40)
+ return (DID_ERROR);
+ return 0;
+}
+
+/*
+ * Initiate scsi command - queueing handler
+ * caller must hold host lock
+ */
+
+static void ql_icmd(struct scsi_cmnd *cmd)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+ unsigned int i;
+
+ priv->qabort = 0;
+
+ REG0;
+ /* clearing of interrupts and the fifo is needed */
+
+ inb(qbase + 5); /* clear interrupts */
+ if (inb(qbase + 5)) /* if still interrupting */
+ outb(2, qbase + 3); /* reset chip */
+ else if (inb(qbase + 7) & 0x1f)
+ outb(1, qbase + 3); /* clear fifo */
+ while (inb(qbase + 5)); /* clear ints */
+ REG1;
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ outb(0, qbase + 0xb); /* disable ints */
+ inb(qbase + 8); /* clear int bits */
+ REG0;
+ outb(0x40, qbase + 0xb); /* enable features */
+
+ /* configurables */
+ outb(qlcfgc, qbase + 0xc);
+ /* config: no reset interrupt, (initiator) bus id */
+ outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
+ outb(qlcfg7, qbase + 7);
+ outb(qlcfg6, qbase + 6);
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9 & 7, qbase + 9); /* prescaler */
+/* outb(0x99, qbase + 5); */
+ outb(scmd_id(cmd), qbase + 4);
+
+ for (i = 0; i < cmd->cmd_len; i++)
+ outb(cmd->cmnd[i], qbase + 2);
+
+ priv->qlcmd = cmd;
+ outb(0x41, qbase + 3); /* select and send command */
+}
+
+/*
+ * Process scsi command - usually after interrupt
+ */
+
+static void ql_pcmd(struct scsi_cmnd *cmd)
+{
+ unsigned int i, j;
+ unsigned long k;
+ unsigned int status; /* scsi returned status */
+ unsigned int message; /* scsi returned message */
+ unsigned int phase; /* recorded scsi phase */
+ unsigned int reqlen; /* total length of transfer */
+ char *buf;
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+
+ rtrc(1)
+ j = inb(qbase + 6);
+ i = inb(qbase + 5);
+ if (i == 0x20) {
+ set_host_byte(cmd, DID_NO_CONNECT);
+ return;
+ }
+ i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */
+ if (i != 0x18) {
+ printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
+ ql_zap(priv);
+ set_host_byte(cmd, DID_BAD_INTR);
+ return;
+ }
+ j &= 7; /* j = inb( qbase + 7 ) >> 5; */
+
+ /* correct status is supposed to be step 4 */
+ /* it sometimes returns step 3 but with 0 bytes left to send */
+ /* We can try stuffing the FIFO with the max each time, but we will get a
+ sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
+
+ if (j != 3 && j != 4) {
+ printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
+ j, i, inb(qbase + 7) & 0x1f);
+ ql_zap(priv);
+ set_host_byte(cmd, DID_ERROR);
+ return;
+ }
+
+ if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */
+ outb(1, qbase + 3); /* clear fifo */
+ /* note that request_bufflen is the total xfer size when sg is used */
+ reqlen = scsi_bufflen(cmd);
+ /* note that it won't work if transfers > 16M are requested */
+ if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */
+ struct scatterlist *sg;
+ rtrc(2)
+ outb(reqlen, qbase); /* low-mid xfer cnt */
+ outb(reqlen >> 8, qbase + 1); /* low-mid xfer cnt */
+ outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */
+ outb(0x90, qbase + 3); /* command do xfer */
+ /* PIO pseudo DMA to buffer or sglist */
+ REG1;
+
+ scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
+ if (priv->qabort) {
+ REG0;
+ set_host_byte(cmd,
+ priv->qabort == 1 ?
+ DID_ABORT : DID_RESET);
+ }
+ buf = sg_virt(sg);
+ if (ql_pdma(priv, phase, buf, sg->length))
+ break;
+ }
+ REG0;
+ rtrc(2);
+ /*
+ * Wait for irq (split into second state of irq handler
+ * if this can take time)
+ */
+ if ((k = ql_wai(priv))) {
+ set_host_byte(cmd, k);
+ return;
+ }
+ k = inb(qbase + 5); /* should be 0x10, bus service */
+ }
+
+ /*
+ * Enter Status (and Message In) Phase
+ */
+
+ k = jiffies + WATCHDOG;
+
+ while (time_before(jiffies, k) && !priv->qabort &&
+ !(inb(qbase + 4) & 6))
+ cpu_relax(); /* wait for status phase */
+
+ if (time_after_eq(jiffies, k)) {
+ ql_zap(priv);
+ set_host_byte(cmd, DID_TIME_OUT);
+ return;
+ }
+
+ /* FIXME: timeout ?? */
+ while (inb(qbase + 5))
+ cpu_relax(); /* clear pending ints */
+
+ if (priv->qabort) {
+ set_host_byte(cmd,
+ priv->qabort == 1 ? DID_ABORT : DID_RESET);
+ return;
+ }
+
+ outb(0x11, qbase + 3); /* get status and message */
+ if ((k = ql_wai(priv))) {
+ set_host_byte(cmd, k);
+ return;
+ }
+ i = inb(qbase + 5); /* get chip irq stat */
+ j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */
+ status = inb(qbase + 2);
+ message = inb(qbase + 2);
+
+ /*
+ * Should get function complete int if Status and message, else
+ * bus serv if only status
+ */
+ if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
+ printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
+ set_host_byte(cmd, DID_ERROR);
+ }
+ outb(0x12, qbase + 3); /* done, disconnect */
+ rtrc(1);
+ if ((k = ql_wai(priv))) {
+ set_host_byte(cmd, k);
+ return;
+ }
+
+ /*
+ * Should get bus service interrupt and disconnect interrupt
+ */
+
+ i = inb(qbase + 5); /* should be bus service */
+ while (!priv->qabort && ((i & 0x20) != 0x20)) {
+ barrier();
+ cpu_relax();
+ i |= inb(qbase + 5);
+ }
+ rtrc(0);
+
+ if (priv->qabort) {
+ set_host_byte(cmd,
+ priv->qabort == 1 ? DID_ABORT : DID_RESET);
+ return;
+ }
+
+ set_host_byte(cmd, DID_OK);
+ if (message != COMMAND_COMPLETE)
+ scsi_msg_to_host_byte(cmd, message);
+ set_status_byte(cmd, status);
+ return;
+}
+
+/*
+ * Interrupt handler
+ */
+
+static void ql_ihandl(void *dev_id)
+{
+ struct scsi_cmnd *icmd;
+ struct Scsi_Host *host = dev_id;
+ struct qlogicfas408_priv *priv = get_priv_by_host(host);
+ int qbase = priv->qbase;
+ REG0;
+
+ if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
+ return;
+
+ if (priv->qlcmd == NULL) { /* no command to process? */
+ int i;
+ i = 16;
+ while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
+ return;
+ }
+ icmd = priv->qlcmd;
+ ql_pcmd(icmd);
+ priv->qlcmd = NULL;
+ /*
+ * If result is CHECK CONDITION done calls qcommand to request
+ * sense
+ */
+ scsi_done(icmd);
+}
+
+irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
+{
+ unsigned long flags;
+ struct Scsi_Host *host = dev_id;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ ql_ihandl(dev_id);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Queued command
+ */
+
+static int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd)
+{
+ void (*done)(struct scsi_cmnd *) = scsi_done;
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+
+ set_host_byte(cmd, DID_OK);
+ set_status_byte(cmd, SAM_STAT_GOOD);
+ if (scmd_id(cmd) == priv->qinitid) {
+ set_host_byte(cmd, DID_BAD_TARGET);
+ done(cmd);
+ return 0;
+ }
+
+ /* wait for the last command's interrupt to finish */
+ while (priv->qlcmd != NULL) {
+ barrier();
+ cpu_relax();
+ }
+ ql_icmd(cmd);
+ return 0;
+}
+
+DEF_SCSI_QCMD(qlogicfas408_queuecommand)
+
+/*
+ * Return bios parameters
+ */
+
+int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev,
+ sector_t capacity, int ip[])
+{
+/* This should mimic the DOS Qlogic driver's behavior exactly */
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
+#if 0
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+#endif
+ }
+ return 0;
+}
+
+/*
+ * Abort a command in progress
+ */
+
+int qlogicfas408_abort(struct scsi_cmnd *cmd)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ priv->qabort = 1;
+ ql_zap(priv);
+ return SUCCESS;
+}
+
+/*
+ * Reset SCSI bus
+ * FIXME: This function is invoked with cmd = NULL directly by
+ * the PCMCIA qlogic_stub code. This wants fixing
+ */
+
+int qlogicfas408_host_reset(struct scsi_cmnd *cmd)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
+ unsigned long flags;
+
+ priv->qabort = 2;
+
+ spin_lock_irqsave(cmd->device->host->host_lock, flags);
+ ql_zap(priv);
+ spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
+
+ return SUCCESS;
+}
+
+/*
+ * Return info string
+ */
+
+const char *qlogicfas408_info(struct Scsi_Host *host)
+{
+ struct qlogicfas408_priv *priv = get_priv_by_host(host);
+ return priv->qinfo;
+}
+
+/*
+ * Get type of chip
+ */
+
+int qlogicfas408_get_chip_type(int qbase, int int_type)
+{
+ REG1;
+ return inb(qbase + 0xe) & 0xf8;
+}
+
+/*
+ * Perform initialization tasks
+ */
+
+void qlogicfas408_setup(int qbase, int id, int int_type)
+{
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ REG0;
+ outb(0x40 | qlcfg8 | id, qbase + 8); /* (ini) bus id, disable scsi rst */
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9, qbase + 9); /* prescaler */
+
+#if QL_RESET_AT_START
+ outb(3, qbase + 3);
+
+ REG1;
+ /* FIXME: timeout */
+ while (inb(qbase + 0xf) & 4)
+ cpu_relax();
+
+ REG0;
+#endif
+}
+
+/*
+ * Checks if this is a QLogic FAS 408
+ */
+
+int qlogicfas408_detect(int qbase, int int_type)
+{
+ REG1;
+ return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
+ ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
+}
+
+/*
+ * Disable interrupts
+ */
+
+void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
+{
+ int qbase = priv->qbase;
+ int int_type = priv->int_type;
+
+ REG1;
+ outb(0, qbase + 0xb); /* disable ints */
+}
+
+/*
+ * Init and exit functions
+ */
+
+static int __init qlogicfas408_init(void)
+{
+ return 0;
+}
+
+static void __exit qlogicfas408_exit(void)
+{
+
+}
+
+MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
+MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
+MODULE_LICENSE("GPL");
+module_init(qlogicfas408_init);
+module_exit(qlogicfas408_exit);
+
+EXPORT_SYMBOL(qlogicfas408_info);
+EXPORT_SYMBOL(qlogicfas408_queuecommand);
+EXPORT_SYMBOL(qlogicfas408_abort);
+EXPORT_SYMBOL(qlogicfas408_host_reset);
+EXPORT_SYMBOL(qlogicfas408_biosparam);
+EXPORT_SYMBOL(qlogicfas408_ihandl);
+EXPORT_SYMBOL(qlogicfas408_get_chip_type);
+EXPORT_SYMBOL(qlogicfas408_setup);
+EXPORT_SYMBOL(qlogicfas408_detect);
+EXPORT_SYMBOL(qlogicfas408_disable_ints);
+