Commit 48a31030066315a74e6c11153b4382edbf133bb3

Authored by Ondrej Zary
Committed by Christoph Hellwig
1 parent 7ff28aee40

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

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

Signed-off-by: Ondrej Zary <linux@rainbow-software.org>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Christoph Hellwig <hch@lst.de>

Showing 4 changed files with 1256 additions and 0 deletions Side-by-side Diff

drivers/scsi/Kconfig
... ... @@ -1453,6 +1453,14 @@
1453 1453 To compile this driver as a module, choose M here: the
1454 1454 module will be called nsp32.
1455 1455  
  1456 +config SCSI_WD719X
  1457 + tristate "Western Digital WD7193/7197/7296 support"
  1458 + depends on PCI && SCSI
  1459 + select EEPROM_93CX6
  1460 + ---help---
  1461 + This is a driver for Western Digital WD7193, WD7197 and WD7296 PCI
  1462 + SCSI controllers (based on WD33C296A chip).
  1463 +
1456 1464 config SCSI_DEBUG
1457 1465 tristate "SCSI debugging host simulator"
1458 1466 depends on SCSI
drivers/scsi/Makefile
... ... @@ -143,6 +143,7 @@
143 143 obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
144 144 obj-$(CONFIG_XEN_SCSI_FRONTEND) += xen-scsifront.o
145 145 obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
  146 +obj-$(CONFIG_SCSI_WD719X) += wd719x.o
146 147  
147 148 obj-$(CONFIG_ARM) += arm/
148 149  
drivers/scsi/wd719x.c
Changes suppressed. Click to show
  1 +/*
  2 + * Driver for Western Digital WD7193, WD7197 and WD7296 SCSI cards
  3 + * Copyright 2013 Ondrej Zary
  4 + *
  5 + * Original driver by
  6 + * Aaron Dewell <dewell@woods.net>
  7 + * Gaerti <Juergen.Gaertner@mbox.si.uni-hannover.de>
  8 + *
  9 + * HW documentation available in book:
  10 + *
  11 + * SPIDER Command Protocol
  12 + * by Chandru M. Sippy
  13 + * SCSI Storage Products (MCP)
  14 + * Western Digital Corporation
  15 + * 09-15-95
  16 + *
  17 + * http://web.archive.org/web/20070717175254/http://sun1.rrzn.uni-hannover.de/gaertner.juergen/wd719x/Linux/Docu/Spider/
  18 + */
  19 +
  20 +/*
  21 + * Driver workflow:
  22 + * 1. SCSI command is transformed to SCB (Spider Control Block) by the
  23 + * queuecommand function.
  24 + * 2. The address of the SCB is stored in a list to be able to access it, if
  25 + * something goes wrong.
  26 + * 3. The address of the SCB is written to the Controller, which loads the SCB
  27 + * via BM-DMA and processes it.
  28 + * 4. After it has finished, it generates an interrupt, and sets registers.
  29 + *
  30 + * flaws:
  31 + * - abort/reset functions
  32 + *
  33 + * ToDo:
  34 + * - tagged queueing
  35 + */
  36 +
  37 +#include <linux/interrupt.h>
  38 +#include <linux/module.h>
  39 +#include <linux/delay.h>
  40 +#include <linux/pci.h>
  41 +#include <linux/firmware.h>
  42 +#include <linux/eeprom_93cx6.h>
  43 +#include <scsi/scsi_cmnd.h>
  44 +#include <scsi/scsi_device.h>
  45 +#include <scsi/scsi_host.h>
  46 +#include "wd719x.h"
  47 +
  48 +/* low-level register access */
  49 +static inline u8 wd719x_readb(struct wd719x *wd, u8 reg)
  50 +{
  51 + return ioread8(wd->base + reg);
  52 +}
  53 +
  54 +static inline u32 wd719x_readl(struct wd719x *wd, u8 reg)
  55 +{
  56 + return ioread32(wd->base + reg);
  57 +}
  58 +
  59 +static inline void wd719x_writeb(struct wd719x *wd, u8 reg, u8 val)
  60 +{
  61 + iowrite8(val, wd->base + reg);
  62 +}
  63 +
  64 +static inline void wd719x_writew(struct wd719x *wd, u8 reg, u16 val)
  65 +{
  66 + iowrite16(val, wd->base + reg);
  67 +}
  68 +
  69 +static inline void wd719x_writel(struct wd719x *wd, u8 reg, u32 val)
  70 +{
  71 + iowrite32(val, wd->base + reg);
  72 +}
  73 +
  74 +/* wait until the command register is ready */
  75 +static inline int wd719x_wait_ready(struct wd719x *wd)
  76 +{
  77 + int i = 0;
  78 +
  79 + do {
  80 + if (wd719x_readb(wd, WD719X_AMR_COMMAND) == WD719X_CMD_READY)
  81 + return 0;
  82 + udelay(1);
  83 + } while (i++ < WD719X_WAIT_FOR_CMD_READY);
  84 +
  85 + dev_err(&wd->pdev->dev, "command register is not ready: 0x%02x\n",
  86 + wd719x_readb(wd, WD719X_AMR_COMMAND));
  87 +
  88 + return -ETIMEDOUT;
  89 +}
  90 +
  91 +/* poll interrupt status register until command finishes */
  92 +static inline int wd719x_wait_done(struct wd719x *wd, int timeout)
  93 +{
  94 + u8 status;
  95 +
  96 + while (timeout > 0) {
  97 + status = wd719x_readb(wd, WD719X_AMR_INT_STATUS);
  98 + if (status)
  99 + break;
  100 + timeout--;
  101 + udelay(1);
  102 + }
  103 +
  104 + if (timeout <= 0) {
  105 + dev_err(&wd->pdev->dev, "direct command timed out\n");
  106 + return -ETIMEDOUT;
  107 + }
  108 +
  109 + if (status != WD719X_INT_NOERRORS) {
  110 + dev_err(&wd->pdev->dev, "direct command failed, status 0x%02x, SUE 0x%02x\n",
  111 + status, wd719x_readb(wd, WD719X_AMR_SCB_ERROR));
  112 + return -EIO;
  113 + }
  114 +
  115 + return 0;
  116 +}
  117 +
  118 +static int wd719x_direct_cmd(struct wd719x *wd, u8 opcode, u8 dev, u8 lun,
  119 + u8 tag, dma_addr_t data, int timeout)
  120 +{
  121 + int ret = 0;
  122 +
  123 + /* clear interrupt status register (allow command register to clear) */
  124 + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
  125 +
  126 + /* Wait for the Command register to become free */
  127 + if (wd719x_wait_ready(wd))
  128 + return -ETIMEDOUT;
  129 +
  130 + /* make sure we get NO interrupts */
  131 + dev |= WD719X_DISABLE_INT;
  132 + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, dev);
  133 + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_2, lun);
  134 + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_3, tag);
  135 + if (data)
  136 + wd719x_writel(wd, WD719X_AMR_SCB_IN, data);
  137 +
  138 + /* clear interrupt status register again */
  139 + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
  140 +
  141 + /* Now, write the command */
  142 + wd719x_writeb(wd, WD719X_AMR_COMMAND, opcode);
  143 +
  144 + if (timeout) /* wait for the command to complete */
  145 + ret = wd719x_wait_done(wd, timeout);
  146 +
  147 + /* clear interrupt status register (clean up) */
  148 + if (opcode != WD719X_CMD_READ_FIRMVER)
  149 + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
  150 +
  151 + return ret;
  152 +}
  153 +
  154 +static void wd719x_destroy(struct wd719x *wd)
  155 +{
  156 + struct wd719x_scb *scb;
  157 +
  158 + /* stop the RISC */
  159 + if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, 0, 0, 0, 0,
  160 + WD719X_WAIT_FOR_RISC))
  161 + dev_warn(&wd->pdev->dev, "RISC sleep command failed\n");
  162 + /* disable RISC */
  163 + wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, 0);
  164 +
  165 + /* free all SCBs */
  166 + list_for_each_entry(scb, &wd->active_scbs, list)
  167 + pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
  168 + scb->phys);
  169 + list_for_each_entry(scb, &wd->free_scbs, list)
  170 + pci_free_consistent(wd->pdev, sizeof(struct wd719x_scb), scb,
  171 + scb->phys);
  172 + /* free internal buffers */
  173 + pci_free_consistent(wd->pdev, wd->fw_size, wd->fw_virt, wd->fw_phys);
  174 + wd->fw_virt = NULL;
  175 + pci_free_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, wd->hash_virt,
  176 + wd->hash_phys);
  177 + wd->hash_virt = NULL;
  178 + pci_free_consistent(wd->pdev, sizeof(struct wd719x_host_param),
  179 + wd->params, wd->params_phys);
  180 + wd->params = NULL;
  181 + free_irq(wd->pdev->irq, wd);
  182 +}
  183 +
  184 +/* finish a SCSI command, mark SCB (if any) as free, unmap buffers */
  185 +static void wd719x_finish_cmd(struct scsi_cmnd *cmd, int result)
  186 +{
  187 + struct wd719x *wd = shost_priv(cmd->device->host);
  188 + struct wd719x_scb *scb = (struct wd719x_scb *) cmd->host_scribble;
  189 +
  190 + if (scb) {
  191 + list_move(&scb->list, &wd->free_scbs);
  192 + dma_unmap_single(&wd->pdev->dev, cmd->SCp.dma_handle,
  193 + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
  194 + scsi_dma_unmap(cmd);
  195 + }
  196 + cmd->result = result << 16;
  197 + cmd->scsi_done(cmd);
  198 +}
  199 +
  200 +/* Build a SCB and send it to the card */
  201 +static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd)
  202 +{
  203 + int i, count_sg;
  204 + unsigned long flags;
  205 + struct wd719x_scb *scb;
  206 + struct wd719x *wd = shost_priv(sh);
  207 + dma_addr_t phys;
  208 +
  209 + cmd->host_scribble = NULL;
  210 +
  211 + /* get a free SCB - either from existing ones or allocate a new one */
  212 + spin_lock_irqsave(wd->sh->host_lock, flags);
  213 + scb = list_first_entry_or_null(&wd->free_scbs, struct wd719x_scb, list);
  214 + if (scb) {
  215 + list_del(&scb->list);
  216 + phys = scb->phys;
  217 + } else {
  218 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  219 + scb = pci_alloc_consistent(wd->pdev, sizeof(struct wd719x_scb),
  220 + &phys);
  221 + spin_lock_irqsave(wd->sh->host_lock, flags);
  222 + if (!scb) {
  223 + dev_err(&wd->pdev->dev, "unable to allocate SCB\n");
  224 + wd719x_finish_cmd(cmd, DID_ERROR);
  225 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  226 + return 0;
  227 + }
  228 + }
  229 + memset(scb, 0, sizeof(struct wd719x_scb));
  230 + list_add(&scb->list, &wd->active_scbs);
  231 +
  232 + scb->phys = phys;
  233 + scb->cmd = cmd;
  234 + cmd->host_scribble = (char *) scb;
  235 +
  236 + scb->CDB_tag = 0; /* Tagged queueing not supported yet */
  237 + scb->devid = cmd->device->id;
  238 + scb->lun = cmd->device->lun;
  239 +
  240 + /* copy the command */
  241 + memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len);
  242 +
  243 + /* map sense buffer */
  244 + scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE;
  245 + cmd->SCp.dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer,
  246 + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
  247 + dma_cache_sync(&wd->pdev->dev, cmd->sense_buffer,
  248 + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE);
  249 + scb->sense_buf = cpu_to_le32(cmd->SCp.dma_handle);
  250 +
  251 + /* request autosense */
  252 + scb->SCB_options |= WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE;
  253 +
  254 + /* check direction */
  255 + if (cmd->sc_data_direction == DMA_TO_DEVICE)
  256 + scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION
  257 + | WD719X_SCB_FLAGS_PCI_TO_SCSI;
  258 + else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
  259 + scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION;
  260 +
  261 + /* Scather/gather */
  262 + count_sg = scsi_dma_map(cmd);
  263 + if (count_sg < 0) {
  264 + wd719x_finish_cmd(cmd, DID_ERROR);
  265 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  266 + return 0;
  267 + }
  268 + BUG_ON(count_sg > WD719X_SG);
  269 +
  270 + if (count_sg) {
  271 + struct scatterlist *sg;
  272 +
  273 + scb->data_length = cpu_to_le32(count_sg *
  274 + sizeof(struct wd719x_sglist));
  275 + scb->data_p = cpu_to_le32(scb->phys +
  276 + offsetof(struct wd719x_scb, sg_list));
  277 +
  278 + scsi_for_each_sg(cmd, sg, count_sg, i) {
  279 + scb->sg_list[i].ptr = cpu_to_le32(sg_dma_address(sg));
  280 + scb->sg_list[i].length = cpu_to_le32(sg_dma_len(sg));
  281 + }
  282 + scb->SCB_options |= WD719X_SCB_FLAGS_DO_SCATTER_GATHER;
  283 + } else { /* zero length */
  284 + scb->data_length = 0;
  285 + scb->data_p = 0;
  286 + }
  287 +
  288 + /* check if the Command register is free */
  289 + if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) {
  290 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  291 + return SCSI_MLQUEUE_HOST_BUSY;
  292 + }
  293 +
  294 + /* write pointer to the AMR */
  295 + wd719x_writel(wd, WD719X_AMR_SCB_IN, scb->phys);
  296 + /* send SCB opcode */
  297 + wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB);
  298 +
  299 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  300 +
  301 + return 0;
  302 +}
  303 +
  304 +static int wd719x_chip_init(struct wd719x *wd)
  305 +{
  306 + int i, ret;
  307 + u32 risc_init[3];
  308 + const struct firmware *fw_wcs, *fw_risc;
  309 + const char fwname_wcs[] = "wd719x-wcs.bin";
  310 + const char fwname_risc[] = "wd719x-risc.bin";
  311 +
  312 + memset(wd->hash_virt, 0, WD719X_HASH_TABLE_SIZE);
  313 +
  314 + /* WCS (sequencer) firmware */
  315 + ret = request_firmware(&fw_wcs, fwname_wcs, &wd->pdev->dev);
  316 + if (ret) {
  317 + dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n",
  318 + fwname_wcs, ret);
  319 + return ret;
  320 + }
  321 + /* RISC firmware */
  322 + ret = request_firmware(&fw_risc, fwname_risc, &wd->pdev->dev);
  323 + if (ret) {
  324 + dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n",
  325 + fwname_risc, ret);
  326 + release_firmware(fw_wcs);
  327 + return ret;
  328 + }
  329 + wd->fw_size = ALIGN(fw_wcs->size, 4) + fw_risc->size;
  330 +
  331 + if (!wd->fw_virt)
  332 + wd->fw_virt = pci_alloc_consistent(wd->pdev, wd->fw_size,
  333 + &wd->fw_phys);
  334 + if (!wd->fw_virt) {
  335 + ret = -ENOMEM;
  336 + goto wd719x_init_end;
  337 + }
  338 +
  339 + /* make a fresh copy of WCS and RISC code */
  340 + memcpy(wd->fw_virt, fw_wcs->data, fw_wcs->size);
  341 + memcpy(wd->fw_virt + ALIGN(fw_wcs->size, 4), fw_risc->data,
  342 + fw_risc->size);
  343 +
  344 + /* Reset the Spider Chip and adapter itself */
  345 + wd719x_writeb(wd, WD719X_PCI_PORT_RESET, WD719X_PCI_RESET);
  346 + udelay(WD719X_WAIT_FOR_RISC);
  347 + /* Clear PIO mode bits set by BIOS */
  348 + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, 0);
  349 + /* ensure RISC is not running */
  350 + wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, 0);
  351 + /* ensure command port is ready */
  352 + wd719x_writeb(wd, WD719X_AMR_COMMAND, 0);
  353 + if (wd719x_wait_ready(wd)) {
  354 + ret = -ETIMEDOUT;
  355 + goto wd719x_init_end;
  356 + }
  357 +
  358 + /* Transfer the first 2K words of RISC code to kick start the uP */
  359 + risc_init[0] = wd->fw_phys; /* WCS FW */
  360 + risc_init[1] = wd->fw_phys + ALIGN(fw_wcs->size, 4); /* RISC FW */
  361 + risc_init[2] = wd->hash_phys; /* hash table */
  362 +
  363 + /* clear DMA status */
  364 + wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3STATUS, 0);
  365 +
  366 + /* address to read firmware from */
  367 + wd719x_writel(wd, WD719X_PCI_EXTERNAL_ADDR, risc_init[1]);
  368 + /* base address to write firmware to (on card) */
  369 + wd719x_writew(wd, WD719X_PCI_INTERNAL_ADDR, WD719X_PRAM_BASE_ADDR);
  370 + /* size: first 2K words */
  371 + wd719x_writew(wd, WD719X_PCI_DMA_TRANSFER_SIZE, 2048 * 2);
  372 + /* start DMA */
  373 + wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3CMD, WD719X_START_CHANNEL2_3DMA);
  374 +
  375 + /* wait for DMA to complete */
  376 + i = WD719X_WAIT_FOR_RISC;
  377 + while (i-- > 0) {
  378 + u8 status = wd719x_readb(wd, WD719X_PCI_CHANNEL2_3STATUS);
  379 + if (status == WD719X_START_CHANNEL2_3DONE)
  380 + break;
  381 + if (status == WD719X_START_CHANNEL2_3ABORT) {
  382 + dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA aborted\n");
  383 + ret = -EIO;
  384 + goto wd719x_init_end;
  385 + }
  386 + udelay(1);
  387 + }
  388 + if (i < 1) {
  389 + dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA timeout\n");
  390 + ret = -ETIMEDOUT;
  391 + goto wd719x_init_end;
  392 + }
  393 +
  394 + /* firmware is loaded, now initialize and wake up the RISC */
  395 + /* write RISC initialization long words to Spider */
  396 + wd719x_writel(wd, WD719X_AMR_SCB_IN, risc_init[0]);
  397 + wd719x_writel(wd, WD719X_AMR_SCB_IN + 4, risc_init[1]);
  398 + wd719x_writel(wd, WD719X_AMR_SCB_IN + 8, risc_init[2]);
  399 +
  400 + /* disable interrupts during initialization of RISC */
  401 + wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, WD719X_DISABLE_INT);
  402 +
  403 + /* issue INITIALIZE RISC comand */
  404 + wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_INIT_RISC);
  405 + /* enable advanced mode (wake up RISC) */
  406 + wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, WD719X_ENABLE_ADVANCE_MODE);
  407 + udelay(WD719X_WAIT_FOR_RISC);
  408 +
  409 + ret = wd719x_wait_done(wd, WD719X_WAIT_FOR_RISC);
  410 + /* clear interrupt status register */
  411 + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
  412 + if (ret) {
  413 + dev_warn(&wd->pdev->dev, "Unable to initialize RISC\n");
  414 + goto wd719x_init_end;
  415 + }
  416 + /* RISC is up and running */
  417 +
  418 + /* Read FW version from RISC */
  419 + ret = wd719x_direct_cmd(wd, WD719X_CMD_READ_FIRMVER, 0, 0, 0, 0,
  420 + WD719X_WAIT_FOR_RISC);
  421 + if (ret) {
  422 + dev_warn(&wd->pdev->dev, "Unable to read firmware version\n");
  423 + goto wd719x_init_end;
  424 + }
  425 + dev_info(&wd->pdev->dev, "RISC initialized with firmware version %.2x.%.2x\n",
  426 + wd719x_readb(wd, WD719X_AMR_SCB_OUT + 1),
  427 + wd719x_readb(wd, WD719X_AMR_SCB_OUT));
  428 +
  429 + /* RESET SCSI bus */
  430 + ret = wd719x_direct_cmd(wd, WD719X_CMD_BUSRESET, 0, 0, 0, 0,
  431 + WD719X_WAIT_FOR_SCSI_RESET);
  432 + if (ret) {
  433 + dev_warn(&wd->pdev->dev, "SCSI bus reset failed\n");
  434 + goto wd719x_init_end;
  435 + }
  436 +
  437 + /* use HostParameter structure to set Spider's Host Parameter Block */
  438 + ret = wd719x_direct_cmd(wd, WD719X_CMD_SET_PARAM, 0,
  439 + sizeof(struct wd719x_host_param), 0,
  440 + wd->params_phys, WD719X_WAIT_FOR_RISC);
  441 + if (ret) {
  442 + dev_warn(&wd->pdev->dev, "Failed to set HOST PARAMETERS\n");
  443 + goto wd719x_init_end;
  444 + }
  445 +
  446 + /* initiate SCAM (does nothing if disabled in BIOS) */
  447 + /* bug?: we should pass a mask of static IDs which we don't have */
  448 + ret = wd719x_direct_cmd(wd, WD719X_CMD_INIT_SCAM, 0, 0, 0, 0,
  449 + WD719X_WAIT_FOR_SCSI_RESET);
  450 + if (ret) {
  451 + dev_warn(&wd->pdev->dev, "SCAM initialization failed\n");
  452 + goto wd719x_init_end;
  453 + }
  454 +
  455 + /* clear AMR_BIOS_SHARE_INT register */
  456 + wd719x_writeb(wd, WD719X_AMR_BIOS_SHARE_INT, 0);
  457 +
  458 +wd719x_init_end:
  459 + release_firmware(fw_wcs);
  460 + release_firmware(fw_risc);
  461 +
  462 + return ret;
  463 +}
  464 +
  465 +static int wd719x_abort(struct scsi_cmnd *cmd)
  466 +{
  467 + int action, result;
  468 + unsigned long flags;
  469 + struct wd719x_scb *scb = (struct wd719x_scb *)cmd->host_scribble;
  470 + struct wd719x *wd = shost_priv(cmd->device->host);
  471 +
  472 + dev_info(&wd->pdev->dev, "abort command, tag: %x\n", cmd->tag);
  473 +
  474 + action = /*cmd->tag ? WD719X_CMD_ABORT_TAG : */WD719X_CMD_ABORT;
  475 +
  476 + spin_lock_irqsave(wd->sh->host_lock, flags);
  477 + result = wd719x_direct_cmd(wd, action, cmd->device->id,
  478 + cmd->device->lun, cmd->tag, scb->phys, 0);
  479 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  480 + if (result)
  481 + return FAILED;
  482 +
  483 + return SUCCESS;
  484 +}
  485 +
  486 +static int wd719x_reset(struct scsi_cmnd *cmd, u8 opcode, u8 device)
  487 +{
  488 + int result;
  489 + unsigned long flags;
  490 + struct wd719x *wd = shost_priv(cmd->device->host);
  491 +
  492 + dev_info(&wd->pdev->dev, "%s reset requested\n",
  493 + (opcode == WD719X_CMD_BUSRESET) ? "bus" : "device");
  494 +
  495 + spin_lock_irqsave(wd->sh->host_lock, flags);
  496 + result = wd719x_direct_cmd(wd, opcode, device, 0, 0, 0,
  497 + WD719X_WAIT_FOR_SCSI_RESET);
  498 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  499 + if (result)
  500 + return FAILED;
  501 +
  502 + return SUCCESS;
  503 +}
  504 +
  505 +static int wd719x_dev_reset(struct scsi_cmnd *cmd)
  506 +{
  507 + return wd719x_reset(cmd, WD719X_CMD_RESET, cmd->device->id);
  508 +}
  509 +
  510 +static int wd719x_bus_reset(struct scsi_cmnd *cmd)
  511 +{
  512 + return wd719x_reset(cmd, WD719X_CMD_BUSRESET, 0);
  513 +}
  514 +
  515 +static int wd719x_host_reset(struct scsi_cmnd *cmd)
  516 +{
  517 + struct wd719x *wd = shost_priv(cmd->device->host);
  518 + struct wd719x_scb *scb, *tmp;
  519 + unsigned long flags;
  520 + int result;
  521 +
  522 + dev_info(&wd->pdev->dev, "host reset requested\n");
  523 + spin_lock_irqsave(wd->sh->host_lock, flags);
  524 + /* Try to reinit the RISC */
  525 + if (wd719x_chip_init(wd) == 0)
  526 + result = SUCCESS;
  527 + else
  528 + result = FAILED;
  529 +
  530 + /* flush all SCBs */
  531 + list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) {
  532 + struct scsi_cmnd *tmp_cmd = scb->cmd;
  533 + wd719x_finish_cmd(tmp_cmd, result);
  534 + }
  535 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  536 +
  537 + return result;
  538 +}
  539 +
  540 +static int wd719x_biosparam(struct scsi_device *sdev, struct block_device *bdev,
  541 + sector_t capacity, int geom[])
  542 +{
  543 + if (capacity >= 0x200000) {
  544 + geom[0] = 255; /* heads */
  545 + geom[1] = 63; /* sectors */
  546 + } else {
  547 + geom[0] = 64; /* heads */
  548 + geom[1] = 32; /* sectors */
  549 + }
  550 + geom[2] = sector_div(capacity, geom[0] * geom[1]); /* cylinders */
  551 +
  552 + return 0;
  553 +}
  554 +
  555 +/* process a SCB-completion interrupt */
  556 +static inline void wd719x_interrupt_SCB(struct wd719x *wd,
  557 + union wd719x_regs regs,
  558 + struct wd719x_scb *scb)
  559 +{
  560 + struct scsi_cmnd *cmd;
  561 + int result;
  562 +
  563 + /* now have to find result from card */
  564 + switch (regs.bytes.SUE) {
  565 + case WD719X_SUE_NOERRORS:
  566 + result = DID_OK;
  567 + break;
  568 + case WD719X_SUE_REJECTED:
  569 + dev_err(&wd->pdev->dev, "command rejected\n");
  570 + result = DID_ERROR;
  571 + break;
  572 + case WD719X_SUE_SCBQFULL:
  573 + dev_err(&wd->pdev->dev, "SCB queue is full\n");
  574 + result = DID_ERROR;
  575 + break;
  576 + case WD719X_SUE_TERM:
  577 + dev_dbg(&wd->pdev->dev, "SCB terminated by direct command\n");
  578 + result = DID_ABORT; /* or DID_RESET? */
  579 + break;
  580 + case WD719X_SUE_CHAN1ABORT:
  581 + case WD719X_SUE_CHAN23ABORT:
  582 + result = DID_ABORT;
  583 + dev_err(&wd->pdev->dev, "DMA abort\n");
  584 + break;
  585 + case WD719X_SUE_CHAN1PAR:
  586 + case WD719X_SUE_CHAN23PAR:
  587 + result = DID_PARITY;
  588 + dev_err(&wd->pdev->dev, "DMA parity error\n");
  589 + break;
  590 + case WD719X_SUE_TIMEOUT:
  591 + result = DID_TIME_OUT;
  592 + dev_dbg(&wd->pdev->dev, "selection timeout\n");
  593 + break;
  594 + case WD719X_SUE_RESET:
  595 + dev_dbg(&wd->pdev->dev, "bus reset occured\n");
  596 + result = DID_RESET;
  597 + break;
  598 + case WD719X_SUE_BUSERROR:
  599 + dev_dbg(&wd->pdev->dev, "SCSI bus error\n");
  600 + result = DID_ERROR;
  601 + break;
  602 + case WD719X_SUE_WRONGWAY:
  603 + dev_err(&wd->pdev->dev, "wrong data transfer direction\n");
  604 + result = DID_ERROR;
  605 + break;
  606 + case WD719X_SUE_BADPHASE:
  607 + dev_err(&wd->pdev->dev, "invalid SCSI phase\n");
  608 + result = DID_ERROR;
  609 + break;
  610 + case WD719X_SUE_TOOLONG:
  611 + dev_err(&wd->pdev->dev, "record too long\n");
  612 + result = DID_ERROR;
  613 + break;
  614 + case WD719X_SUE_BUSFREE:
  615 + dev_err(&wd->pdev->dev, "unexpected bus free\n");
  616 + result = DID_NO_CONNECT; /* or DID_ERROR ???*/
  617 + break;
  618 + case WD719X_SUE_ARSDONE:
  619 + dev_dbg(&wd->pdev->dev, "auto request sense\n");
  620 + if (regs.bytes.SCSI == 0)
  621 + result = DID_OK;
  622 + else
  623 + result = DID_PARITY;
  624 + break;
  625 + case WD719X_SUE_IGNORED:
  626 + dev_err(&wd->pdev->dev, "target id %d ignored command\n",
  627 + scb->cmd->device->id);
  628 + result = DID_NO_CONNECT;
  629 + break;
  630 + case WD719X_SUE_WRONGTAGS:
  631 + dev_err(&wd->pdev->dev, "reversed tags\n");
  632 + result = DID_ERROR;
  633 + break;
  634 + case WD719X_SUE_BADTAGS:
  635 + dev_err(&wd->pdev->dev, "tag type not supported by target\n");
  636 + result = DID_ERROR;
  637 + break;
  638 + case WD719X_SUE_NOSCAMID:
  639 + dev_err(&wd->pdev->dev, "no SCAM soft ID available\n");
  640 + result = DID_ERROR;
  641 + break;
  642 + default:
  643 + dev_warn(&wd->pdev->dev, "unknown SUE error code: 0x%x\n",
  644 + regs.bytes.SUE);
  645 + result = DID_ERROR;
  646 + break;
  647 + }
  648 + cmd = scb->cmd;
  649 +
  650 + wd719x_finish_cmd(cmd, result);
  651 +}
  652 +
  653 +static irqreturn_t wd719x_interrupt(int irq, void *dev_id)
  654 +{
  655 + struct wd719x *wd = dev_id;
  656 + union wd719x_regs regs;
  657 + unsigned long flags;
  658 + u32 SCB_out;
  659 +
  660 + spin_lock_irqsave(wd->sh->host_lock, flags);
  661 + /* read SCB pointer back from card */
  662 + SCB_out = wd719x_readl(wd, WD719X_AMR_SCB_OUT);
  663 + /* read all status info at once */
  664 + regs.all = cpu_to_le32(wd719x_readl(wd, WD719X_AMR_OP_CODE));
  665 +
  666 + switch (regs.bytes.INT) {
  667 + case WD719X_INT_NONE:
  668 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  669 + return IRQ_NONE;
  670 + case WD719X_INT_LINKNOSTATUS:
  671 + dev_err(&wd->pdev->dev, "linked command completed with no status\n");
  672 + break;
  673 + case WD719X_INT_BADINT:
  674 + dev_err(&wd->pdev->dev, "unsolicited interrupt\n");
  675 + break;
  676 + case WD719X_INT_NOERRORS:
  677 + case WD719X_INT_LINKNOERRORS:
  678 + case WD719X_INT_ERRORSLOGGED:
  679 + case WD719X_INT_SPIDERFAILED:
  680 + /* was the cmd completed a direct or SCB command? */
  681 + if (regs.bytes.OPC == WD719X_CMD_PROCESS_SCB) {
  682 + struct wd719x_scb *scb;
  683 + list_for_each_entry(scb, &wd->active_scbs, list)
  684 + if (SCB_out == scb->phys)
  685 + break;
  686 + if (SCB_out == scb->phys)
  687 + wd719x_interrupt_SCB(wd, regs, scb);
  688 + else
  689 + dev_err(&wd->pdev->dev, "card returned invalid SCB pointer\n");
  690 + } else
  691 + dev_warn(&wd->pdev->dev, "direct command 0x%x completed\n",
  692 + regs.bytes.OPC);
  693 + break;
  694 + case WD719X_INT_PIOREADY:
  695 + dev_err(&wd->pdev->dev, "card indicates PIO data ready but we never use PIO\n");
  696 + /* interrupt will not be cleared until all data is read */
  697 + break;
  698 + default:
  699 + dev_err(&wd->pdev->dev, "unknown interrupt reason: %d\n",
  700 + regs.bytes.INT);
  701 +
  702 + }
  703 + /* clear interrupt so another can happen */
  704 + wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE);
  705 + spin_unlock_irqrestore(wd->sh->host_lock, flags);
  706 +
  707 + return IRQ_HANDLED;
  708 +}
  709 +
  710 +static void wd719x_eeprom_reg_read(struct eeprom_93cx6 *eeprom)
  711 +{
  712 + struct wd719x *wd = eeprom->data;
  713 + u8 reg = wd719x_readb(wd, WD719X_PCI_GPIO_DATA);
  714 +
  715 + eeprom->reg_data_out = reg & WD719X_EE_DO;
  716 +}
  717 +
  718 +static void wd719x_eeprom_reg_write(struct eeprom_93cx6 *eeprom)
  719 +{
  720 + struct wd719x *wd = eeprom->data;
  721 + u8 reg = 0;
  722 +
  723 + if (eeprom->reg_data_in)
  724 + reg |= WD719X_EE_DI;
  725 + if (eeprom->reg_data_clock)
  726 + reg |= WD719X_EE_CLK;
  727 + if (eeprom->reg_chip_select)
  728 + reg |= WD719X_EE_CS;
  729 +
  730 + wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, reg);
  731 +}
  732 +
  733 +/* read config from EEPROM so it can be downloaded by the RISC on (re-)init */
  734 +static void wd719x_read_eeprom(struct wd719x *wd)
  735 +{
  736 + struct eeprom_93cx6 eeprom;
  737 + u8 gpio;
  738 + struct wd719x_eeprom_header header;
  739 +
  740 + eeprom.data = wd;
  741 + eeprom.register_read = wd719x_eeprom_reg_read;
  742 + eeprom.register_write = wd719x_eeprom_reg_write;
  743 + eeprom.width = PCI_EEPROM_WIDTH_93C46;
  744 +
  745 + /* set all outputs to low */
  746 + wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, 0);
  747 + /* configure GPIO pins */
  748 + gpio = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL);
  749 + /* GPIO outputs */
  750 + gpio &= (~(WD719X_EE_CLK | WD719X_EE_DI | WD719X_EE_CS));
  751 + /* GPIO input */
  752 + gpio |= WD719X_EE_DO;
  753 + wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, gpio);
  754 +
  755 + /* read EEPROM header */
  756 + eeprom_93cx6_multireadb(&eeprom, 0, (u8 *)&header, sizeof(header));
  757 +
  758 + if (header.sig1 == 'W' && header.sig2 == 'D')
  759 + eeprom_93cx6_multireadb(&eeprom, header.cfg_offset,
  760 + (u8 *)wd->params,
  761 + sizeof(struct wd719x_host_param));
  762 + else { /* default EEPROM values */
  763 + dev_warn(&wd->pdev->dev, "EEPROM signature is invalid (0x%02x 0x%02x), using default values\n",
  764 + header.sig1, header.sig2);
  765 + wd->params->ch_1_th = 0x10; /* 16 DWs = 64 B */
  766 + wd->params->scsi_conf = 0x4c; /* 48ma, spue, parity check */
  767 + wd->params->own_scsi_id = 0x07; /* ID 7, SCAM disabled */
  768 + wd->params->sel_timeout = 0x4d; /* 250 ms */
  769 + wd->params->sleep_timer = 0x01;
  770 + wd->params->cdb_size = cpu_to_le16(0x5555); /* all 6 B */
  771 + wd->params->scsi_pad = 0x1b;
  772 + if (wd->type == WD719X_TYPE_7193) /* narrow card - disable */
  773 + wd->params->wide = cpu_to_le32(0x00000000);
  774 + else /* initiate & respond to WIDE messages */
  775 + wd->params->wide = cpu_to_le32(0xffffffff);
  776 + wd->params->sync = cpu_to_le32(0xffffffff);
  777 + wd->params->soft_mask = 0x00; /* all disabled */
  778 + wd->params->unsol_mask = 0x00; /* all disabled */
  779 + }
  780 + /* disable TAGGED messages */
  781 + wd->params->tag_en = cpu_to_le16(0x0000);
  782 +}
  783 +
  784 +/* Read card type from GPIO bits 1 and 3 */
  785 +static enum wd719x_card_type wd719x_detect_type(struct wd719x *wd)
  786 +{
  787 + u8 card = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL);
  788 +
  789 + card |= WD719X_GPIO_ID_BITS;
  790 + wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, card);
  791 + card = wd719x_readb(wd, WD719X_PCI_GPIO_DATA) & WD719X_GPIO_ID_BITS;
  792 + switch (card) {
  793 + case 0x08:
  794 + return WD719X_TYPE_7193;
  795 + case 0x02:
  796 + return WD719X_TYPE_7197;
  797 + case 0x00:
  798 + return WD719X_TYPE_7296;
  799 + default:
  800 + dev_warn(&wd->pdev->dev, "unknown card type 0x%x\n", card);
  801 + return WD719X_TYPE_UNKNOWN;
  802 + }
  803 +}
  804 +
  805 +static int wd719x_board_found(struct Scsi_Host *sh)
  806 +{
  807 + struct wd719x *wd = shost_priv(sh);
  808 + char *card_types[] = { "Unknown card", "WD7193", "WD7197", "WD7296" };
  809 + int ret;
  810 +
  811 + INIT_LIST_HEAD(&wd->active_scbs);
  812 + INIT_LIST_HEAD(&wd->free_scbs);
  813 +
  814 + sh->base = pci_resource_start(wd->pdev, 0);
  815 +
  816 + wd->type = wd719x_detect_type(wd);
  817 +
  818 + wd->sh = sh;
  819 + sh->irq = wd->pdev->irq;
  820 + wd->fw_virt = NULL;
  821 +
  822 + /* memory area for host (EEPROM) parameters */
  823 + wd->params = pci_alloc_consistent(wd->pdev,
  824 + sizeof(struct wd719x_host_param),
  825 + &wd->params_phys);
  826 + if (!wd->params) {
  827 + dev_warn(&wd->pdev->dev, "unable to allocate parameter buffer\n");
  828 + return -ENOMEM;
  829 + }
  830 +
  831 + /* memory area for the RISC for hash table of outstanding requests */
  832 + wd->hash_virt = pci_alloc_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE,
  833 + &wd->hash_phys);
  834 + if (!wd->hash_virt) {
  835 + dev_warn(&wd->pdev->dev, "unable to allocate hash buffer\n");
  836 + ret = -ENOMEM;
  837 + goto fail_free_params;
  838 + }
  839 +
  840 + ret = request_irq(wd->pdev->irq, wd719x_interrupt, IRQF_SHARED,
  841 + "wd719x", wd);
  842 + if (ret) {
  843 + dev_warn(&wd->pdev->dev, "unable to assign IRQ %d\n",
  844 + wd->pdev->irq);
  845 + goto fail_free_hash;
  846 + }
  847 +
  848 + /* read parameters from EEPROM */
  849 + wd719x_read_eeprom(wd);
  850 +
  851 + ret = wd719x_chip_init(wd);
  852 + if (ret)
  853 + goto fail_free_irq;
  854 +
  855 + sh->this_id = wd->params->own_scsi_id & WD719X_EE_SCSI_ID_MASK;
  856 +
  857 + dev_info(&wd->pdev->dev, "%s at I/O 0x%lx, IRQ %u, SCSI ID %d\n",
  858 + card_types[wd->type], sh->base, sh->irq, sh->this_id);
  859 +
  860 + return 0;
  861 +
  862 +fail_free_irq:
  863 + free_irq(wd->pdev->irq, wd);
  864 +fail_free_hash:
  865 + pci_free_consistent(wd->pdev, WD719X_HASH_TABLE_SIZE, wd->hash_virt,
  866 + wd->hash_phys);
  867 +fail_free_params:
  868 + pci_free_consistent(wd->pdev, sizeof(struct wd719x_host_param),
  869 + wd->params, wd->params_phys);
  870 +
  871 + return ret;
  872 +}
  873 +
  874 +static struct scsi_host_template wd719x_template = {
  875 + .name = "Western Digital 719x",
  876 + .queuecommand = wd719x_queuecommand,
  877 + .eh_abort_handler = wd719x_abort,
  878 + .eh_device_reset_handler = wd719x_dev_reset,
  879 + .eh_bus_reset_handler = wd719x_bus_reset,
  880 + .eh_host_reset_handler = wd719x_host_reset,
  881 + .bios_param = wd719x_biosparam,
  882 + .proc_name = "wd719x",
  883 + .can_queue = 255,
  884 + .this_id = 7,
  885 + .sg_tablesize = WD719X_SG,
  886 + .cmd_per_lun = WD719X_CMD_PER_LUN,
  887 + .use_clustering = ENABLE_CLUSTERING,
  888 +};
  889 +
  890 +static int wd719x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *d)
  891 +{
  892 + int err;
  893 + struct Scsi_Host *sh;
  894 + struct wd719x *wd;
  895 +
  896 + err = pci_enable_device(pdev);
  897 + if (err)
  898 + goto fail;
  899 +
  900 + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
  901 + dev_warn(&pdev->dev, "Unable to set 32-bit DMA mask\n");
  902 + goto disable_device;
  903 + }
  904 +
  905 + err = pci_request_regions(pdev, "wd719x");
  906 + if (err)
  907 + goto disable_device;
  908 + pci_set_master(pdev);
  909 +
  910 + err = -ENODEV;
  911 + if (pci_resource_len(pdev, 0) == 0)
  912 + goto release_region;
  913 +
  914 + err = -ENOMEM;
  915 + sh = scsi_host_alloc(&wd719x_template, sizeof(struct wd719x));
  916 + if (!sh)
  917 + goto release_region;
  918 +
  919 + wd = shost_priv(sh);
  920 + wd->base = pci_iomap(pdev, 0, 0);
  921 + if (!wd->base)
  922 + goto free_host;
  923 + wd->pdev = pdev;
  924 +
  925 + err = wd719x_board_found(sh);
  926 + if (err)
  927 + goto unmap;
  928 +
  929 + err = scsi_add_host(sh, &wd->pdev->dev);
  930 + if (err)
  931 + goto destroy;
  932 +
  933 + scsi_scan_host(sh);
  934 +
  935 + pci_set_drvdata(pdev, sh);
  936 + return 0;
  937 +
  938 +destroy:
  939 + wd719x_destroy(wd);
  940 +unmap:
  941 + pci_iounmap(pdev, wd->base);
  942 +free_host:
  943 + scsi_host_put(sh);
  944 +release_region:
  945 + pci_release_regions(pdev);
  946 +disable_device:
  947 + pci_disable_device(pdev);
  948 +fail:
  949 + return err;
  950 +}
  951 +
  952 +
  953 +static void wd719x_pci_remove(struct pci_dev *pdev)
  954 +{
  955 + struct Scsi_Host *sh = pci_get_drvdata(pdev);
  956 + struct wd719x *wd = shost_priv(sh);
  957 +
  958 + scsi_remove_host(sh);
  959 + wd719x_destroy(wd);
  960 + pci_iounmap(pdev, wd->base);
  961 + pci_release_regions(pdev);
  962 + pci_disable_device(pdev);
  963 +
  964 + scsi_host_put(sh);
  965 +}
  966 +
  967 +static DEFINE_PCI_DEVICE_TABLE(wd719x_pci_table) = {
  968 + { PCI_DEVICE(PCI_VENDOR_ID_WD, 0x3296) },
  969 + {}
  970 +};
  971 +
  972 +MODULE_DEVICE_TABLE(pci, wd719x_pci_table);
  973 +
  974 +static struct pci_driver wd719x_pci_driver = {
  975 + .name = "wd719x",
  976 + .id_table = wd719x_pci_table,
  977 + .probe = wd719x_pci_probe,
  978 + .remove = wd719x_pci_remove,
  979 +};
  980 +
  981 +static int __init wd719x_init(void)
  982 +{
  983 + return pci_register_driver(&wd719x_pci_driver);
  984 +}
  985 +
  986 +static void __exit wd719x_exit(void)
  987 +{
  988 + pci_unregister_driver(&wd719x_pci_driver);
  989 +}
  990 +
  991 +module_init(wd719x_init);
  992 +module_exit(wd719x_exit);
  993 +
  994 +MODULE_DESCRIPTION("Western Digital WD7193/7197/7296 SCSI driver");
  995 +MODULE_AUTHOR("Ondrej Zary, Aaron Dewell, Juergen Gaertner");
  996 +MODULE_LICENSE("GPL");
  997 +MODULE_FIRMWARE("wd719x-wcs.bin");
  998 +MODULE_FIRMWARE("wd719x-risc.bin");
drivers/scsi/wd719x.h
  1 +#ifndef _WD719X_H_
  2 +#define _WD719X_H_
  3 +
  4 +#define WD719X_SG 255 /* Scatter/gather size */
  5 +#define WD719X_CMD_PER_LUN 1 /* We should be able to do linked commands, but
  6 + * this is 1 for now to be safe. */
  7 +
  8 +struct wd719x_sglist {
  9 + __le32 ptr;
  10 + __le32 length;
  11 +} __packed;
  12 +
  13 +enum wd719x_card_type {
  14 + WD719X_TYPE_UNKNOWN = 0,
  15 + WD719X_TYPE_7193,
  16 + WD719X_TYPE_7197,
  17 + WD719X_TYPE_7296,
  18 +};
  19 +
  20 +union wd719x_regs {
  21 + __le32 all; /* All Status at once */
  22 + struct {
  23 + u8 OPC; /* Opcode register */
  24 + u8 SCSI; /* SCSI Errors */
  25 + u8 SUE; /* Spider unique Errors */
  26 + u8 INT; /* Interrupt Status */
  27 + } bytes;
  28 +};
  29 +
  30 +/* Spider Command Block (SCB) */
  31 +struct wd719x_scb {
  32 + __le32 Int_SCB; /* 00-03 Internal SCB link pointer (must be cleared) */
  33 + u8 SCB_opcode; /* 04 SCB Command opcode */
  34 + u8 CDB_tag; /* 05 SCSI Tag byte for CDB queues (0 if untagged) */
  35 + u8 lun; /* 06 SCSI LUN */
  36 + u8 devid; /* 07 SCSI Device ID */
  37 + u8 CDB[16]; /* 08-23 SCSI CDB (16 bytes as defined by ANSI spec. */
  38 + __le32 data_p; /* 24-27 Data transfer address (or SG list address) */
  39 + __le32 data_length; /* 28-31 Data transfer Length (or SG list length) */
  40 + __le32 CDB_link; /* 32-35 SCSI CDB Link Ptr */
  41 + __le32 sense_buf; /* 36-39 Auto request sense buffer address */
  42 + u8 sense_buf_length;/* 40 Auto request sense transfer length */
  43 + u8 reserved; /* 41 reserved */
  44 + u8 SCB_options; /* 42 SCB-options */
  45 + u8 SCB_tag_msg; /* 43 Tagged messages options */
  46 + /* Not filled in by host */
  47 + __le32 req_ptr; /* 44-47 Ptr to Host Request returned on interrupt */
  48 + u8 host_opcode; /* 48 Host Command Opcode (same as AMR_00) */
  49 + u8 scsi_stat; /* 49 SCSI Status returned */
  50 + u8 ret_error; /* 50 SPIDER Unique Error Code returned (SUE) */
  51 + u8 int_stat; /* 51 Message u8 / Interrupt Status byte returned */
  52 + __le32 transferred; /* 52-55 Bytes Transferred */
  53 + u8 last_trans[3]; /* 56-58 Bytes Transferred in last session */
  54 + u8 length; /* 59 SCSI Messages Length (1-8) */
  55 + u8 sync_offset; /* 60 Synchronous offset */
  56 + u8 sync_rate; /* 61 Synchronous rate */
  57 + u8 flags[2]; /* 62-63 SCB specific flags (local to each thread) */
  58 + /* everything below is for driver use (not used by card) */
  59 + dma_addr_t phys; /* bus address of the SCB */
  60 + struct scsi_cmnd *cmd; /* a copy of the pointer we were passed */
  61 + struct list_head list;
  62 + struct wd719x_sglist sg_list[WD719X_SG] __aligned(8); /* SG list */
  63 +} __packed;
  64 +
  65 +struct wd719x {
  66 + struct Scsi_Host *sh; /* pointer to host structure */
  67 + struct pci_dev *pdev;
  68 + void __iomem *base;
  69 + enum wd719x_card_type type; /* type of card */
  70 + void *fw_virt; /* firmware buffer CPU address */
  71 + dma_addr_t fw_phys; /* firmware buffer bus address */
  72 + size_t fw_size; /* firmware buffer size */
  73 + struct wd719x_host_param *params; /* host parameters (EEPROM) */
  74 + dma_addr_t params_phys; /* host parameters bus address */
  75 + void *hash_virt; /* hash table CPU address */
  76 + dma_addr_t hash_phys; /* hash table bus address */
  77 + struct list_head active_scbs;
  78 + struct list_head free_scbs;
  79 +};
  80 +
  81 +/* timeout delays in microsecs */
  82 +#define WD719X_WAIT_FOR_CMD_READY 500
  83 +#define WD719X_WAIT_FOR_RISC 2000
  84 +#define WD719X_WAIT_FOR_SCSI_RESET 3000000
  85 +
  86 +/* All commands except 0x00 generate an interrupt */
  87 +#define WD719X_CMD_READY 0x00 /* Command register ready (or noop) */
  88 +#define WD719X_CMD_INIT_RISC 0x01 /* Initialize RISC */
  89 +/* 0x02 is reserved */
  90 +#define WD719X_CMD_BUSRESET 0x03 /* Assert SCSI bus reset */
  91 +#define WD719X_CMD_READ_FIRMVER 0x04 /* Read the Firmware Revision */
  92 +#define WD719X_CMD_ECHO_BYTES 0x05 /* Echo command bytes (DW) */
  93 +/* 0x06 is reserved */
  94 +/* 0x07 is reserved */
  95 +#define WD719X_CMD_GET_PARAM 0x08 /* Get programmable parameters */
  96 +#define WD719X_CMD_SET_PARAM 0x09 /* Set programmable parameters */
  97 +#define WD719X_CMD_SLEEP 0x0a /* Put SPIDER to sleep */
  98 +#define WD719X_CMD_READ_INIT 0x0b /* Read initialization parameters */
  99 +#define WD719X_CMD_RESTORE_INIT 0x0c /* Restore initialization parameters */
  100 +/* 0x0d is reserved */
  101 +/* 0x0e is reserved */
  102 +/* 0x0f is reserved */
  103 +#define WD719X_CMD_ABORT_TAG 0x10 /* Send Abort tag message to target */
  104 +#define WD719X_CMD_ABORT 0x11 /* Send Abort message to target */
  105 +#define WD719X_CMD_RESET 0x12 /* Send Reset message to target */
  106 +#define WD719X_CMD_INIT_SCAM 0x13 /* Initiate SCAM */
  107 +#define WD719X_CMD_GET_SYNC 0x14 /* Get synchronous rates */
  108 +#define WD719X_CMD_SET_SYNC 0x15 /* Set synchronous rates */
  109 +#define WD719X_CMD_GET_WIDTH 0x16 /* Get SCSI bus width */
  110 +#define WD719X_CMD_SET_WIDTH 0x17 /* Set SCSI bus width */
  111 +#define WD719X_CMD_GET_TAGS 0x18 /* Get tag flags */
  112 +#define WD719X_CMD_SET_TAGS 0x19 /* Set tag flags */
  113 +#define WD719X_CMD_GET_PARAM2 0x1a /* Get programmable params (format 2) */
  114 +#define WD719X_CMD_SET_PARAM2 0x1b /* Set programmable params (format 2) */
  115 +/* Commands with request pointers (mailbox) */
  116 +#define WD719X_CMD_PROCESS_SCB 0x80 /* Process SCSI Control Block (SCB) */
  117 +/* No interrupt generated on acceptance of SCB pointer */
  118 +
  119 +/* interrupt status defines */
  120 +#define WD719X_INT_NONE 0x00 /* No interrupt pending */
  121 +#define WD719X_INT_NOERRORS 0x01 /* Command completed with no errors */
  122 +#define WD719X_INT_LINKNOERRORS 0x02 /* link cmd completed with no errors */
  123 +#define WD719X_INT_LINKNOSTATUS 0x03 /* link cmd completed with no flag set */
  124 +#define WD719X_INT_ERRORSLOGGED 0x04 /* cmd completed with errors logged */
  125 +#define WD719X_INT_SPIDERFAILED 0x05 /* cmd failed without valid SCSI status */
  126 +#define WD719X_INT_BADINT 0x80 /* unsolicited interrupt */
  127 +#define WD719X_INT_PIOREADY 0xf0 /* data ready for PIO output */
  128 +
  129 +/* Spider Unique Error Codes (SUE) */
  130 +#define WD719X_SUE_NOERRORS 0x00 /* No errors detected by SPIDER */
  131 +#define WD719X_SUE_REJECTED 0x01 /* Command Rejected (bad opcode/param) */
  132 +#define WD719X_SUE_SCBQFULL 0x02 /* SCB queue full */
  133 +/* 0x03 is reserved */
  134 +#define WD719X_SUE_TERM 0x04 /* Host terminated SCB via primative cmd */
  135 +#define WD719X_SUE_CHAN1PAR 0x05 /* PCI Channel 1 parity error occurred */
  136 +#define WD719X_SUE_CHAN1ABORT 0x06 /* PCI Channel 1 system abort occurred */
  137 +#define WD719X_SUE_CHAN23PAR 0x07 /* PCI Channel 2/3 parity error occurred */
  138 +#define WD719X_SUE_CHAN23ABORT 0x08 /* PCI Channel 2/3 system abort occurred */
  139 +#define WD719X_SUE_TIMEOUT 0x10 /* Selection/reselection timeout */
  140 +#define WD719X_SUE_RESET 0x11 /* SCSI bus reset occurred */
  141 +#define WD719X_SUE_BUSERROR 0x12 /* SCSI bus error */
  142 +#define WD719X_SUE_WRONGWAY 0x13 /* Wrong data transfer dir set by target */
  143 +#define WD719X_SUE_BADPHASE 0x14 /* SCSI phase illegal or unexpected */
  144 +#define WD719X_SUE_TOOLONG 0x15 /* target requested too much data */
  145 +#define WD719X_SUE_BUSFREE 0x16 /* Unexpected SCSI bus free */
  146 +#define WD719X_SUE_ARSDONE 0x17 /* Auto request sense executed */
  147 +#define WD719X_SUE_IGNORED 0x18 /* SCSI message was ignored by target */
  148 +#define WD719X_SUE_WRONGTAGS 0x19 /* Tagged SCB & tags off (or vice versa) */
  149 +#define WD719X_SUE_BADTAGS 0x1a /* Wrong tag message type for target */
  150 +#define WD719X_SUE_NOSCAMID 0x1b /* No SCAM soft ID available */
  151 +
  152 +/* code sizes */
  153 +#define WD719X_HASH_TABLE_SIZE 4096
  154 +
  155 +/* Advanced Mode Registers */
  156 +/* Regs 0x00..0x1f are for Advanced Mode of the card (RISC is running). */
  157 +#define WD719X_AMR_COMMAND 0x00
  158 +#define WD719X_AMR_CMD_PARAM 0x01
  159 +#define WD719X_AMR_CMD_PARAM_2 0x02
  160 +#define WD719X_AMR_CMD_PARAM_3 0x03
  161 +#define WD719X_AMR_SCB_IN 0x04
  162 +
  163 +#define WD719X_AMR_BIOS_SHARE_INT 0x0f
  164 +
  165 +#define WD719X_AMR_SCB_OUT 0x18
  166 +#define WD719X_AMR_OP_CODE 0x1c
  167 +#define WD719X_AMR_SCSI_STATUS 0x1d
  168 +#define WD719X_AMR_SCB_ERROR 0x1e
  169 +#define WD719X_AMR_INT_STATUS 0x1f
  170 +
  171 +#define WD719X_DISABLE_INT 0x80
  172 +
  173 +/* SCB flags */
  174 +#define WD719X_SCB_FLAGS_CHECK_DIRECTION 0x01
  175 +#define WD719X_SCB_FLAGS_PCI_TO_SCSI 0x02
  176 +#define WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE 0x10
  177 +#define WD719X_SCB_FLAGS_DO_SCATTER_GATHER 0x20
  178 +#define WD719X_SCB_FLAGS_NO_DISCONNECT 0x40
  179 +
  180 +/* PCI Registers used for reset, initial code download */
  181 +/* Regs 0x20..0x3f are for Normal (DOS) mode (RISC is asleep). */
  182 +#define WD719X_PCI_GPIO_CONTROL 0x3C
  183 +#define WD719X_PCI_GPIO_DATA 0x3D
  184 +#define WD719X_PCI_PORT_RESET 0x3E
  185 +#define WD719X_PCI_MODE_SELECT 0x3F
  186 +
  187 +#define WD719X_PCI_EXTERNAL_ADDR 0x60
  188 +#define WD719X_PCI_INTERNAL_ADDR 0x64
  189 +#define WD719X_PCI_DMA_TRANSFER_SIZE 0x66
  190 +#define WD719X_PCI_CHANNEL2_3CMD 0x68
  191 +#define WD719X_PCI_CHANNEL2_3STATUS 0x69
  192 +
  193 +#define WD719X_GPIO_ID_BITS 0x0a
  194 +#define WD719X_PRAM_BASE_ADDR 0x00
  195 +
  196 +/* codes written to or read from the card */
  197 +#define WD719X_PCI_RESET 0x01
  198 +#define WD719X_ENABLE_ADVANCE_MODE 0x01
  199 +
  200 +#define WD719X_START_CHANNEL2_3DMA 0x17
  201 +#define WD719X_START_CHANNEL2_3DONE 0x01
  202 +#define WD719X_START_CHANNEL2_3ABORT 0x20
  203 +
  204 +/* 33C296 GPIO bits for EEPROM pins */
  205 +#define WD719X_EE_DI (1 << 1)
  206 +#define WD719X_EE_CS (1 << 2)
  207 +#define WD719X_EE_CLK (1 << 3)
  208 +#define WD719X_EE_DO (1 << 4)
  209 +
  210 +/* EEPROM contents */
  211 +struct wd719x_eeprom_header {
  212 + u8 sig1;
  213 + u8 sig2;
  214 + u8 version;
  215 + u8 checksum;
  216 + u8 cfg_offset;
  217 + u8 cfg_size;
  218 + u8 setup_offset;
  219 + u8 setup_size;
  220 +} __packed;
  221 +
  222 +#define WD719X_EE_SIG1 0
  223 +#define WD719X_EE_SIG2 1
  224 +#define WD719X_EE_VERSION 2
  225 +#define WD719X_EE_CHECKSUM 3
  226 +#define WD719X_EE_CFG_OFFSET 4
  227 +#define WD719X_EE_CFG_SIZE 5
  228 +#define WD719X_EE_SETUP_OFFSET 6
  229 +#define WD719X_EE_SETUP_SIZE 7
  230 +
  231 +#define WD719X_EE_SCSI_ID_MASK 0xf
  232 +
  233 +/* SPIDER Host Parameters Block (=EEPROM configuration block) */
  234 +struct wd719x_host_param {
  235 + u8 ch_1_th; /* FIFO threshold */
  236 + u8 scsi_conf; /* SCSI configuration */
  237 + u8 own_scsi_id; /* controller SCSI ID */
  238 + u8 sel_timeout; /* selection timeout*/
  239 + u8 sleep_timer; /* seep timer */
  240 + __le16 cdb_size;/* CDB size groups */
  241 + __le16 tag_en; /* Tag msg enables (ID 0-15) */
  242 + u8 scsi_pad; /* SCSI pad control */
  243 + __le32 wide; /* WIDE msg options (ID 0-15) */
  244 + __le32 sync; /* SYNC msg options (ID 0-15) */
  245 + u8 soft_mask; /* soft error mask */
  246 + u8 unsol_mask; /* unsolicited error mask */
  247 +} __packed;
  248 +
  249 +#endif /* _WD719X_H_ */