diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/scsi/qlogicfas408.c | |
download | linux-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.c | 644 |
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); + |