Skip to contentSkip to conversation

wd719x: Introduce Western Digital WD7193/7197/7296 PCI SCSI card driver

PATCH Superseded Ondrej Zary 2 participants 7 messages Nov 24, 2014

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover wd719x: Introduce Western Digital WD7193/7197/7296 PCI SCSI card driver

Hello,
this is a new driver for Western Digital WD7193, WD7197 and WD7296 PCI SCSI controllers based on WD33C296A chip. These nice cards never worked good with Linux.

It's based on old and ugly wd719x driver written back in 2.0 days, then hacked to 2.2 and finally to 2.4 kernels. Most of the code is rewritten: from ~4100 lines to ~1200.

I've tested the driver on a WD7193 card with some hard drives and CD-ROMs:

QUANTUM  LP240S GM240S01X 4.6
IBM      0663L12          e g
IBM      DORS-32160       WA0A
SONY     CD-ROM CDU-55S   1.0t
SONY     CD-ROM CDU-415   1.1g

The card supports TCQ and linked commands (for cmd_per_lun > 1?) but I don't know how it should be implemented in a driver.

Device/bus/host resets seem to work fine when tested by sg_reset. But don't know how to test command abort.

The card requires firmware that can be cut out of the Windows NT driver that can be downloaded from WD at:
http://support.wdc.com/product/download.asp?groupid=801&sid=27&lang=en
There is no license anywhere in the file or on the page - so I guess that the firmware cannot be added to linux-firmware.

This script that downloads and extracts the firmware is in Documentation/

Signature · 1 line
Ondrej Zary
#1 lore raw

PATCH 1/3 eeprom-93cx6: Add (read-only) support for 8-bit mode

Add read-only support for EEPROMs configured in 8-bit mode (ORG pin connected to GND).
This will be used by wd719x driver.

---
 drivers/misc/eeprom/eeprom_93cx6.c |   62 +++++++++++++++++++++++++++++++++++-
 include/linux/eeprom_93cx6.h       |    4 +++
 2 files changed, 65 insertions(+), 1 deletion(-)
drivers/misc/eeprom/eeprom_93cx6.c +61 -1
index 0ff4b02..0cf2c9d 100644
@@ -170,7 +170,7 @@ static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom,
 }
 
 /**
- * eeprom_93cx6_read - Read multiple words from eeprom
+ * eeprom_93cx6_read - Read a word from eeprom
  * @eeprom: Pointer to eeprom structure
  * @word: Word index from where we should start reading
  * @data: target pointer where the information will have to be stored
@@ -235,6 +235,66 @@ void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word,
 EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread);
 
 /**
+ * eeprom_93cx6_readb - Read a byte from eeprom
+ * @eeprom: Pointer to eeprom structure
+ * @word: Byte index from where we should start reading
+ * @data: target pointer where the information will have to be stored
+ *
+ * This function will read a byte of the eeprom data
+ * into the given data pointer.
+ */
+void eeprom_93cx6_readb(struct eeprom_93cx6 *eeprom, const u8 byte,
+	u8 *data)
+{
+	u16 command;
+	u16 tmp;
+
+	/*
+	 * Initialize the eeprom register
+	 */
+	eeprom_93cx6_startup(eeprom);
+
+	/*
+	 * Select the read opcode and the byte to be read.
+	 */
+	command = (PCI_EEPROM_READ_OPCODE << (eeprom->width + 1)) | byte;
+	eeprom_93cx6_write_bits(eeprom, command,
+		PCI_EEPROM_WIDTH_OPCODE + eeprom->width + 1);
+
+	/*
+	 * Read the requested 8 bits.
+	 */
+	eeprom_93cx6_read_bits(eeprom, &tmp, 8);
+	*data = tmp & 0xff;
+
+	/*
+	 * Cleanup eeprom register.
+	 */
+	eeprom_93cx6_cleanup(eeprom);
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_readb);
+
+/**
+ * eeprom_93cx6_multireadb - Read multiple bytes from eeprom
+ * @eeprom: Pointer to eeprom structure
+ * @byte: Index from where we should start reading
+ * @data: target pointer where the information will have to be stored
+ * @words: Number of bytes that should be read.
+ *
+ * This function will read all requested bytes from the eeprom,
+ * this is done by calling eeprom_93cx6_readb() multiple times.
+ */
+void eeprom_93cx6_multireadb(struct eeprom_93cx6 *eeprom, const u8 byte,
+	u8 *data, const u16 bytes)
+{
+	unsigned int i;
+
+	for (i = 0; i < bytes; i++)
+		eeprom_93cx6_readb(eeprom, byte + i, &data[i]);
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_multireadb);
+
+/**
  * eeprom_93cx6_wren - set the write enable state
  * @eeprom: Pointer to eeprom structure
  * @enable: true to enable writes, otherwise disable writes
include/linux/eeprom_93cx6.h +4 -0
index e50f98b..eb0b198 100644
@@ -75,6 +75,10 @@ extern void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom,
 	const u8 word, u16 *data);
 extern void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom,
 	const u8 word, __le16 *data, const u16 words);
+extern void eeprom_93cx6_readb(struct eeprom_93cx6 *eeprom,
+	const u8 byte, u8 *data);
+extern void eeprom_93cx6_multireadb(struct eeprom_93cx6 *eeprom,
+	const u8 byte, u8 *data, const u16 bytes);
 
 extern void eeprom_93cx6_wren(struct eeprom_93cx6 *eeprom, bool enable);
 
Signature · 1 line
Ondrej Zary
Reviewed-by on v1 1/3
#2 lore raw

Re: [PATCH 1/3] eeprom-93cx6: Add (read-only) support for 8-bit mode

Quoted 5 lines · Ondrej Zary

Add read-only support for EEPROMs configured in 8-bit mode (ORG pin connected to GND).
This will be used by wd719x driver.

Cheers,

Hannes

Signature · 4 lines
Dr. Hannes Reinecke		      zSeries & Storage
[email protected]			      +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 21284 (AG Nürnberg)
#3 lore raw

PATCH 2/3 wd719x: Introduce Western Digital WD7193/7197/7296 PCI SCSI card driver

In reply to Ondrej Zary

Introduce wd719x, a driver for Western Digital WD7193, WD7197 and WD7296 PCI SCSI controllers based on WD33C296A chip.
Tested with WD7193 card.

---
 drivers/scsi/Kconfig  |    8 +
 drivers/scsi/Makefile |    1 +
 drivers/scsi/wd719x.c | 1008 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/wd719x.h |  249 ++++++++++++
 4 files changed, 1266 insertions(+)
 create mode 100644 drivers/scsi/wd719x.c
 create mode 100644 drivers/scsi/wd719x.h
drivers/scsi/Kconfig +8 -0
index 3a820f6..fd81d14 100644
@@ -1451,6 +1451,14 @@ config SCSI_NSP32
 	  To compile this driver as a module, choose M here: the
 	  module will be called nsp32.
 
+config SCSI_WD719X
+	tristate "Western Digital WD7193/7197/7296 support"
+	depends on PCI && SCSI
+	select EEPROM_93CX6
+	---help---
+	  This is a driver for Western Digital WD7193, WD7197 and WD7296 PCI
+	  SCSI controllers (based on WD33C296A chip).
+
 config SCSI_DEBUG
 	tristate "SCSI debugging host simulator"
 	depends on SCSI
drivers/scsi/Makefile +1 -0
index 59f1ce6..d8bfca5 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_SCSI_VIRTIO)	+= virtio_scsi.o
 obj-$(CONFIG_VMWARE_PVSCSI)	+= vmw_pvscsi.o
 obj-$(CONFIG_XEN_SCSI_FRONTEND)	+= xen-scsifront.o
 obj-$(CONFIG_HYPERV_STORAGE)	+= hv_storvsc.o
+obj-$(CONFIG_SCSI_WD719X)	+= wd719x.o
 
 obj-$(CONFIG_ARM)		+= arm/
 
drivers/scsi/wd719x.c new file +1008 -0

1,008 lines not on this page · raw message

drivers/scsi/wd719x.h new file +249 -0

249 lines not on this page · raw message

Signature · 1 line
Ondrej Zary
#4 lore raw
Quoted 354 lines · Ondrej Zary

Introduce wd719x, a driver for Western Digital WD7193, WD7197 and WD7296 PCI SCSI controllers based on WD33C296A chip.
Tested with WD7193 card.

---
 drivers/scsi/Kconfig  |    8 +
 drivers/scsi/Makefile |    1 +
 drivers/scsi/wd719x.c | 1008 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/wd719x.h |  249 ++++++++++++
 4 files changed, 1266 insertions(+)
 create mode 100644 drivers/scsi/wd719x.c
 create mode 100644 drivers/scsi/wd719x.h
drivers/scsi/Kconfig +8 -0
index 3a820f6..fd81d14 100644
@@ -1451,6 +1451,14 @@ config SCSI_NSP32
 	  To compile this driver as a module, choose M here: the
 	  module will be called nsp32.
 
+config SCSI_WD719X
+	tristate "Western Digital WD7193/7197/7296 support"
+	depends on PCI && SCSI
+	select EEPROM_93CX6
+	---help---
+	  This is a driver for Western Digital WD7193, WD7197 and WD7296 PCI
+	  SCSI controllers (based on WD33C296A chip).
+
 config SCSI_DEBUG
 	tristate "SCSI debugging host simulator"
 	depends on SCSI
drivers/scsi/Makefile +1 -0
index 59f1ce6..d8bfca5 100644
@@ -143,6 +143,7 @@ obj-$(CONFIG_SCSI_VIRTIO)	+= virtio_scsi.o
 obj-$(CONFIG_VMWARE_PVSCSI)	+= vmw_pvscsi.o
 obj-$(CONFIG_XEN_SCSI_FRONTEND)	+= xen-scsifront.o
 obj-$(CONFIG_HYPERV_STORAGE)	+= hv_storvsc.o
+obj-$(CONFIG_SCSI_WD719X)	+= wd719x.o
 
 obj-$(CONFIG_ARM)		+= arm/
 
drivers/scsi/wd719x.c new file +302 -0
new file mode 100644
index 0000000..ce6dafd
@@ -0,0 +1,1008 @@
+/*
+ * Driver for Western Digital WD7193, WD7197 and WD7296 SCSI cards
+ * Copyright 2013 Ondrej Zary
+ *
+ * Original driver by
+ * Aaron Dewell <[email protected]>
+ * Gaerti <[email protected]>
+ *
+ * HW documentation available in book:
+ *
+ * SPIDER Command Protocol
+ * by Chandru M. Sippy
+ * SCSI Storage Products (MCP)
+ * Western Digital Corporation
+ * 09-15-95
+ *
+ * http://web.archive.org/web/20070717175254/http://sun1.rrzn.uni-hannover.de/gaertner.juergen/wd719x/Linux/Docu/Spider/
+ */
+
+/*
+ * Driver workflow:
+ * 1. SCSI command is transformed to SCB (Spider Control Block) by the
+ *    queuecommand function.
+ * 2. The address of the SCB is stored in a list to be able to access it, if
+ *    something goes wrong.
+ * 3. The address of the SCB is written to the Controller, which loads the SCB
+ *    via BM-DMA and processes it.
+ * 4. After it has finished, it generates an interrupt, and sets registers.
+ *
+ * flaws:
+ *  - abort/reset functions
+ *
+ * ToDo:
+ *  - tagged queueing
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/eeprom_93cx6.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+#include "wd719x.h"
+
+/* low-level register access */
+static inline u8 wd719x_readb(struct wd719x *wd, u8 reg)
+{
+	return ioread8(wd->base + reg);
+}
+
+static inline u32 wd719x_readl(struct wd719x *wd, u8 reg)
+{
+	return ioread32(wd->base + reg);
+}
+
+static inline void wd719x_writeb(struct wd719x *wd, u8 reg, u8 val)
+{
+	iowrite8(val, wd->base + reg);
+}
+
+static inline void wd719x_writew(struct wd719x *wd, u8 reg, u16 val)
+{
+	iowrite16(val, wd->base + reg);
+}
+
+static inline void wd719x_writel(struct wd719x *wd, u8 reg, u32 val)
+{
+	iowrite32(val, wd->base + reg);
+}
+
+/* wait until the command register is ready */
+static inline int wd719x_wait_ready(struct wd719x *wd)
+{
+	int i = 0;
+
+	do {
+		if (wd719x_readb(wd, WD719X_AMR_COMMAND) == WD719X_CMD_READY)
+			return 0;
+		udelay(1);
+	} while (i++ < WD719X_WAIT_FOR_CMD_READY);
+
+	dev_err(&wd->pdev->dev, "command register is not ready: 0x%02x\n",
+		wd719x_readb(wd, WD719X_AMR_COMMAND));
+
+	return -ETIMEDOUT;
+}
+
+/* poll interrupt status register until command finishes */
+static inline int wd719x_wait_done(struct wd719x *wd, int timeout)
+{
+	u8 status;
+
+	while (timeout > 0) {
+		status = wd719x_readb(wd, WD719X_AMR_INT_STATUS);
+		if (status)
+			break;
+		timeout--;
+		udelay(1);
+	}
+
+	if (timeout <= 0) {
+		dev_err(&wd->pdev->dev, "direct command timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	if (status != WD719X_INT_NOERRORS) {
+		dev_err(&wd->pdev->dev, "direct command failed, status 0x%02x, SUE 0x%02x\n",
+			status, wd719x_readb(wd, WD719X_AMR_SCB_ERROR));
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int wd719x_direct_cmd(struct wd719x *wd, u8 opcode, u8 dev, u8 lun,
+			     u8 tag, dma_addr_t data, int timeout)
+{
+	int ret = 0;
+
+	/* clear interrupt status register (allow command register to clear) */
+	wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+
+	/* Wait for the Command register to become free */
+	if (wd719x_wait_ready(wd))
+		return -ETIMEDOUT;
+
+	/* make sure we get NO interrupts */
+	dev |= WD719X_DISABLE_INT;
+	wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, dev);
+	wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_2, lun);
+	wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_3, tag);
+	if (data)
+		wd719x_writel(wd, WD719X_AMR_SCB_IN, data);
+
+	/* clear interrupt status register again */
+	wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+
+	/* Now, write the command */
+	wd719x_writeb(wd, WD719X_AMR_COMMAND, opcode);
+
+	if (timeout)	/* wait for the command to complete */
+		ret = wd719x_wait_done(wd, timeout);
+
+	/* clear interrupt status register (clean up) */
+	if (opcode != WD719X_CMD_READ_FIRMVER)
+		wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
+
+	return ret;
+}
+
+static void wd719x_destroy(struct wd719x *wd)
+{
+	struct wd719x_scb *scb;
+
+	/* stop the RISC */
+	if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0,
+			      WD719X_WAIT_FOR_RISC))
+		dev_warn(&wd->pdev->dev, "RISC sleep command failed\n");
+	/* disable RISC */
+	wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, 0);
+
+	/* free all SCBs */
+	list_for_each_entry(scb, &wd->active_scbs, list)
+		pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
+				    scb->phys);
+	list_for_each_entry(scb, &wd->free_scbs, list)
+		pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
+				    scb->phys);
+	/* free internal buffers */
+	pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys);
+	wd->fw_virt = NULL;
+	pci_free_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, wd->hash_virt,
+			    wd->hash_phys);
+	wd->hash_virt = NULL;
+	pci_free_consistent(wd->pdev, sizeof(struct wd719x_host_param),
+			    wd->params, wd->params_phys);
+	wd->params = NULL;
+	free_irq(wd->pdev->irq, wd);
+}
+
+/* finish a SCSI command, mark SCB (if any) as free, unmap buffers */
+static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result)
+{
+	struct wd719x *wd = shost_priv(cmd->device->host);
+	struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble;
+
+	if (scb) {
+		list_move(&scb->list, &wd->free_scbs);
+		dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
+				 SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+		scsi_dma_unmap(cmd);
+	}
+	cmd->result = result << 16;
+	cmd->scsi_done(cmd);
+}
+
+static int wd719x_send_scb(struct wd719x_scb *scb)
+{
+	struct wd719x *wd = shost_priv(scb->cmd->device->host);
+
+	/* wait for the Command register to become free */
+	if (wd719x_wait_ready(wd))
+		return DID_TIME_OUT;
+
+	/* first write pointer to the AMR */
+	wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys);
+	/* send SCB opcode */
+	wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB);
+
+	return DID_OK;
+}
+
+/* Build a SCB and send it to the card using send_scb */
+static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
+{
+	int i, result, count_sg;
+	unsigned long flags;
+	struct wd719x_scb *scb;
+	struct wd719x *wd = shost_priv(sh);
+	dma_addr_t phys;
+
+	cmd->host_scribble = NULL;
+
+	/* get a free SCB - either from existing ones or allocate a new one */
+	spin_lock_irqsave(wd->sh->host_lock, flags);
+	scb = list_first_entry_or_null(&wd->free_scbs, struct wd719x_scb, list);
+	if (scb) {
+		list_del(&scb->list);
+		phys = scb->phys;
+	} else {
+		spin_unlock_irqrestore(wd->sh->host_lock, flags);
+		scb = pci_alloc_consistent(wd->pdev, sizeof(struct wd719x_scb),
+					   &phys);
+		spin_lock_irqsave(wd->sh->host_lock, flags);
+		if (!scb) {
+			dev_err(&wd->pdev->dev, "unable to allocate SCB\n");
+			wd719x_finish_cmd(cmd, DID_ERROR);
+			spin_unlock_irqrestore(wd->sh->host_lock, flags);
+			return 0;
+		}
+	}
+	memset(scb, 0, sizeof(struct wd719x_scb));
+	list_add(&scb->list, &wd->active_scbs);
+
+	scb->phys = phys;
+	scb->cmd = cmd;
+	cmd->host_scribble = (char *) scb;
+
+	scb->CDB_tag = 0;	/* Tagged queueing not supported yet */
+	scb->devid = cmd->device->id;
+	scb->lun = cmd->device->lun;
+
+	/* copy the command */
+	memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len);
+
+	/* map sense buffer */
+	scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE;
+	cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer,
+			SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+	dma_cache_sync(&wd->pdev->dev, cmd->sense_buffer,
+			SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
+	scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle);
+
+	/* request autosense */
+	scb->SCB_options |= WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE;
+
+	/* check direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE)
+		scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION
+				 |  WD719X_SCB_FLAGS_PCI_TO_SCSI;
+	else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+		scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION;
+
+	/* Scather/gather */
+	count_sg = scsi_dma_map(cmd);
+	if (count_sg < 0) {
+		wd719x_finish_cmd(cmd, DID_ERROR);
+		spin_unlock_irqrestore(wd->sh->host_lock, flags);
+		return 0;
+	}
+	BUG_ON(count_sg > WD719X_SG);
+
+	if (count_sg) {
+		struct scatterlist *sg;
+
+		scb->data_length = cpu_to_le32(count_sg *
+					       sizeof(struct wd719x_sglist));
+		scb->data_p = cpu_to_le32(scb->phys +
+					  offsetof(struct wd719x_scb, sg_list));
+
+		scsi_for_each_sg(cmd, sg, count_sg, i) {
+			scb->sg_list[i].ptr = cpu_to_le32(sg_dma_address(sg));
+			scb->sg_list[i].length = cpu_to_le32(sg_dma_len(sg));
+		}
+		scb->SCB_options |= WD719X_SCB_FLAGS_DO_SCATTER_GATHER;
+	} else { /* zero length */
+		scb->data_length = 0;
+		scb->data_p = 0;
+	}
+

drivers/scsi/wd719x.c line 312

Quoted 10 lines
drivers/scsi/wd719x.c new file +10 -0
+	result = wd719x_send_scb(scb);
+	if (result != DID_OK) {
+		dev_warn(&wd->pdev->dev, "can't queue SCB\n");
+		wd719x_finish_cmd(cmd, result);
+	}
+
+	spin_unlock_irqrestore(wd->sh->host_lock, flags);
+
+	return 0;
+}

Why did you use 'wait_ready' here?
Any sane HBA driver should set the queue depth parameters correctly to avoid this from happening.
Wouldn't it be far better to just return HOST_BUSY here if the command register isn't free and submit the command directly otherwise?

Cheers,

Hannes

Signature · 4 lines
Dr. Hannes Reinecke		      zSeries & Storage
[email protected]			      +49 911 74053 688
SUSE LINUX GmbH, Maxfeldstr. 5, 90409 Nürnberg
GF: J. Hawn, J. Guild, F. Imendörffer, HRB 21284 (AG Nürnberg)
#5 lore raw
Quoted 7 lines · On Monday 24 November 2014 14:15:47 Hannes Reinecke wrote:
Quoted 6 lines · Ondrej Zary

Introduce wd719x, a driver for Western Digital WD7193, WD7197 and WD7296 PCI SCSI controllers based on WD33C296A chip.
Tested with WD7193 card.

[...]

Quoted 9 lines
Quoted 9 lines
drivers/scsi/wd719x.c new file +9 -0
+	result = wd719x_send_scb(scb);
+	if (result != DID_OK) {
+		dev_warn(&wd->pdev->dev, "can't queue SCB\n");
+		wd719x_finish_cmd(cmd, result);
+	}
+
+	spin_unlock_irqrestore(wd->sh->host_lock, flags);
+
+	return 0;
Quoted 8 lines
Quoted 1 line

+}

Why did you use 'wait_ready' here?
Any sane HBA driver should set the queue depth parameters correctly to avoid this from happening.
Wouldn't it be far better to just return HOST_BUSY here if the command register isn't free and submit the command directly otherwise?

It probably comes from the original driver. Will send a new version.

Signature · 1 line
Ondrej Zary
#6 lore raw

PATCH 3/3 wd719x: Add firmware documentation

In reply to Ondrej Zary

Add documentation and script to obtain required firmware.

---
 Documentation/scsi/wd719x.txt |   21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 Documentation/scsi/wd719x.txt
Documentation/scsi/wd719x.txt new file +21 -0
new file mode 100644
index 0000000..0816b02
@@ -0,0 +1,21 @@
+Driver for Western Digital WD7193, WD7197 and WD7296 SCSI cards
+---------------------------------------------------------------
+
+The card requires firmware that can be cut out of the Windows NT driver that
+can be downloaded from WD at:
+http://support.wdc.com/product/download.asp?groupid=801&sid=27&lang=en
+
+There is no license anywhere in the file or on the page - so the firmware
+probably cannot be added to linux-firmware.
+
+This script downloads and extracts the firmware, creating wd719x-risc.bin and
+d719x-wcs.bin files. Put them in /lib/firmware/.
+
+#!/bin/sh
+wget http://support.wdc.com/download/archive/pciscsi.exe
+lha xi pciscsi.exe pci-scsi.exe
+lha xi pci-scsi.exe nt/wd7296a.sys
+rm pci-scsi.exe
+dd if=wd7296a.sys of=wd719x-risc.bin bs=1 skip=5760 count=14336
+dd if=wd7296a.sys of=wd719x-wcs.bin bs=1 skip=20096 count=514
+rm wd7296a.sys
Signature · 1 line
Ondrej Zary