* [PATCH] sym53c500_cs PCMCIA SCSI driver (new)
@ 2004-04-10 2:17 Bob Tracy
2004-04-16 12:05 ` Christoph Hellwig
0 siblings, 1 reply; 15+ messages in thread
From: Bob Tracy @ 2004-04-10 2:17 UTC (permalink / raw)
To: linux-kernel; +Cc: linux-scsi
[-- Attachment #1: Type: text/plain, Size: 951 bytes --]
The attached patch set implements a PCMCIA SCSI driver for host adapters
based on the Symbios 53c500 chip. The original driver for this chip was
written by Thomas Corner and available only as an add-on to the
pcmcia-cs package. I've been maintaining the add-on driver on an
infrequent basis for the past several years, and the release of the 2.6
kernel "forced" a long overdue update.
The only host adapter I'm aware of that uses the sym53c500 controller
chip is the "new" version of the New Media Bus Toaster (circa 1996),
and the attached driver has been tested using this particular adapter
on a 2.6.4 kernel. The patch set applies cleanly to 2.6.4 and 2.6.5.
Comments / feedback / cheers / jeers accepted...
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
[-- Attachment #2: patch05_sym53c500 --]
[-- Type: text/plain, Size: 39062 bytes --]
--- linux/drivers/scsi/pcmcia/Kconfig.orig Thu Apr 1 13:53:55 2004
+++ linux/drivers/scsi/pcmcia/Kconfig Thu Apr 1 14:00:42 2004
@@ -69,4 +69,14 @@
To compile this driver as a module, choose M here: the
module will be called qlogic_cs.
+config PCMCIA_SYM53C500
+ tristate "Symbios 53c500 PCMCIA support"
+ depends on m
+ help
+ Say Y here if you have a New Media Bus Toaster or other PCMCIA
+ SCSI adapter based on the Symbios 53c500 controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sym53c500_cs.
+
endmenu
--- linux/drivers/scsi/pcmcia/Makefile.orig Sat Mar 29 21:39:31 2003
+++ linux/drivers/scsi/pcmcia/Makefile Thu Apr 1 13:51:01 2004
@@ -6,7 +6,9 @@
obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o
obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o
obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o
+obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o
aha152x_cs-objs := aha152x_stub.o aha152x_core.o
fdomain_cs-objs := fdomain_stub.o fdomain_core.o
qlogic_cs-objs := qlogic_stub.o qlogic_core.o
+sym53c500_cs-objs := sym53c500.o SYM53C500.o
--- linux/drivers/scsi/pcmcia/SYM53C500.c.orig Mon Apr 5 14:51:07 2004
+++ linux/drivers/scsi/pcmcia/SYM53C500.c Fri Apr 9 14:44:32 2004
@@ -0,0 +1,860 @@
+/*
+* SYM53C500.c
+*
+* Low-level SCSI driver for SYM53C500 chip - Bob Tracy (rct@frus.com)
+*
+* Original driver by Tom Corner (tcorner@via.at) was adapted from
+* NCR53c406a.c which is Copyrighted (C) 1994, 1995, 1996
+* Normunds Saumanis (normunds@fi.ibm.com)
+*
+* A rewrite has been long overdue, and the 2.6 kernel forced the issue.
+* All the USE_BIOS code has been ripped out. It was never used, and
+* could not have worked anyway. USE_DMA has gone bye-bye too.
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*/
+
+#define SYM53C500_DEBUG 0
+#define VERBOSE_SYM53C500_DEBUG 0
+
+/* Set this to 0 if you encounter kernel lockups while transferring
+ * data in PIO mode */
+#define USE_FAST_PIO 1
+
+/* ============= End of user configurable parameters ============= */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+
+#include "scsi.h"
+#include "hosts.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+#include "SYM53C500.h"
+
+/* ============================================================= */
+
+#define WATCHDOG 5000000
+
+#define SYNC_MODE 0 /* Synchronous transfer mode */
+
+#if DEBUG
+#undef SYM53C500_DEBUG
+#define SYM53C500_DEBUG 1
+#endif
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#define C3_IMG 0x20 /* CDB */
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xa4 /*? changed from b6= AA PI SIE POL */
+#define C7_IMG 0x80 /* added for SYM53C500 t. corner */
+
+#define REG0 (outb(C4_IMG, CONFIG4)) /* select register set 0 */
+#define REG1 outb(C7_IMG, CONFIG7); outb(C5_IMG, CONFIG5) /* select register set 1 */
+
+#if SYM53C500_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_SYM53C500_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(count) \
+ outb(count & 0xff, TC_LSB); \
+ outb((count >> 8) & 0xff, TC_MSB); \
+ outb((count >> 16) & 0xff, TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+
+#if SYM53C500_DEBUG
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+/*----------------------------------------------------------------*/
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* Static function prototypes */
+static void SYM53C500_intr(int, void *, struct pt_regs *);
+static irqreturn_t do_SYM53C500_intr(int, void *, struct pt_regs *);
+static void chip_init(void);
+static void calc_port_addr(void);
+static int irq_probe(void);
+
+/* ================================================================= */
+
+static int port_base = 0;
+static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized'*/
+
+static int fast_pio = USE_FAST_PIO;
+
+static Scsi_Cmnd *current_SC;
+static char info_msg[256];
+
+/* ================================================================= */
+
+/* possible i/o port addresses */
+static unsigned short ports[] = { 0x130, 0x230, 0x280, 0x290, 0x320, 0x330, 0x340, 0x350 };
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+/* ============================================================ */
+
+/* Control Register Set 0 */
+static int TC_LSB; /* transfer counter lsb */
+static int TC_MSB; /* transfer counter msb */
+static int SCSI_FIFO; /* scsi fifo register */
+static int CMD_REG; /* command register */
+static int STAT_REG; /* status register */
+static int DEST_ID; /* selection/reselection bus id */
+static int INT_REG; /* interrupt status register */
+static int SRTIMOUT; /* select/reselect timeout reg */
+static int SEQ_REG; /* sequence step register */
+static int SYNCPRD; /* synchronous transfer period */
+static int FIFO_FLAGS; /* indicates # of bytes in fifo */
+static int SYNCOFF; /* synchronous offset register */
+static int CONFIG1; /* configuration register */
+static int CLKCONV; /* clock conversion reg */
+/*static int TESTREG;*/ /* test mode register */
+static int CONFIG2; /* Configuration 2 Register */
+static int CONFIG3; /* Configuration 3 Register */
+static int CONFIG4; /* Configuration 4 Register */
+static int TC_HIGH; /* Transfer Counter High */
+/*static int FIFO_BOTTOM;*/ /* Reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/*static int JUMPER_SENSE;*/ /* Jumper sense port reg (r/w) */
+/*static int SRAM_PTR;*/ /* SRAM address pointer reg (r/w) */
+/*static int SRAM_DATA;*/ /* SRAM data register (r/w) */
+static int PIO_FIFO; /* PIO FIFO registers (r/w) */
+/*static int PIO_FIFO1;*/ /* */
+/*static int PIO_FIFO2;*/ /* */
+/*static int PIO_FIFO3;*/ /* */
+static int PIO_STATUS; /* PIO status (r/w) */
+/*static int ATA_CMD;*/ /* ATA command/status reg (r/w) */
+/*static int ATA_ERR;*/ /* ATA features/error register (r/w)*/
+static int PIO_FLAG; /* PIO flag interrupt enable (r/w) */
+static int CONFIG5; /* Configuration 5 register (r/w) */
+/*static int SIGNATURE;*/ /* Signature Register (r) */
+/*static int CONFIG6;*/ /* Configuration 6 register (r) */
+static int CONFIG7;
+
+/* ============================================================== */
+
+/*
+* SYM53C500_proc_info is basically a stub function for now. It
+* wouldn't exist except for the fact there were /proc entries for
+* this driver under 2.4 and earlier kernels, and the author (sole
+* user?) expects to see them.
+*
+* buffer: I/O buffer
+* start: if inout == FALSE, pointer into buffer where user
+* read should start
+* offset: current offset
+* length: length of buffer
+* hostno: Scsi_Host host_no
+* inout: 1 --> user is writing; 0 --> user is reading
+*/
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { \
+ if (length > (pos - buffer)) \
+ pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
+ } while (0)
+
+int
+SYM53C500_proc_info(struct Scsi_Host *shost, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ char *pos = buffer;
+ int thislength;
+
+ if (inout)
+ return(-ENOSYS); /* look, but don't touch */
+
+ /*
+ * Cheesy, but it's what we had before.
+ */
+ SPRINTF("%s\n", info_msg);
+
+ thislength = pos - (buffer + offset);
+
+ if (thislength < 0) {
+ *start = 0;
+ return 0;
+ }
+
+ thislength = MIN(thislength, length);
+ *start = buffer + offset;
+
+ return thislength;
+}
+
+static __inline__ int
+SYM53C500_pio_read(unsigned char *request, unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+
+ REG1;
+ while (reqlen) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch (i & 0x1e) {
+ default:
+ case 0x10: /* fifo empty */
+ len = 0;
+ break;
+ case 0x0:
+ len = 1;
+ break;
+ case 0x8: /* fifo 1/3 full */
+ len = 42;
+ break;
+ case 0xc: /* fifo 2/3 full */
+ len = 84;
+ break;
+ case 0xe: /* fifo full */
+ len = 128;
+ break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+ return 0;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ insl(PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ *request++ = inb(PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static __inline__ int
+SYM53C500_pio_write(unsigned char *request, unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+
+ REG1;
+ while (reqlen && !(i & 0x40)) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch (i & 0x1e) {
+ case 0x10:
+ len = 128;
+ break;
+ case 0x0:
+ len = 84;
+ break;
+ case 0x8:
+ len = 42;
+ break;
+ case 0xc:
+ len = 1;
+ break;
+ default:
+ case 0xe:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ outsl(PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ outb(*request++, PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+struct Scsi_Host *
+__SYM53C500_detect(struct scsi_host_template *tpnt)
+{
+ struct Scsi_Host *shpnt;
+ int i;
+
+ if (port_base) {
+ if (!request_region(port_base, 0x10, "SYM53C500"))
+ port_base = 0;
+ } else {
+ for (i = 0; i < PORT_COUNT && !port_base; i++) {
+ if (!request_region(ports[i], 0x10, "SYM53C500")) {
+ DEB(printk("SYM53C500: port %x in use\n", ports[i]));
+ } else {
+ VDEB(printk("SYM53C500: port %x available\n", ports[i]));
+ outb(C5_IMG, ports[i] + 0x0d); /* reg set 1 */
+ if ((inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7 && (inb(ports[i] + 0x0e) & 0xf8) == 0x58) {
+ port_base = ports[i];
+ VDEB(printk("SYM53C500: Sig register valid\n"));
+ VDEB(printk("port_base=%x\n", port_base));
+ break;
+ }
+ release_region(ports[i], 0x10);
+ }
+ }
+ }
+
+ if (!port_base) { /* no ports found */
+ printk("SYM53C500: no available ports found\n");
+ return NULL;
+ }
+
+ DEB(printk("SYM53C500 detected at 0x%x\n", port_base));
+
+ calc_port_addr();
+ chip_init();
+
+ if (irq_level < 0) {
+ irq_level = irq_probe();
+ if (irq_level < 0) { /* Trouble */
+ printk("SYM53C500: IRQ problem, irq_level=%d, giving up\n", irq_level);
+ goto err_release;
+ }
+ }
+
+ shpnt = scsi_host_alloc(tpnt, 0);
+ if (!shpnt) {
+ printk("SYM53C500: Unable to register host, giving up.\n");
+ goto err_release;
+ }
+
+ if (irq_level > 0) {
+ if (request_irq(irq_level, do_SYM53C500_intr, 0, "SYM53C500", shpnt)) {
+ printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
+ goto err_free_scsi;
+ }
+ tpnt->can_queue = 1;
+ DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
+ } else if (irq_level == 0) {
+ tpnt->can_queue = 0;
+ DEB(printk("SYM53C500: No interrupts detected\n"));
+ goto err_free_scsi;
+ } else {
+ DEB(printk("SYM53C500: Shouldn't get here!\n"));
+ goto err_free_scsi;
+ }
+
+ shpnt->irq = irq_level;
+ shpnt->io_port = port_base;
+ shpnt->n_io_port = 0x10;
+ shpnt->dma_channel = -1;
+
+ sprintf(info_msg, "SYM53C500 at 0x%x, IRQ %d, %s PIO mode.",
+ port_base, irq_level, fast_pio ? "fast" : "slow");
+
+ return (shpnt);
+
+err_free_scsi:
+ scsi_host_put(shpnt);
+err_release:
+ release_region(port_base, 0x10);
+ return NULL;
+}
+
+int
+SYM53C500_detect(struct scsi_host_template *sht)
+{
+ return (__SYM53C500_detect(sht) != NULL);
+}
+
+/*
+* Note fast_pio is global and set at compile time.
+*/
+void
+SYM53C500_preset(int port, int irq)
+{
+ port_base = port;
+ irq_level = irq;
+
+ DEB(printk("SYM53C500: port_base=0x%X, irq=%d, fast_pio=%d\n",
+ port_base, irq_level, fast_pio);)
+}
+
+const char*
+SYM53C500_info(struct Scsi_Host *SChost)
+{
+ DEB(printk("SYM53C500_info called\n"));
+ return (info_msg);
+}
+
+int
+SYM53C500_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ int i;
+
+ VDEB(printk("SYM53C500_queue called\n"));
+/*
+* target and lun members no longer exist.
+*
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
+ SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->target,
+ SCpnt->lun, SCpnt->request_bufflen));
+*/
+
+ VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+
+ current_SC = SCpnt;
+ current_SC->scsi_done = done;
+ current_SC->SCp.phase = command_ph;
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+
+ /* We are locked here already by the mid layer */
+ REG0;
+ outb(SCpnt->device->id, DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, CMD_REG); /* reset the fifos */
+
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ outb(SCpnt->cmnd[i], SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, CMD_REG);
+
+ rtrc(1);
+ return 0;
+}
+
+int
+SYM53C500_abort(Scsi_Cmnd *SCpnt)
+{
+ DEB(printk("SYM53C500_abort called\n"));
+ return FAILED; /* Don't know how to abort */
+}
+
+int
+SYM53C500_host_reset(Scsi_Cmnd *SCpnt)
+{
+ DEB(printk("SYM53C500_host_reset called\n"));
+ outb(C4_IMG, CONFIG4); /* Select reg set 0 */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG); /* required after reset */
+ outb(SCSI_RESET, CMD_REG);
+ chip_init();
+
+ rtrc(2);
+ return SUCCESS;
+}
+
+int
+SYM53C500_device_reset(Scsi_Cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+int
+SYM53C500_bus_reset(Scsi_Cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+int
+SYM53C500_biosparm(struct scsi_device *disk,
+ struct block_device *dev,
+ sector_t capacity, int *info_array)
+{
+ int size;
+
+ DEB(printk("SYM53C500_biosparm called\n"));
+
+ size = capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size >> 11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255 * 63);
+ }
+ return 0;
+}
+
+static irqreturn_t
+do_SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ SYM53C500_intr(irq, dev_id, regs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ DEB(unsigned char fifo_size;)
+ DEB(unsigned char seq_reg;)
+ unsigned char status, int_reg;
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+
+ VDEB(printk("SYM53C500_intr called\n"));
+
+ REG1;
+ pio_status = inb(PIO_STATUS);
+ REG0;
+ status = inb(STAT_REG);
+ DEB(seq_reg = inb(SEQ_REG));
+ int_reg = inb(INT_REG);
+ DEB(fifo_size = inb(FIFO_FLAGS) & 0x1f);
+
+#if SYM53C500_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x",
+ status, seq_reg, int_reg, fifo_size);
+ printk(", pio=%02x\n", pio_status);
+#endif /* SYM53C500_DEBUG */
+
+ if (int_reg & 0x80) { /* SCSI reset intr */
+ rtrc(3);
+ DEB(printk("SYM53C500: reset intr received\n"));
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_RESET << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (pio_status & 0x80) {
+ printk("SYM53C500: Warning: PIO error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (status & 0x20) { /* Parity error */
+ printk("SYM53C500: Warning: parity error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_PARITY << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (status & 0x40) { /* Gross error */
+ printk("SYM53C500: Warning: gross error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (int_reg & 0x20) { /* Disconnect */
+ DEB(printk("SYM53C500: disconnect intr received\n"));
+ if (current_SC->SCp.phase != message_in) { /* Unexpected disconnect */
+ current_SC->result = DID_NO_CONNECT << 16;
+ } else { /* Command complete, return status and message */
+ current_SC->result = (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+
+ rtrc(0);
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ switch (status & 0x07) { /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ rtrc(5);
+ current_SC->SCp.phase = data_out;
+ VDEB(printk("SYM53C500: Data-Out phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_write(current_SC->request_buffer, current_SC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_write(page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ rtrc(6);
+ current_SC->SCp.phase = data_in;
+ VDEB(printk("SYM53C500: Data-In phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_read(current_SC->request_buffer, current_SC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_read(page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ current_SC->SCp.phase = command_ph;
+ printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ rtrc(7);
+ current_SC->SCp.phase = status_ph;
+ VDEB(printk("SYM53C500: Status phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ outb(INIT_CMD_COMPLETE, CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("SYM53C500: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("SYM53C500: Message-Out phase\n"));
+ current_SC->SCp.phase = message_out;
+ outb(SET_ATN, CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ rtrc(4);
+ VDEB(printk("SYM53C500: Message-In phase\n"));
+ current_SC->SCp.phase = message_in;
+
+ current_SC->SCp.Status = inb(SCSI_FIFO);
+ current_SC->SCp.Message = inb(SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n", current_SC->SCp.Status, current_SC->SCp.Message));
+
+ if (current_SC->SCp.Message == SAVE_POINTERS || current_SC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+ }
+}
+
+static int
+irq_probe(void)
+{
+ int irqs, irq;
+ unsigned long i;
+
+ inb(INT_REG); /* clear the interrupt register */
+ irqs = probe_irq_on();
+
+ /* Invalid command will cause an interrupt */
+ REG0;
+ outb(0xff, CMD_REG);
+
+ /* Wait for the interrupt to occur */
+ i = jiffies + WATCHDOG;
+ while (time_after(i, jiffies) && !(inb(STAT_REG) & 0x80))
+ barrier();
+ if (time_before_eq(i, jiffies)) { /* Timed out, must be hardware trouble */
+ probe_irq_off(irqs);
+ return -1;
+ }
+
+ irq = probe_irq_off(irqs);
+
+ /* Kick the chip */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG);
+ chip_init();
+
+ return irq;
+}
+
+static void
+chip_init(void)
+{
+ REG1;
+ outb(0x01, PIO_STATUS);
+ outb(0x00, PIO_FLAG);
+
+ outb(C4_IMG, CONFIG4); /* REG0; */
+ outb(C3_IMG, CONFIG3);
+ outb(C2_IMG, CONFIG2);
+ outb(C1_IMG, CONFIG1);
+
+ outb(0x05, CLKCONV); /* clock conversion factor */
+ outb(0x9C, SRTIMOUT); /* Selection timeout */
+ outb(0x05, SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, SYNCOFF); /* synchronous mode */
+}
+
+void
+calc_port_addr(void)
+{
+ /* Control Register Set 0 */
+ TC_LSB = (port_base + 0x00);
+ TC_MSB = (port_base + 0x01);
+ SCSI_FIFO = (port_base + 0x02);
+ CMD_REG = (port_base + 0x03);
+ STAT_REG = (port_base + 0x04);
+ DEST_ID = (port_base + 0x04);
+ INT_REG = (port_base + 0x05);
+ SRTIMOUT = (port_base + 0x05);
+ SEQ_REG = (port_base + 0x06);
+ SYNCPRD = (port_base + 0x06);
+ FIFO_FLAGS = (port_base + 0x07);
+ SYNCOFF = (port_base + 0x07);
+ CONFIG1 = (port_base + 0x08);
+ CLKCONV = (port_base + 0x09);
+ /* TESTREG = (port_base + 0x0A); */
+ CONFIG2 = (port_base + 0x0B);
+ CONFIG3 = (port_base + 0x0C);
+ CONFIG4 = (port_base + 0x0D);
+ TC_HIGH = (port_base + 0x0E);
+ /* FIFO_BOTTOM = (port_base + 0x0F); */
+
+ /* Control Register Set 1 */
+ /* JUMPER_SENSE = (port_base + 0x00); */
+ /* SRAM_PTR = (port_base + 0x01); */
+ /* SRAM_DATA = (port_base + 0x02); */
+ PIO_FIFO = (port_base + 0x04);
+ /* PIO_FIFO1 = (port_base + 0x05); */
+ /* PIO_FIFO2 = (port_base + 0x06); */
+ /* PIO_FIFO3 = (port_base + 0x07); */
+ PIO_STATUS = (port_base + 0x08);
+ /* ATA_CMD = (port_base + 0x09); */
+ /* ATA_ERR = (port_base + 0x0A); */
+ PIO_FLAG = (port_base + 0x0B);
+ CONFIG5 = (port_base + 0x09);
+ /* SIGNATURE = (port_base + 0x0E); */
+ /* CONFIG6 = (port_base +0x0F); */
+ CONFIG7 = (port_base + 0x0d);
+}
+
+MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
+MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver");
+MODULE_LICENSE("GPL");
+
+struct scsi_host_template sym53c500_driver_template = SYM53C500;
--- linux/drivers/scsi/pcmcia/SYM53C500.h.orig Mon Apr 5 14:51:11 2004
+++ linux/drivers/scsi/pcmcia/SYM53C500.h Fri Apr 9 15:05:37 2004
@@ -0,0 +1,69 @@
+#ifndef _SYM53C500_H
+#define _SYM53C500_H
+
+/*
+* SYM53C500.h
+*
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at)
+*
+* Adapted from NCR53c406a.h which is Copyrighted (C) 1994
+* Normunds Saumanis (normunds@rx.tech.swh.lv)
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*/
+
+/* A useful macro... */
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+
+int SYM53C500_detect(struct scsi_host_template *);
+struct Scsi_Host *__SYM53C500_detect(struct scsi_host_template *);
+const char *SYM53C500_info(struct Scsi_Host *);
+int SYM53C500_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int SYM53C500_abort(Scsi_Cmnd *);
+int SYM53C500_device_reset(Scsi_Cmnd *);
+int SYM53C500_bus_reset(Scsi_Cmnd *);
+int SYM53C500_host_reset(Scsi_Cmnd *);
+int SYM53C500_biosparm(struct scsi_device *, struct block_device *, sector_t, int *);
+int SYM53C500_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/*
+* scsi_host_template initializer
+*
+* Include file defining structure implies proc_info is optional.
+* Trouble is, directory for host adapter won't get created unless
+* there's a proc_info routine, i.e., nothing will appear in /proc.
+* This is different from the 2.4 implementation.
+*/
+#define SYM53C500 { \
+ .module = THIS_MODULE, \
+ .name = "SYM53C500", \
+ .info = SYM53C500_info, \
+ .queuecommand = SYM53C500_queue, \
+ .eh_abort_handler = SYM53C500_abort, \
+ .eh_device_reset_handler = SYM53C500_device_reset,\
+ .eh_bus_reset_handler = SYM53C500_bus_reset, \
+ .eh_host_reset_handler = SYM53C500_host_reset, \
+ .bios_param = SYM53C500_biosparm, \
+ .proc_info = SYM53C500_proc_info, \
+ .proc_name = "SYM53C500", \
+ .can_queue = 1, \
+ .this_id = 7, \
+ .sg_tablesize = 32, \
+ .cmd_per_lun = 1, \
+ .use_clustering = ENABLE_CLUSTERING \
+}
+
+#endif /* _SYM5C500_H */
--- linux/drivers/scsi/pcmcia/sym53c500.c.orig Mon Apr 5 14:51:19 2004
+++ linux/drivers/scsi/pcmcia/sym53c500.c Thu Apr 8 15:12:18 2004
@@ -0,0 +1,377 @@
+/*
+* sym53c500.c
+*
+* A driver for newer Bus Toaster SCSI cards using the SYM53C500 chip
+* adapted by Tom Corner (tcorner@via.at) from a driver for the Qlogic
+* SCSI card written by David Hinds (dhinds@allegro.stanford.edu).
+*
+* Rewritten by Bob Tracy (rct@frus.com) for inclusion in the 2.6 and later
+* kernel source trees. The pcmcia-cs add-on version of this driver is not
+* supported beyond 2.4.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <scsi/scsi.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_ioctl.h>
+
+#include "scsi.h"
+#include "hosts.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+#include "SYM53C500.h"
+
+extern struct scsi_host_template sym53c500_driver_template;
+/* extern void SYM53C500_setup(char *str, int *ints); */
+extern void SYM53C500_preset(int port, int irq);
+extern struct Scsi_Host *__SYM53C500_detect(struct scsi_host_template *);
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"sym53c500_cs.c 0.7 2004/04/01 14:15:58 (Bob Tracy)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static unsigned int irq_mask = 0xdeb8; /* 3, 6, 7, 9-12, 14, 15 */
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/*====================================================================*/
+
+typedef struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+ unsigned short manf_id;
+} scsi_info_t;
+
+static void SYM53C500_release(dev_link_t *link);
+static int SYM53C500_event(event_t event, int priority, event_callback_args_t *args);
+static dev_link_t *SYM53C500_attach(void);
+static void SYM53C500_detach(dev_link_t *);
+
+static dev_link_t *dev_list = NULL;
+static dev_info_t dev_info = "sym53c500_cs";
+
+static dev_link_t *
+SYM53C500_attach(void)
+{
+ scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int i, ret;
+
+ DEBUG(0, "SYM53C500_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link;
+ link->priv = info;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.event_handler = &SYM53C500_event;
+ client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ SYM53C500_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* SYM53C500_attach */
+
+/*====================================================================*/
+
+static void
+SYM53C500_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link)
+ break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits. */
+ *linkp = link->next;
+ kfree(link->priv);
+ link->priv = NULL;
+} /* SYM53C500_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void
+SYM53C500_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ unsigned short tuple_data[32];
+ struct Scsi_Host *host;
+
+ DEBUG(0, "SYM53C500_config(0x%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *)tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+ (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+ info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+
+ if (link->io.BasePort1 != 0) {
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /*
+ * That's the trouble with copying liberally from another driver.
+ * Some things probably aren't relevant, and I suspect this entire
+ * section dealing with manufacturer IDs can be scrapped. --rct
+ */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ /* set ATAcmd */
+ outb(0xb4, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+
+ /*
+ * A bad hack: __SYM53C500_detect() will do a request_region(),
+ * so we have to get out of the way first.
+ */
+ release_region(link->io.BasePort1, link->io.NumPorts1);
+
+ SYM53C500_preset(link->io.BasePort1, link->irq.AssignedIRQ);
+
+ host = __SYM53C500_detect(&sym53c500_driver_template);
+ if (!host) {
+ printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
+ goto out;
+ }
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+ scsi_add_host(host, NULL); /* XXX handle failure */
+ scsi_scan_host(host);
+
+out:
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ SYM53C500_release(link);
+ return;
+} /* SYM53C500_config */
+
+/*====================================================================*/
+
+static void
+SYM53C500_release(dev_link_t *link)
+{
+ scsi_info_t *info = link->priv;
+ struct Scsi_Host *shost = info->host;
+
+ DEBUG(0, "SYM53C500_release(0x%p)\n", link);
+
+ /*
+ * Interrupts getting hosed on card removal. Try
+ * the following code, mostly from qlogicfas.c.
+ */
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+
+ scsi_remove_host(shost);
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+ scsi_host_put(shost);
+} /* SYM53C500_release */
+
+/*====================================================================*/
+
+static int
+SYM53C500_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ scsi_info_t *info = link->priv;
+
+ DEBUG(1, "SYM53C500_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ SYM53C500_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle, &link->conf);
+ /* See earlier comment about manufacturer IDs. */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ outb(0x80, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+ /* Fugly! */
+ SYM53C500_bus_reset(NULL);
+ }
+ break;
+ }
+ return 0;
+} /* SYM53C500_event */
+
+/*====================================================================*/
+
+static struct pcmcia_driver sym53c500_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "sym53c500_cs",
+ },
+ .attach = SYM53C500_attach,
+ .detach = SYM53C500_detach,
+};
+
+static int __init
+init_sym53c500_cs(void)
+{
+ return pcmcia_register_driver(&sym53c500_cs_driver);
+}
+
+static void __exit
+exit_sym53c500_cs(void)
+{
+ pcmcia_unregister_driver(&sym53c500_cs_driver);
+
+ /* XXX: this really needs to move into generic code... */
+ while (dev_list != NULL)
+ SYM53C500_detach(dev_list);
+}
+
+module_init(init_sym53c500_cs);
+module_exit(exit_sym53c500_cs);
--- linux/Documentation/scsi/sym53c500_cs.txt.orig Thu Apr 1 16:42:23 2004
+++ linux/Documentation/scsi/sym53c500_cs.txt Mon Apr 5 16:00:24 2004
@@ -0,0 +1,16 @@
+The sym53c500_cs driver originated as an add-on to David Hinds' pcmcia-cs
+package, and was written by Tom Corner (tcorner@via.at). This rewrite
+addresses the following concerns:
+
+ (1) extensive kernel changes between 2.4 and 2.6.
+ (2) deprecated PCMCIA support outside the kernel.
+
+The Symbios Logic 53c500 chip was used in the "new" (circa 1997) version
+of the New Media Bus Toaster PCMCIA SCSI controller. I am unaware of any
+other products using this chip.
+
+Through the years, there have been a number of downloads of the pcmcia-cs
+version of this driver, and I guess it worked for those users. It worked
+for Tom Corner, and it works for me. Your mileage will probably vary.
+
+--Bob Tracy (rct@frus.com)
--- linux/Documentation/scsi/00-INDEX.orig Sat Mar 29 21:17:08 2003
+++ linux/Documentation/scsi/00-INDEX Thu Apr 1 16:44:34 2004
@@ -62,6 +62,8 @@
- info on API between SCSI layer and low level drivers
st.txt
- info on scsi tape driver
+sym53c500_cs.txt
+ - info on PCMCIA driver for Symbios Logic 53c500 based adapters
sym53c8xx_2.txt
- info on second generation driver for sym53c8xx based adapters
tmscsim.txt
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (new)
2004-04-10 2:17 [PATCH] sym53c500_cs PCMCIA SCSI driver (new) Bob Tracy
@ 2004-04-16 12:05 ` Christoph Hellwig
2004-04-16 14:17 ` Bob Tracy
0 siblings, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2004-04-16 12:05 UTC (permalink / raw)
To: Bob Tracy; +Cc: linux-kernel, linux-scsi
On Fri, Apr 09, 2004 at 09:17:03PM -0500, Bob Tracy wrote:
> The attached patch set implements a PCMCIA SCSI driver for host adapters
> based on the Symbios 53c500 chip. The original driver for this chip was
> written by Thomas Corner and available only as an add-on to the
> pcmcia-cs package. I've been maintaining the add-on driver on an
> infrequent basis for the past several years, and the release of the 2.6
> kernel "forced" a long overdue update.
>
> The only host adapter I'm aware of that uses the sym53c500 controller
> chip is the "new" version of the New Media Bus Toaster (circa 1996),
> and the attached driver has been tested using this particular adapter
> on a 2.6.4 kernel. The patch set applies cleanly to 2.6.4 and 2.6.5.
>
> Comments / feedback / cheers / jeers accepted...
I've given it a short spin and here's a bunch of comments:
- the split into three source files is supserflous, one file should do it
- please don't use host.h or scsi.h from drivers/scsi/. The defintions
not present in include/scsi/ are deprecated and shall not be used (the
most prominent example in your driver are the Scsi_<Foo> typedefs that
have been replaced by struct scsi_foo
- the driver doesn't even try to deal with multiple HBAs
- your detection logic could be streamlined a little, e.g. the request/release
resource mess
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (new)
2004-04-16 12:05 ` Christoph Hellwig
@ 2004-04-16 14:17 ` Bob Tracy
2004-04-17 9:45 ` Christoph Hellwig
2004-04-22 19:38 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (new) Bill Davidsen
0 siblings, 2 replies; 15+ messages in thread
From: Bob Tracy @ 2004-04-16 14:17 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-kernel, linux-scsi
Christoph Hellwig wrote:
> I've given it a short spin and here's a bunch of comments:
Thank you for taking the time and trouble to review it.
> - the split into three source files is supserflous, one file should do it
Given that the driver currently supports only PCMCIA implementations,
I agree. My thinking was if someone comes up with a host adapter that
isn't PCMCIA, the SYM53C500.c file is to the sym53c500_cs driver what
the qlogicfas.c file is to the qlogic_cs driver, that is, core functions
that could support multiple types of host adapters. The logic to
handle the different types of adapters isn't there, and I don't know
that it ever will be (else, it's probable that someone would have
written the Linux driver long before now). However, after baring my
ignorance to the world and saying I was unaware of non-PCMCIA
implementations, I found a FreeBSD driver for the NCR 53c500. Never
say "never," I guess... Your opinion counts for much, but you're the
only person I've heard from. Is there a consensus I should forget
about the non-PCMCIA cases?
> - please don't use host.h or scsi.h from drivers/scsi/. The defintions
> not present in include/scsi/ are deprecated and shall not be used (the
> most prominent example in your driver are the Scsi_<Foo> typedefs that
> have been replaced by struct scsi_foo
I caught that in the coding style guidelines (and in the mentioned
include files), and will fix for the next submission.
> - the driver doesn't even try to deal with multiple HBAs
Guilty as charged. Functionally, there's nothing in the driver I
submitted that wasn't in the original. Suggestions welcome... Which
of the existing PCMCIA SCSI drivers do a proper job of handling
multiple host adapters in your opinion? I'll try to adapt that code to
fit this driver. If I have to "roll my own" from scratch, I'm probably
in over my head.
> - your detection logic could be streamlined a little, e.g. the request/release
> resource mess
I'll see what I can do.
Although I touched on it above, by way of apology/explanation, the goal
for the initial port was to replicate the functionality I already had in
older kernel versions. It appears I faithfully replicated the
deficiencies of the old driver as well :-). Again, thank you for the
feedback.
Anyone else have input before I act on the recommendations I've been
given? Unless I hear otherwise, I'll start work on the code
consolidation and removal of dependencies on deprecated include files.
The detection logic and handling multiple HBAs will take a bit more
effort...
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (new)
2004-04-16 14:17 ` Bob Tracy
@ 2004-04-17 9:45 ` Christoph Hellwig
2004-04-20 16:11 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (second round) Bob Tracy
2004-04-22 19:38 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (new) Bill Davidsen
1 sibling, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2004-04-17 9:45 UTC (permalink / raw)
To: Bob Tracy; +Cc: linux-kernel, linux-scsi
On Fri, Apr 16, 2004 at 09:17:20AM -0500, Bob Tracy wrote:
> Given that the driver currently supports only PCMCIA implementations,
> I agree. My thinking was if someone comes up with a host adapter that
> isn't PCMCIA, the SYM53C500.c file is to the sym53c500_cs driver what
> the qlogicfas.c file is to the qlogic_cs driver, that is, core functions
> that could support multiple types of host adapters. The logic to
> handle the different types of adapters isn't there, and I don't know
> that it ever will be (else, it's probable that someone would have
> written the Linux driver long before now). However, after baring my
> ignorance to the world and saying I was unaware of non-PCMCIA
> implementations, I found a FreeBSD driver for the NCR 53c500. Never
> say "never," I guess... Your opinion counts for much, but you're the
> only person I've heard from. Is there a consensus I should forget
> about the non-PCMCIA cases?
I'd suggest to keep it as simple as you can for the time beeing. If we
ever find a user with a ISA or whatever variant he can split it out. And
such a split would work a little different from what you did now.
> > - the driver doesn't even try to deal with multiple HBAs
>
> Guilty as charged. Functionally, there's nothing in the driver I
> submitted that wasn't in the original. Suggestions welcome... Which
> of the existing PCMCIA SCSI drivers do a proper job of handling
> multiple host adapters in your opinion? I'll try to adapt that code to
> fit this driver. If I have to "roll my own" from scratch, I'm probably
> in over my head.
It looks like nsp_cs at least tries to :-)
> > - your detection logic could be streamlined a little, e.g. the request/release
> > resource mess
>
> I'll see what I can do.
>
> Although I touched on it above, by way of apology/explanation, the goal
> for the initial port was to replicate the functionality I already had in
> older kernel versions. It appears I faithfully replicated the
> deficiencies of the old driver as well :-). Again, thank you for the
> feedback.
Hey, you don't need to apologize. Anyt 2.6 driver is better than none and
your looks quite okay from the functional standpoint. We just need to have
a little higher bars for new drivers as we already have lots of maintaince
overhead for old and sloppy written drivers.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH] sym53c500_cs PCMCIA SCSI driver (second round)
2004-04-17 9:45 ` Christoph Hellwig
@ 2004-04-20 16:11 ` Bob Tracy
2004-04-20 16:24 ` Christoph Hellwig
0 siblings, 1 reply; 15+ messages in thread
From: Bob Tracy @ 2004-04-20 16:11 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-kernel, linux-scsi
[-- Attachment #1: Type: text/plain, Size: 1627 bytes --]
(Summary -- new version of sym53c500_cs driver attached.)
Christoph Hellwig wrote:
> - the driver doesn't even try to deal with multiple HBAs
>
> It looks like nsp_cs at least tries to :-)
I'm not sure that nsp_cs does much more than assign a value to the
"unique_id" member of the scsi_host structure (base port), and I
probably missed what I was looking for, but I can certainly do as
much :-). Is there any way to test whether the driver code can
support multiple HBAs short of having more than one?
> Hey, you don't need to apologize. Anyt 2.6 driver is better than none and
> your looks quite okay from the functional standpoint. We just need to have
> a little higher bars for new drivers as we already have lots of maintaince
> overhead for old and sloppy written drivers.
Thank you for the explanation: clearly I don't want to increase the
maintenance burden on myself and/or anyone else.
Attached is the second attempt at a 2.6 PCMCIA driver for SCSI cards
based on the Symbios 53c500 controller. The patch set was tested with
(and applies cleanly to) 2.6.5. As requested, the driver source has
been consolidated into a single file. All remnants of code that might
have supported non-PCMCIA host adapters have been ripped out, which
allowed for the requested streamlining of the detection logic: Card
Services is doing most of the heavy lifting in that area anyway :-).
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
[-- Attachment #2: patch05_sym53c500 --]
[-- Type: text/plain, Size: 34745 bytes --]
--- linux/drivers/scsi/pcmcia/Kconfig.orig Fri Apr 16 16:00:41 2004
+++ linux/drivers/scsi/pcmcia/Kconfig Fri Apr 9 20:17:36 2004
@@ -69,4 +69,14 @@
To compile this driver as a module, choose M here: the
module will be called qlogic_cs.
+config PCMCIA_SYM53C500
+ tristate "Symbios 53c500 PCMCIA support"
+ depends on m
+ help
+ Say Y here if you have a New Media Bus Toaster or other PCMCIA
+ SCSI adapter based on the Symbios 53c500 controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sym53c500_cs.
+
endmenu
--- linux/drivers/scsi/pcmcia/Makefile.orig Fri Apr 16 16:00:41 2004
+++ linux/drivers/scsi/pcmcia/Makefile Fri Apr 16 20:23:44 2004
@@ -6,6 +6,7 @@
obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o
obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o
obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o
+obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o
aha152x_cs-objs := aha152x_stub.o aha152x_core.o
fdomain_cs-objs := fdomain_stub.o fdomain_core.o
--- linux/drivers/scsi/pcmcia/sym53c500_cs.c.orig Fri Apr 16 16:05:19 2004
+++ linux/drivers/scsi/pcmcia/sym53c500_cs.c Tue Apr 20 10:54:55 2004
@@ -0,0 +1,1150 @@
+/*
+* sym53c500_cs.c Bob Tracy (rct@frus.com)
+*
+* A rewrite of the pcmcia-cs add-on driver for newer (circa 1997)
+* New Media Bus Toaster PCMCIA SCSI cards using the Symbios Logic
+* 53c500 controller. For inclusion in the 2.6 and later kernel
+* source trees. The pcmcia-cs add-on version of this driver is
+* not supported beyond 2.4.
+*
+* The rewrite was long overdue, and the 2.6 kernel forced the issue.
+* All the USE_BIOS code has been ripped out. It was never used, and
+* could not have worked anyway. USE_DMA has gone bye-bye too.
+*
+* The original pcmcia-cs add-on driver consisted of three files
+* with history/copyright information as follows:
+*
+* SYM53C500.h
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at).
+* Adapted from NCR53c406a.h which is Copyrighted (C) 1994
+* Normunds Saumanis (normunds@rx.tech.swh.lv)
+*
+* SYM53C500.c
+* Bob Tracy (rct@frus.com)
+* Original driver by Tom Corner (tcorner@via.at) was adapted
+* from NCR53c406a.c which is Copyrighted (C) 1994, 1995, 1996
+* Normunds Saumanis (normunds@fi.ibm.com)
+*
+* sym53c500.c
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at) was adapted from a
+* driver for the Qlogic SCSI card written by
+* David Hinds (dhinds@allegro.stanford.edu).
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*/
+
+#define SYM53C500_DEBUG 0
+#define VERBOSE_SYM53C500_DEBUG 0
+
+/* Set this to 0 if you encounter kernel lockups while transferring
+ * data in PIO mode */
+#define USE_FAST_PIO 1
+
+/* =============== End of user configurable parameters ============== */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+/* A useful macro... */
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* Function prototypes. */
+const char *SYM53C500_info(struct Scsi_Host *);
+int SYM53C500_queue(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
+int SYM53C500_abort(struct scsi_cmnd *);
+int SYM53C500_device_reset(struct scsi_cmnd *);
+int SYM53C500_bus_reset(struct scsi_cmnd *);
+int SYM53C500_host_reset(struct scsi_cmnd *);
+int SYM53C500_biosparm(struct scsi_device *, struct block_device *, sector_t, int *);
+int SYM53C500_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+
+/* Static function prototypes */
+static dev_link_t *SYM53C500_attach(void);
+static void SYM53C500_config(dev_link_t *);
+static void SYM53C500_detach(dev_link_t *);
+static int SYM53C500_event(event_t event, int priority, event_callback_args_t *args);
+static void SYM53C500_intr(int, void *, struct pt_regs *);
+static void SYM53C500_release(dev_link_t *link);
+static void chip_init(void);
+static void calc_port_addr(void);
+static irqreturn_t do_SYM53C500_intr(int, void *, struct pt_regs *);
+
+/* ================================================================== */
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"sym53c500_cs.c 0.7 2004/04/01 14:15:58 (Bob Tracy)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ================================================================== */
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static unsigned int irq_mask = 0xdeb8; /* 3, 6, 7, 9-12, 14, 15 */
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+/* ================================================================== */
+
+#define SYNC_MODE 0 /* Synchronous transfer mode */
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#define C3_IMG 0x20 /* CDB */
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xa4 /*? changed from b6= AA PI SIE POL */
+#define C7_IMG 0x80 /* added for SYM53C500 t. corner */
+
+#define REG0 (outb(C4_IMG, CONFIG4)) /* select register set 0 */
+#define REG1 outb(C7_IMG, CONFIG7); outb(C5_IMG, CONFIG5) /* select register set 1 */
+
+#if SYM53C500_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_SYM53C500_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(count) \
+ outb(count & 0xff, TC_LSB); \
+ outb((count >> 8) & 0xff, TC_MSB); \
+ outb((count >> 16) & 0xff, TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/* ================================================================== */
+
+/*
+* the following will set the monitor border color
+* (useful to find where things crash or get stuck)
+*
+* 1 = blue
+* 2 = green
+* 3 = cyan
+* 4 = red
+* 5 = magenta
+* 6 = yellow
+* 7 = white
+*/
+
+#if SYM53C500_DEBUG
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+/* ================================================================== */
+
+typedef struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+ unsigned short manf_id;
+} scsi_info_t;
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* ================================================================== */
+
+/*
+* Global (within this module) variables.
+*/
+
+/*
+* scsi_host_template initializer
+*
+* Include file defining structure implies proc_info is optional.
+* Trouble is, directory for host adapter won't get created unless
+* there's a proc_info routine, i.e., nothing will appear in /proc.
+* This is different from the 2.4 implementation.
+*/
+static struct scsi_host_template sym53c500_driver_template = {
+ .module = THIS_MODULE,
+ .name = "SYM53C500",
+ .info = SYM53C500_info,
+ .queuecommand = SYM53C500_queue,
+ .eh_abort_handler = SYM53C500_abort,
+ .eh_device_reset_handler = SYM53C500_device_reset,
+ .eh_bus_reset_handler = SYM53C500_bus_reset,
+ .eh_host_reset_handler = SYM53C500_host_reset,
+ .bios_param = SYM53C500_biosparm,
+ .proc_info = SYM53C500_proc_info,
+ .proc_name = "SYM53C500",
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = 32,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+static int port_base = 0;
+static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized'*/
+
+static int fast_pio = USE_FAST_PIO;
+
+static struct scsi_cmnd *current_SC;
+static char info_msg[256];
+
+/*
+* possible i/o port addresses: provided for informational purposes only,
+* because if Card Services can't find the card, the card doesn't exist.
+*
+* 0x130, 0x230, 0x280, 0x290, 0x320, 0x330, 0x340, 0x350
+*/
+
+/* Control Register Set 0 */
+static int TC_LSB; /* transfer counter lsb */
+static int TC_MSB; /* transfer counter msb */
+static int SCSI_FIFO; /* scsi fifo register */
+static int CMD_REG; /* command register */
+static int STAT_REG; /* status register */
+static int DEST_ID; /* selection/reselection bus id */
+static int INT_REG; /* interrupt status register */
+static int SRTIMOUT; /* select/reselect timeout reg */
+static int SEQ_REG; /* sequence step register */
+static int SYNCPRD; /* synchronous transfer period */
+static int FIFO_FLAGS; /* indicates # of bytes in fifo */
+static int SYNCOFF; /* synchronous offset register */
+static int CONFIG1; /* configuration register */
+static int CLKCONV; /* clock conversion reg */
+/*static int TESTREG;*/ /* test mode register */
+static int CONFIG2; /* Configuration 2 Register */
+static int CONFIG3; /* Configuration 3 Register */
+static int CONFIG4; /* Configuration 4 Register */
+static int TC_HIGH; /* Transfer Counter High */
+/*static int FIFO_BOTTOM;*/ /* Reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/*static int JUMPER_SENSE;*/ /* Jumper sense port reg (r/w) */
+/*static int SRAM_PTR;*/ /* SRAM address pointer reg (r/w) */
+/*static int SRAM_DATA;*/ /* SRAM data register (r/w) */
+static int PIO_FIFO; /* PIO FIFO registers (r/w) */
+/*static int PIO_FIFO1;*/ /* */
+/*static int PIO_FIFO2;*/ /* */
+/*static int PIO_FIFO3;*/ /* */
+static int PIO_STATUS; /* PIO status (r/w) */
+/*static int ATA_CMD;*/ /* ATA command/status reg (r/w) */
+/*static int ATA_ERR;*/ /* ATA features/error register (r/w)*/
+static int PIO_FLAG; /* PIO flag interrupt enable (r/w) */
+static int CONFIG5; /* Configuration 5 register (r/w) */
+/*static int SIGNATURE;*/ /* Signature Register (r) */
+/*static int CONFIG6;*/ /* Configuration 6 register (r) */
+static int CONFIG7;
+
+static dev_link_t *dev_list = NULL;
+static dev_info_t dev_info = "sym53c500_cs";
+
+/* ================================================================== */
+
+static dev_link_t *
+SYM53C500_attach(void)
+{
+ scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int i, ret;
+
+ DEBUG(0, "SYM53C500_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link;
+ link->priv = info;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.event_handler = &SYM53C500_event;
+ client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ SYM53C500_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* SYM53C500_attach */
+
+static void
+SYM53C500_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link)
+ break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits. */
+ *linkp = link->next;
+ kfree(link->priv);
+ link->priv = NULL;
+} /* SYM53C500_detach */
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void
+SYM53C500_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ unsigned short tuple_data[32];
+ struct Scsi_Host *host;
+ struct scsi_host_template *tpnt = &sym53c500_driver_template;
+
+ DEBUG(0, "SYM53C500_config(0x%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *)tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+ (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+ info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+
+ if (link->io.BasePort1 != 0) {
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /*
+ * That's the trouble with copying liberally from another driver.
+ * Some things probably aren't relevant, and I suspect this entire
+ * section dealing with manufacturer IDs can be scrapped. --rct
+ */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ /* set ATAcmd */
+ outb(0xb4, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+
+ /*
+ * Note fast_pio is global and set at compile time.
+ */
+ port_base = link->io.BasePort1;
+ irq_level = link->irq.AssignedIRQ;
+
+ DEB(printk("SYM53C500: port_base=0x%X, irq=%d, fast_pio=%d\n",
+ port_base, irq_level, fast_pio);)
+
+ calc_port_addr();
+ chip_init();
+
+ host = scsi_host_alloc(tpnt, 0);
+ if (!host) {
+ printk("SYM53C500: Unable to register host, giving up.\n");
+ goto err_release;
+ }
+
+ if (irq_level > 0) {
+ if (request_irq(irq_level, do_SYM53C500_intr, 0, "SYM53C500", host)) {
+ printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
+ goto err_free_scsi;
+ }
+ tpnt->can_queue = 1;
+ DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
+ } else if (irq_level == 0) {
+ tpnt->can_queue = 0;
+ DEB(printk("SYM53C500: No interrupts detected\n"));
+ goto err_free_scsi;
+ } else {
+ DEB(printk("SYM53C500: Shouldn't get here!\n"));
+ goto err_free_scsi;
+ }
+
+ host->unique_id = port_base;
+ host->irq = irq_level;
+ host->io_port = port_base;
+ host->n_io_port = 0x10;
+ host->dma_channel = -1;
+
+ sprintf(info_msg, "SYM53C500 at 0x%x, IRQ %d, %s PIO mode.",
+ port_base, irq_level, fast_pio ? "fast" : "slow");
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+ scsi_add_host(host, NULL); /* XXX handle failure */
+ scsi_scan_host(host);
+
+ goto out; /* SUCCESS */
+
+err_free_scsi:
+ scsi_host_put(host);
+err_release:
+ release_region(port_base, 0x10);
+ printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
+
+out:
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ SYM53C500_release(link);
+ return;
+} /* SYM53C500_config */
+
+static void
+SYM53C500_release(dev_link_t *link)
+{
+ scsi_info_t *info = link->priv;
+ struct Scsi_Host *shost = info->host;
+
+ DEBUG(0, "SYM53C500_release(0x%p)\n", link);
+
+ /*
+ * Interrupts getting hosed on card removal. Try
+ * the following code, mostly from qlogicfas.c.
+ */
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+
+ scsi_remove_host(shost);
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+ scsi_host_put(shost);
+} /* SYM53C500_release */
+
+static int
+SYM53C500_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ scsi_info_t *info = link->priv;
+
+ DEBUG(1, "SYM53C500_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ SYM53C500_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle, &link->conf);
+ /* See earlier comment about manufacturer IDs. */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ outb(0x80, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+ /* Fugly! */
+ SYM53C500_bus_reset(NULL);
+ }
+ break;
+ }
+ return 0;
+} /* SYM53C500_event */
+
+/*
+* SYM53C500_proc_info is basically a stub function for now. It
+* wouldn't exist except for the fact there were /proc entries for
+* this driver under 2.4 and earlier kernels, and the author (sole
+* user?) expects to see them.
+*
+* buffer: I/O buffer
+* start: if inout == FALSE, pointer into buffer where user
+* read should start
+* offset: current offset
+* length: length of buffer
+* hostno: Scsi_Host host_no
+* inout: 1 --> user is writing; 0 --> user is reading
+*/
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { \
+ if (length > (pos - buffer)) \
+ pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
+ } while (0)
+
+int
+SYM53C500_proc_info(struct Scsi_Host *shost, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ char *pos = buffer;
+ int thislength;
+
+ if (inout)
+ return(-ENOSYS); /* look, but don't touch */
+
+ /*
+ * Cheesy, but it's what we had before.
+ */
+ SPRINTF("%s\n", info_msg);
+
+ thislength = pos - (buffer + offset);
+
+ if (thislength < 0) {
+ *start = 0;
+ return 0;
+ }
+
+ thislength = MIN(thislength, length);
+ *start = buffer + offset;
+
+ return thislength;
+}
+
+static __inline__ int
+SYM53C500_pio_read(unsigned char *request, unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+
+ REG1;
+ while (reqlen) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch (i & 0x1e) {
+ default:
+ case 0x10: /* fifo empty */
+ len = 0;
+ break;
+ case 0x0:
+ len = 1;
+ break;
+ case 0x8: /* fifo 1/3 full */
+ len = 42;
+ break;
+ case 0xc: /* fifo 2/3 full */
+ len = 84;
+ break;
+ case 0xe: /* fifo full */
+ len = 128;
+ break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+ return 0;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ insl(PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ *request++ = inb(PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static __inline__ int
+SYM53C500_pio_write(unsigned char *request, unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+
+ REG1;
+ while (reqlen && !(i & 0x40)) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch (i & 0x1e) {
+ case 0x10:
+ len = 128;
+ break;
+ case 0x0:
+ len = 84;
+ break;
+ case 0x8:
+ len = 42;
+ break;
+ case 0xc:
+ len = 1;
+ break;
+ default:
+ case 0xe:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ outsl(PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ outb(*request++, PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+const char*
+SYM53C500_info(struct Scsi_Host *SChost)
+{
+ DEB(printk("SYM53C500_info called\n"));
+ return (info_msg);
+}
+
+int
+SYM53C500_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ int i;
+
+ VDEB(printk("SYM53C500_queue called\n"));
+
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
+ SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->request_bufflen));
+
+ VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+
+ current_SC = SCpnt;
+ current_SC->scsi_done = done;
+ current_SC->SCp.phase = command_ph;
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+
+ /* We are locked here already by the mid layer */
+ REG0;
+ outb(SCpnt->device->id, DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, CMD_REG); /* reset the fifos */
+
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ outb(SCpnt->cmnd[i], SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, CMD_REG);
+
+ rtrc(1);
+ return 0;
+}
+
+int
+SYM53C500_abort(struct scsi_cmnd *SCpnt)
+{
+ DEB(printk("SYM53C500_abort called\n"));
+ return FAILED; /* Don't know how to abort */
+}
+
+int
+SYM53C500_host_reset(struct scsi_cmnd *SCpnt)
+{
+ DEB(printk("SYM53C500_host_reset called\n"));
+ outb(C4_IMG, CONFIG4); /* Select reg set 0 */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG); /* required after reset */
+ outb(SCSI_RESET, CMD_REG);
+ chip_init();
+
+ rtrc(2);
+ return SUCCESS;
+}
+
+int
+SYM53C500_device_reset(struct scsi_cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+int
+SYM53C500_bus_reset(struct scsi_cmnd *SCpnt)
+{
+ return FAILED;
+}
+
+int
+SYM53C500_biosparm(struct scsi_device *disk,
+ struct block_device *dev,
+ sector_t capacity, int *info_array)
+{
+ int size;
+
+ DEB(printk("SYM53C500_biosparm called\n"));
+
+ size = capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size >> 11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255 * 63);
+ }
+ return 0;
+}
+
+static irqreturn_t
+do_SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+ SYM53C500_intr(irq, dev_id, regs);
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ DEB(unsigned char fifo_size;)
+ DEB(unsigned char seq_reg;)
+ unsigned char status, int_reg;
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+
+ VDEB(printk("SYM53C500_intr called\n"));
+
+ REG1;
+ pio_status = inb(PIO_STATUS);
+ REG0;
+ status = inb(STAT_REG);
+ DEB(seq_reg = inb(SEQ_REG));
+ int_reg = inb(INT_REG);
+ DEB(fifo_size = inb(FIFO_FLAGS) & 0x1f);
+
+#if SYM53C500_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x",
+ status, seq_reg, int_reg, fifo_size);
+ printk(", pio=%02x\n", pio_status);
+#endif /* SYM53C500_DEBUG */
+
+ if (int_reg & 0x80) { /* SCSI reset intr */
+ rtrc(3);
+ DEB(printk("SYM53C500: reset intr received\n"));
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_RESET << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (pio_status & 0x80) {
+ printk("SYM53C500: Warning: PIO error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (status & 0x20) { /* Parity error */
+ printk("SYM53C500: Warning: parity error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_PARITY << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (status & 0x40) { /* Gross error */
+ printk("SYM53C500: Warning: gross error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if (int_reg & 0x20) { /* Disconnect */
+ DEB(printk("SYM53C500: disconnect intr received\n"));
+ if (current_SC->SCp.phase != message_in) { /* Unexpected disconnect */
+ current_SC->result = DID_NO_CONNECT << 16;
+ } else { /* Command complete, return status and message */
+ current_SC->result = (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+
+ rtrc(0);
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ switch (status & 0x07) { /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ rtrc(5);
+ current_SC->SCp.phase = data_out;
+ VDEB(printk("SYM53C500: Data-Out phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_write(current_SC->request_buffer, current_SC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_write(page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ rtrc(6);
+ current_SC->SCp.phase = data_in;
+ VDEB(printk("SYM53C500: Data-In phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_read(current_SC->request_buffer, current_SC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_read(page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ current_SC->SCp.phase = command_ph;
+ printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ rtrc(7);
+ current_SC->SCp.phase = status_ph;
+ VDEB(printk("SYM53C500: Status phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ outb(INIT_CMD_COMPLETE, CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("SYM53C500: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("SYM53C500: Message-Out phase\n"));
+ current_SC->SCp.phase = message_out;
+ outb(SET_ATN, CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ rtrc(4);
+ VDEB(printk("SYM53C500: Message-In phase\n"));
+ current_SC->SCp.phase = message_in;
+
+ current_SC->SCp.Status = inb(SCSI_FIFO);
+ current_SC->SCp.Message = inb(SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n", current_SC->SCp.Status, current_SC->SCp.Message));
+
+ if (current_SC->SCp.Message == SAVE_POINTERS || current_SC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+ }
+}
+
+static void
+chip_init(void)
+{
+ REG1;
+ outb(0x01, PIO_STATUS);
+ outb(0x00, PIO_FLAG);
+
+ outb(C4_IMG, CONFIG4); /* REG0; */
+ outb(C3_IMG, CONFIG3);
+ outb(C2_IMG, CONFIG2);
+ outb(C1_IMG, CONFIG1);
+
+ outb(0x05, CLKCONV); /* clock conversion factor */
+ outb(0x9C, SRTIMOUT); /* Selection timeout */
+ outb(0x05, SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, SYNCOFF); /* synchronous mode */
+}
+
+void
+calc_port_addr(void)
+{
+ /* Control Register Set 0 */
+ TC_LSB = (port_base + 0x00);
+ TC_MSB = (port_base + 0x01);
+ SCSI_FIFO = (port_base + 0x02);
+ CMD_REG = (port_base + 0x03);
+ STAT_REG = (port_base + 0x04);
+ DEST_ID = (port_base + 0x04);
+ INT_REG = (port_base + 0x05);
+ SRTIMOUT = (port_base + 0x05);
+ SEQ_REG = (port_base + 0x06);
+ SYNCPRD = (port_base + 0x06);
+ FIFO_FLAGS = (port_base + 0x07);
+ SYNCOFF = (port_base + 0x07);
+ CONFIG1 = (port_base + 0x08);
+ CLKCONV = (port_base + 0x09);
+ /* TESTREG = (port_base + 0x0A); */
+ CONFIG2 = (port_base + 0x0B);
+ CONFIG3 = (port_base + 0x0C);
+ CONFIG4 = (port_base + 0x0D);
+ TC_HIGH = (port_base + 0x0E);
+ /* FIFO_BOTTOM = (port_base + 0x0F); */
+
+ /* Control Register Set 1 */
+ /* JUMPER_SENSE = (port_base + 0x00); */
+ /* SRAM_PTR = (port_base + 0x01); */
+ /* SRAM_DATA = (port_base + 0x02); */
+ PIO_FIFO = (port_base + 0x04);
+ /* PIO_FIFO1 = (port_base + 0x05); */
+ /* PIO_FIFO2 = (port_base + 0x06); */
+ /* PIO_FIFO3 = (port_base + 0x07); */
+ PIO_STATUS = (port_base + 0x08);
+ /* ATA_CMD = (port_base + 0x09); */
+ /* ATA_ERR = (port_base + 0x0A); */
+ PIO_FLAG = (port_base + 0x0B);
+ CONFIG5 = (port_base + 0x09);
+ /* SIGNATURE = (port_base + 0x0E); */
+ /* CONFIG6 = (port_base +0x0F); */
+ CONFIG7 = (port_base + 0x0d);
+}
+
+MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
+MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver");
+MODULE_LICENSE("GPL");
+
+static struct pcmcia_driver sym53c500_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "sym53c500_cs",
+ },
+ .attach = SYM53C500_attach,
+ .detach = SYM53C500_detach,
+};
+
+static int __init
+init_sym53c500_cs(void)
+{
+ return pcmcia_register_driver(&sym53c500_cs_driver);
+}
+
+static void __exit
+exit_sym53c500_cs(void)
+{
+ pcmcia_unregister_driver(&sym53c500_cs_driver);
+
+ /* XXX: this really needs to move into generic code... */
+ while (dev_list != NULL)
+ SYM53C500_detach(dev_list);
+}
+
+module_init(init_sym53c500_cs);
+module_exit(exit_sym53c500_cs);
--- linux/Documentation/scsi/sym53c500_cs.txt.orig Fri Apr 16 16:06:19 2004
+++ linux/Documentation/scsi/sym53c500_cs.txt Fri Apr 16 15:05:53 2004
@@ -0,0 +1,17 @@
+The sym53c500_cs driver originated as an add-on to David Hinds' pcmcia-cs
+package, and was written by Tom Corner (tcorner@via.at). This rewrite
+addresses the following concerns:
+
+ (1) extensive kernel changes between 2.4 and 2.6.
+ (2) deprecated PCMCIA support outside the kernel.
+
+The Symbios Logic 53c500 chip was used in the "new" (circa 1997) version
+of the New Media Bus Toaster PCMCIA SCSI controller. Presumably there are
+other products using this chip, but I've never laid eyes (much less hands)
+on one.
+
+Through the years, there have been a number of downloads of the pcmcia-cs
+version of this driver, and I guess it worked for those users. It worked
+for Tom Corner, and it works for me. Your mileage will probably vary.
+
+--Bob Tracy (rct@frus.com)
--- linux/Documentation/scsi/00-INDEX.orig Fri Apr 16 16:07:11 2004
+++ linux/Documentation/scsi/00-INDEX Fri Apr 9 20:17:36 2004
@@ -62,6 +62,8 @@
- info on API between SCSI layer and low level drivers
st.txt
- info on scsi tape driver
+sym53c500_cs.txt
+ - info on PCMCIA driver for Symbios Logic 53c500 based adapters
sym53c8xx_2.txt
- info on second generation driver for sym53c8xx based adapters
tmscsim.txt
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (second round)
2004-04-20 16:11 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (second round) Bob Tracy
@ 2004-04-20 16:24 ` Christoph Hellwig
2004-04-25 3:01 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?) Bob Tracy
0 siblings, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2004-04-20 16:24 UTC (permalink / raw)
To: Bob Tracy; +Cc: Christoph Hellwig, linux-kernel, linux-scsi
On Tue, Apr 20, 2004 at 11:11:11AM -0500, Bob Tracy wrote:
> I'm not sure that nsp_cs does much more than assign a value to the
> "unique_id" member of the scsi_host structure (base port), and I
> probably missed what I was looking for, but I can certainly do as
> much :-). Is there any way to test whether the driver code can
> support multiple HBAs short of having more than one?
There's not real test. But a good indication is that you have all
almost no global variables (except some debug switches) and all
I/O address / state / etc in per-host instance data.
> +#include <linux/module.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/ioport.h>
> +#include <linux/proc_fs.h>
> +#include <linux/stat.h>
> +#include <asm/io.h>
> +#include <asm/dma.h>
> +#include <asm/bitops.h>
> +#include <asm/irq.h>
> +#include <linux/major.h>
> +#include <linux/blkdev.h>
> +#include <linux/spinlock.h>
Please try to include all <asm/*> headers after <linux/*>.
You should't need major.h and proc_fs.h at least.
> +#include <pcmcia/version.h>
A proper pcmcia driver shouldn't need this one.
> +/* A useful macro... */
> +#define MIN(a, b) ((a) > (b) ? (b) : (a))
The kernel already has min(), and it even does type-checking.
> +#ifndef NULL
> +#define NULL 0
> +#endif
Shouldn't be needed.
> +/* Function prototypes. */
> +const char *SYM53C500_info(struct Scsi_Host *);
> +int SYM53C500_queue(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
> +int SYM53C500_abort(struct scsi_cmnd *);
> +int SYM53C500_device_reset(struct scsi_cmnd *);
> +int SYM53C500_bus_reset(struct scsi_cmnd *);
> +int SYM53C500_host_reset(struct scsi_cmnd *);
> +int SYM53C500_biosparm(struct scsi_device *, struct block_device *, sector_t, int *);
> +int SYM53C500_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
Everything in a scsi driver should be static. Also try to avoid prototypes
for static functions whenever possible by ordering functions by their calling
chain.
> +#ifdef PCMCIA_DEBUG
> +static int pc_debug = PCMCIA_DEBUG;
> +MODULE_PARM(pc_debug, "i");
In 2.6 please use the type-chcking whizz-bang module_param instead of
MODULE_PARAM.
> +/*
> +* the following will set the monitor border color
> +* (useful to find where things crash or get stuck)
> +*
> +* 1 = blue
> +* 2 = green
> +* 3 = cyan
> +* 4 = red
> +* 5 = magenta
> +* 6 = yellow
> +* 7 = white
> +*/
> +
> +#if SYM53C500_DEBUG
> +#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
> +#else
> +#define rtrc(i) {}
> +#endif
Do you actually use this debug code? It's rather pc-specific..
> +typedef struct scsi_info_t {
> + dev_link_t link;
> + dev_node_t node;
> + struct Scsi_Host *host;
> + unsigned short manf_id;
> +} scsi_info_t;
Try to avoid typedefs if possible according to the Linux Kernel coding
style. And yes, the pcmcia code isn't how it should be done, I know
you only copied it ;-)
> +static int port_base = 0;
> +static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized'*/
> +
> +static int fast_pio = USE_FAST_PIO;
> +
> +static struct scsi_cmnd *current_SC;
> +static char info_msg[256];
This is what I mean. To support more than one card these variables and
some more would have to be in a per-host struct.
> + scsi_add_host(host, NULL); /* XXX handle failure */
So handle the failure case :)
> + struct Scsi_Host *shost = info->host;
> +
> + DEBUG(0, "SYM53C500_release(0x%p)\n", link);
> +
> + /*
> + * Interrupts getting hosed on card removal. Try
> + * the following code, mostly from qlogicfas.c.
> + */
> + if (shost->irq)
> + free_irq(shost->irq, shost);
> + if (shost->dma_channel != 0xff)
> + free_dma(shost->dma_channel);
> + if (shost->io_port && shost->n_io_port)
> + release_region(shost->io_port, shost->n_io_port);
> +
> + scsi_remove_host(shost);
You need to call scsi_remove_host as the first thing in this function.
> +/*
> +* SYM53C500_proc_info is basically a stub function for now. It
> +* wouldn't exist except for the fact there were /proc entries for
> +* this driver under 2.4 and earlier kernels, and the author (sole
> +* user?) expects to see them.
Well, please just kill it. ->proc_info is deprecated in 2.6.
> +int
> +SYM53C500_device_reset(struct scsi_cmnd *SCpnt)
> +{
> + return FAILED;
> +}
> +
> +int
> +SYM53C500_bus_reset(struct scsi_cmnd *SCpnt)
> +{
> + return FAILED;
> +}
You don't need to implement these. If an EH method isn't implemented
FAILED is the default return value.
> +static irqreturn_t
> +do_SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
> +{
> + unsigned long flags;
> + struct Scsi_Host *dev = dev_id;
> +
> + spin_lock_irqsave(dev->host_lock, flags);
> + SYM53C500_intr(irq, dev_id, regs);
> + spin_unlock_irqrestore(dev->host_lock, flags);
> + return IRQ_HANDLED;
> +}
You should probably merged SYM53C500_intr with this one.
> + if (pio_status & 0x80) {
> + printk("SYM53C500: Warning: PIO error!\n");
> + current_SC->SCp.phase = idle;
> + current_SC->result = DID_ERROR << 16;
> + current_SC->scsi_done(current_SC);
> + return;
> + }
> +
> + if (status & 0x20) { /* Parity error */
> + printk("SYM53C500: Warning: parity error!\n");
> + current_SC->SCp.phase = idle;
> + current_SC->result = DID_PARITY << 16;
> + current_SC->scsi_done(current_SC);
> + return;
> + }
This screams for a goto error and the error handler in one place :)
> + /* Control Register Set 0 */
> + TC_LSB = (port_base + 0x00);
> + TC_MSB = (port_base + 0x01);
> + SCSI_FIFO = (port_base + 0x02);
These variables are _really_ strange. In a normal Linux driver
the names that are variables here (TC_LSB, etc..) would be defines
to the numberical values you add to port_base and when you're actually
using them you'd use shost->io_port + TC_LSB or whatever as the actual
address.
> +static void __exit
> +exit_sym53c500_cs(void)
> +{
> + pcmcia_unregister_driver(&sym53c500_cs_driver);
> +
> + /* XXX: this really needs to move into generic code... */
> + while (dev_list != NULL)
> + SYM53C500_detach(dev_list);
It's actually superflous for scsi drivers these days.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (new)
2004-04-16 14:17 ` Bob Tracy
2004-04-17 9:45 ` Christoph Hellwig
@ 2004-04-22 19:38 ` Bill Davidsen
2004-04-22 20:48 ` Bob Tracy
1 sibling, 1 reply; 15+ messages in thread
From: Bill Davidsen @ 2004-04-22 19:38 UTC (permalink / raw)
To: Bob Tracy; +Cc: Christoph Hellwig, linux-kernel, linux-scsi
Bob Tracy wrote:
> Christoph Hellwig wrote:
>
>>I've given it a short spin and here's a bunch of comments:
>
>
> Thank you for taking the time and trouble to review it.
>
>
>> - the split into three source files is supserflous, one file should do it
>
>
> Given that the driver currently supports only PCMCIA implementations,
> I agree. My thinking was if someone comes up with a host adapter that
> isn't PCMCIA, the SYM53C500.c file is to the sym53c500_cs driver what
> the qlogicfas.c file is to the qlogic_cs driver, that is, core functions
> that could support multiple types of host adapters. The logic to
> handle the different types of adapters isn't there, and I don't know
> that it ever will be (else, it's probable that someone would have
> written the Linux driver long before now). However, after baring my
> ignorance to the world and saying I was unaware of non-PCMCIA
> implementations, I found a FreeBSD driver for the NCR 53c500. Never
> say "never," I guess... Your opinion counts for much, but you're the
> only person I've heard from. Is there a consensus I should forget
> about the non-PCMCIA cases?
>
>
>> - please don't use host.h or scsi.h from drivers/scsi/. The defintions
>> not present in include/scsi/ are deprecated and shall not be used (the
>> most prominent example in your driver are the Scsi_<Foo> typedefs that
>> have been replaced by struct scsi_foo
>
>
> I caught that in the coding style guidelines (and in the mentioned
> include files), and will fix for the next submission.
>
>
>> - the driver doesn't even try to deal with multiple HBAs
>
>
> Guilty as charged. Functionally, there's nothing in the driver I
> submitted that wasn't in the original. Suggestions welcome... Which
> of the existing PCMCIA SCSI drivers do a proper job of handling
> multiple host adapters in your opinion? I'll try to adapt that code to
> fit this driver. If I have to "roll my own" from scratch, I'm probably
> in over my head.
>
>
>> - your detection logic could be streamlined a little, e.g. the request/release
>> resource mess
>
>
> I'll see what I can do.
>
> Although I touched on it above, by way of apology/explanation, the goal
> for the initial port was to replicate the functionality I already had in
> older kernel versions. It appears I faithfully replicated the
> deficiencies of the old driver as well :-). Again, thank you for the
> feedback.
>
> Anyone else have input before I act on the recommendations I've been
> given? Unless I hear otherwise, I'll start work on the code
> consolidation and removal of dependencies on deprecated include files.
> The detection logic and handling multiple HBAs will take a bit more
> effort...
>
WRT the split of code... if there is some reason why there will never be
another type of card then the split is unnessessary. But otherwise,
you've done the work, and it matches the way other drivers were split,
so why scrap it?
As you guessed I don't feel strongly one way or the other, just thought
you could use a little support for having made the effort to design for
the future.
--
-bill davidsen (davidsen@tmr.com)
"The secret to procrastination is to put things off until the
last possible moment - but no longer" -me
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (new)
2004-04-22 19:38 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (new) Bill Davidsen
@ 2004-04-22 20:48 ` Bob Tracy
0 siblings, 0 replies; 15+ messages in thread
From: Bob Tracy @ 2004-04-22 20:48 UTC (permalink / raw)
To: Bill Davidsen; +Cc: Christoph Hellwig, linux-kernel, linux-scsi
Bill Davidsen wrote:
> WRT the split of code... if there is some reason why there will never be
> another type of card then the split is unnessessary. But otherwise,
> you've done the work, and it matches the way other drivers were split,
> so why scrap it?
>
> As you guessed I don't feel strongly one way or the other, just thought
> you could use a little support for having made the effort to design for
> the future.
You give me too much credit :-). I inherited the split design from the
original author and simply preserved it. As far as maintaining the
split design, the only evidence I've seen of non-PCMCIA cards using the
Symbios/NCR 53c500 controller is the existence of a driver in the FreeBSD
tree. In any event, I don't have the hardware to test anything other
than the PCMCIA case, and if there's someone out there who needs support
for the non-PCMCIA case, I expect the Linux driver would have been written
before now. Christoph suggested in an earlier posting that the split to
support other hardware would probably be accomplished differently these
days anyway, so we (the community) probably haven't lost all that much by
concentrating on producing a well-written PCMCIA-only driver.
I saved my first attempt at a 2.6 driver (in case someone wants to
revisit the issue). Otherwise, although I appreciate your support,
it's OBE. I'm waiting to hear back from Christoph on a few design
issues (questions asked off-line) before submitting a third 2.6 driver.
The main issue I'm wrestling with at the moment is support for the
improbable case of multiple HBAs... I agree in principle with the
objective as long as I don't do something stupid that kills performance
for the normal case of a single HBA. As I see it, there are essentially
two approaches:
(1) Calculate all the needed hardware register offsets up front at HBA
init time, and carry them as part of the per-instance data. This
approach has a few things going for it -- the in-core memory
requirement is essentially identical for the single HBA case, and
dereferencing a structure pointer to get a register address is
barely worse than accessing the equivalent global variable in the
driver.
(2) Calculate a particular register offset each time that register is
accessed, using something like shost->io_port + offset, which, it
is claimed, is normal Linux driver practice. Although this
approach is extremely attractive from the standpoint of code
simplicity, I'm concerned about the arithmetic overhead. If it
turns out most of the hardware register accesses occur during
initialization, my concerns are pretty well meaningless. Obviously
I need to look at this more closely.
You may safely assume I'm learning more about this than I originally
anticipated :-).
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?)
2004-04-20 16:24 ` Christoph Hellwig
@ 2004-04-25 3:01 ` Bob Tracy
2004-04-25 7:36 ` Russell King
2004-04-25 14:34 ` Christoph Hellwig
0 siblings, 2 replies; 15+ messages in thread
From: Bob Tracy @ 2004-04-25 3:01 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-kernel, linux-scsi
[-- Attachment #1: Type: text/plain, Size: 1985 bytes --]
Well, here's the latest attempt at a 2.6 driver for PCMCIA SCSI cards
using the Symbios 53c500 controller. This includes (at least) "newer"
versions of the New Media Bus Toaster (circa 1997). The attached patch
set applies cleanly to 2.6.5.
In addition to all the concerns Christoph raised in response to the
previous two submissions, there was an issue with the code for handling
a CS_EVENT_CARD_RESET event: the event handler was calling the bus_reset
function with a NULL scsi_cmnd argument. The potential problem was
masked by the fact my bus_reset function was a trivial reimplementation
of the default behavior in the absence of a bus_reset function. Folks
using the qlogic_cs driver are hereby warned: if the event handler
tries to process a CS_EVENT_CARD_RESET event (probably following a
"resume"), qlogicfas_bus_reset() will be called with a NULL scsi_cmnd
argument, and the next thing you'll see will be the "oops" resulting
from a null pointer dereference. For what it's worth, someone besides
me has noted this can happen: see the comment block above
qlogicfas_bus_reset() in linux/drivers/scsi/qlogicfas.c.
Major differences between this version and prior ones:
(1) Support for multiple HBAs should now be in place. The only global
host variable affects the PIO speed, and that's affected by a compile-
time define.
(2) Although proc_info is officially obsolete in 2.6, I didn't delete
the associated code: it can be enabled by defining PROC_INFO at compile
time (for people like me who find it useful).
(3) Cleanup/consolidation of multiple functions, including deletion of
code rendered superfluous by new 2.6 ways of doing things.
Barring any remaining issues (Christoph?), I think we're finally ready
for prime time :-).
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
[-- Attachment #2: patch05_sym53c500b --]
[-- Type: text/plain, Size: 34141 bytes --]
--- linux/drivers/scsi/pcmcia/Kconfig.orig Fri Apr 16 16:00:41 2004
+++ linux/drivers/scsi/pcmcia/Kconfig Fri Apr 9 20:17:36 2004
@@ -69,4 +69,14 @@
To compile this driver as a module, choose M here: the
module will be called qlogic_cs.
+config PCMCIA_SYM53C500
+ tristate "Symbios 53c500 PCMCIA support"
+ depends on m
+ help
+ Say Y here if you have a New Media Bus Toaster or other PCMCIA
+ SCSI adapter based on the Symbios 53c500 controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sym53c500_cs.
+
endmenu
--- linux/drivers/scsi/pcmcia/Makefile.orig Fri Apr 16 16:00:41 2004
+++ linux/drivers/scsi/pcmcia/Makefile Fri Apr 16 20:23:44 2004
@@ -6,6 +6,7 @@
obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o
obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o
obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o
+obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o
aha152x_cs-objs := aha152x_stub.o aha152x_core.o
fdomain_cs-objs := fdomain_stub.o fdomain_core.o
--- linux/drivers/scsi/pcmcia/sym53c500_cs.c.orig Fri Apr 16 16:05:19 2004
+++ linux/drivers/scsi/pcmcia/sym53c500_cs.c Sat Apr 24 21:15:30 2004
@@ -0,0 +1,1094 @@
+/*
+* sym53c500_cs.c Bob Tracy (rct@frus.com)
+*
+* A rewrite of the pcmcia-cs add-on driver for newer (circa 1997)
+* New Media Bus Toaster PCMCIA SCSI cards using the Symbios Logic
+* 53c500 controller: intended for use with 2.6 and later kernels.
+* The pcmcia-cs add-on version of this driver is not supported
+* beyond 2.4. It consisted of three files with history/copyright
+* information as follows:
+*
+* SYM53C500.h
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at).
+* Adapted from NCR53c406a.h which is Copyrighted (C) 1994
+* Normunds Saumanis (normunds@rx.tech.swh.lv)
+*
+* SYM53C500.c
+* Bob Tracy (rct@frus.com)
+* Original driver by Tom Corner (tcorner@via.at) was adapted
+* from NCR53c406a.c which is Copyrighted (C) 1994, 1995, 1996
+* Normunds Saumanis (normunds@fi.ibm.com)
+*
+* sym53c500.c
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at) was adapted from a
+* driver for the Qlogic SCSI card written by
+* David Hinds (dhinds@allegro.stanford.edu).
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*/
+
+#define SYM53C500_DEBUG 0
+#define VERBOSE_SYM53C500_DEBUG 0
+
+/*
+* proc_info() is deprecated in 2.6. Set this to 1 if you want to
+* see the entries under /proc that were there in earlier kernels.
+*/
+#define PROC_INFO 1
+
+/*
+* Set this to 0 if you encounter kernel lockups while transferring
+* data in PIO mode.
+*/
+#define USE_FAST_PIO 1
+
+/* =============== End of user configurable parameters ============== */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/stat.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+/* Static function prototypes: needed for scsi_host_template initializer. */
+
+static int SYM53C500_abort(struct scsi_cmnd *);
+static const char *SYM53C500_info(struct Scsi_Host *);
+#if PROC_INFO
+static int SYM53C500_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
+#endif
+static int SYM53C500_queue(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
+static int SYM53C500_host_reset(struct scsi_cmnd *);
+static int SYM53C500_biosparm(struct scsi_device *, struct block_device *, sector_t, int *);
+
+/* ================================================================== */
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"sym53c500_cs.c 0.8 2004/04/20 14:43:52 (Bob Tracy)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ================================================================== */
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static unsigned int irq_mask = 0xdeb8; /* 3, 6, 7, 9-12, 14, 15 */
+static int irq_list[4] = { -1 };
+static int num_irqs = 1;
+
+module_param(irq_mask, int, 0);
+MODULE_PARM_DESC(irq_mask, "IRQ mask bits (default: 0xdeb8)");
+module_param_array(irq_list, int, num_irqs, 0);
+MODULE_PARM_DESC(irq_list, "Comma-separated list of up to 4 IRQs to try (default: auto select).");
+
+/* ================================================================== */
+
+#define SYNC_MODE 0 /* Synchronous transfer mode */
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#define C3_IMG 0x20 /* CDB */
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xa4 /* ? changed from b6= AA PI SIE POL */
+#define C7_IMG 0x80 /* added for SYM53C500 t. corner */
+
+/* Hardware Registers: offsets from io_port (base) */
+
+/* Control Register Set 0 */
+#define TC_LSB 0x00 /* transfer counter lsb */
+#define TC_MSB 0x01 /* transfer counter msb */
+#define SCSI_FIFO 0x02 /* scsi fifo register */
+#define CMD_REG 0x03 /* command register */
+#define STAT_REG 0x04 /* status register */
+#define DEST_ID 0x04 /* selection/reselection bus id */
+#define INT_REG 0x05 /* interrupt status register */
+#define SRTIMOUT 0x05 /* select/reselect timeout reg */
+#define SEQ_REG 0x06 /* sequence step register */
+#define SYNCPRD 0x06 /* synchronous transfer period */
+#define FIFO_FLAGS 0x07 /* indicates # of bytes in fifo */
+#define SYNCOFF 0x07 /* synchronous offset register */
+#define CONFIG1 0x08 /* configuration register */
+#define CLKCONV 0x09 /* clock conversion register */
+/* #define TESTREG 0x0A */ /* test mode register */
+#define CONFIG2 0x0B /* configuration 2 register */
+#define CONFIG3 0x0C /* configuration 3 register */
+#define CONFIG4 0x0D /* configuration 4 register */
+#define TC_HIGH 0x0E /* transfer counter high */
+/* #define FIFO_BOTTOM 0x0F */ /* reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/* #define JUMPER_SENSE 0x00 */ /* jumper sense port reg (r/w) */
+/* #define SRAM_PTR 0x01 */ /* SRAM address pointer reg (r/w) */
+/* #define SRAM_DATA 0x02 */ /* SRAM data register (r/w) */
+#define PIO_FIFO 0x04 /* PIO FIFO registers (r/w) */
+/* #define PIO_FIFO1 0x05 */ /* */
+/* #define PIO_FIFO2 0x06 */ /* */
+/* #define PIO_FIFO3 0x07 */ /* */
+#define PIO_STATUS 0x08 /* PIO status (r/w) */
+/* #define ATA_CMD 0x09 */ /* ATA command/status reg (r/w) */
+/* #define ATA_ERR 0x0A */ /* ATA features/error reg (r/w) */
+#define PIO_FLAG 0x0B /* PIO flag interrupt enable (r/w) */
+#define CONFIG5 0x09 /* configuration 5 register */
+/* #define SIGNATURE 0x0E */ /* signature register (r) */
+/* #define CONFIG6 0x0F */ /* configuration 6 register (r) */
+#define CONFIG7 0x0d
+
+/* select register set 0 */
+#define REG0(x) (outb(C4_IMG, (x) + CONFIG4))
+/* select register set 1 */
+#define REG1(x) outb(C7_IMG, (x) + CONFIG7); outb(C5_IMG, (x) + CONFIG5)
+
+#if SYM53C500_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_SYM53C500_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(x, count) \
+ outb(count & 0xff, (x) + TC_LSB); \
+ outb((count >> 8) & 0xff, (x) + TC_MSB); \
+ outb((count >> 16) & 0xff, (x) + TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/* ================================================================== */
+
+struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+ unsigned short manf_id;
+};
+
+/*
+* Repository for per-instance host data.
+*/
+struct sym53c500_data {
+ struct scsi_cmnd *current_SC;
+ char info_msg[256];
+};
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* ================================================================== */
+
+/*
+* Global (within this module) variables.
+*/
+
+/*
+* scsi_host_template initializer
+*
+* Include file defining structure implies proc_info is optional.
+* Trouble is, directory for host adapter won't get created unless
+* there's a proc_info routine, i.e., nothing will appear in /proc.
+* This is different from the 2.4 implementation.
+*/
+static struct scsi_host_template sym53c500_driver_template = {
+ .module = THIS_MODULE,
+ .name = "SYM53C500",
+ .info = SYM53C500_info,
+ .queuecommand = SYM53C500_queue,
+ .eh_abort_handler = SYM53C500_abort,
+ .eh_host_reset_handler = SYM53C500_host_reset,
+ .bios_param = SYM53C500_biosparm,
+#if PROC_INFO
+ .proc_info = SYM53C500_proc_info,
+#endif
+ .proc_name = "SYM53C500",
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = 32,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+static int fast_pio = USE_FAST_PIO;
+
+/*
+* possible i/o port addresses: provided for informational purposes only,
+* because if Card Services can't find the card, the card doesn't exist.
+*
+* 0x130, 0x230, 0x280, 0x290, 0x320, 0x330, 0x340, 0x350
+*/
+
+static dev_link_t *dev_list = NULL;
+static dev_info_t dev_info = "sym53c500_cs";
+
+/* ================================================================== */
+
+static void
+chip_init(int io_port)
+{
+ REG1(io_port);
+ outb(0x01, io_port + PIO_STATUS);
+ outb(0x00, io_port + PIO_FLAG);
+
+ outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */
+ outb(C3_IMG, io_port + CONFIG3);
+ outb(C2_IMG, io_port + CONFIG2);
+ outb(C1_IMG, io_port + CONFIG1);
+
+ outb(0x05, io_port + CLKCONV); /* clock conversion factor */
+ outb(0x9C, io_port + SRTIMOUT); /* Selection timeout */
+ outb(0x05, io_port + SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, io_port + SYNCOFF); /* synchronous mode */
+}
+
+static void
+SYM53C500_int_host_reset(int io_port)
+{
+ outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */
+ outb(CHIP_RESET, io_port + CMD_REG);
+ outb(SCSI_NOP, io_port + CMD_REG); /* required after reset */
+ outb(SCSI_RESET, io_port + CMD_REG);
+ chip_init(io_port);
+}
+
+static __inline__ int
+SYM53C500_pio_read(int base, unsigned char *request, unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+
+ REG1(base);
+ while (reqlen) {
+ i = inb(base + PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch (i & 0x1e) {
+ default:
+ case 0x10: /* fifo empty */
+ len = 0;
+ break;
+ case 0x0:
+ len = 1;
+ break;
+ case 0x8: /* fifo 1/3 full */
+ len = 42;
+ break;
+ case 0xc: /* fifo 2/3 full */
+ len = 84;
+ break;
+ case 0xe: /* fifo full */
+ len = 128;
+ break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+ return 0;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ insl(base + PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ *request++ = inb(base + PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static __inline__ int
+SYM53C500_pio_write(int base, unsigned char *request, unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+
+ REG1(base);
+ while (reqlen && !(i & 0x40)) {
+ i = inb(base + PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch (i & 0x1e) {
+ case 0x10:
+ len = 128;
+ break;
+ case 0x0:
+ len = 84;
+ break;
+ case 0x8:
+ len = 42;
+ break;
+ case 0xc:
+ len = 1;
+ break;
+ default:
+ case 0xe:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ outsl(base + PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ outb(*request++, base + PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static irqreturn_t
+SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+ DEB(unsigned char fifo_size;)
+ DEB(unsigned char seq_reg;)
+ unsigned char status, int_reg;
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+ int port_base = dev->io_port;
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)dev->hostdata;
+ struct scsi_cmnd *curSC = data->current_SC;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+
+ VDEB(printk("SYM53C500_intr called\n"));
+
+ REG1(port_base);
+ pio_status = inb(port_base + PIO_STATUS);
+ REG0(port_base);
+ status = inb(port_base + STAT_REG);
+ DEB(seq_reg = inb(port_base + SEQ_REG));
+ int_reg = inb(port_base + INT_REG);
+ DEB(fifo_size = inb(port_base + FIFO_FLAGS) & 0x1f);
+
+#if SYM53C500_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x",
+ status, seq_reg, int_reg, fifo_size);
+ printk(", pio=%02x\n", pio_status);
+#endif /* SYM53C500_DEBUG */
+
+ if (int_reg & 0x80) { /* SCSI reset intr */
+ DEB(printk("SYM53C500: reset intr received\n"));
+ curSC->result = DID_RESET << 16;
+ goto idle_out;
+ }
+
+ if (pio_status & 0x80) {
+ printk("SYM53C500: Warning: PIO error!\n");
+ curSC->result = DID_ERROR << 16;
+ goto idle_out;
+ }
+
+ if (status & 0x20) { /* Parity error */
+ printk("SYM53C500: Warning: parity error!\n");
+ curSC->result = DID_PARITY << 16;
+ goto idle_out;
+ }
+
+ if (status & 0x40) { /* Gross error */
+ printk("SYM53C500: Warning: gross error!\n");
+ curSC->result = DID_ERROR << 16;
+ goto idle_out;
+ }
+
+ if (int_reg & 0x20) { /* Disconnect */
+ DEB(printk("SYM53C500: disconnect intr received\n"));
+ if (curSC->SCp.phase != message_in) { /* Unexpected disconnect */
+ curSC->result = DID_NO_CONNECT << 16;
+ } else { /* Command complete, return status and message */
+ curSC->result = (curSC->SCp.Status & 0xff)
+ | ((curSC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+ goto idle_out;
+ }
+
+ switch (status & 0x07) { /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ curSC->SCp.phase = data_out;
+ VDEB(printk("SYM53C500: Data-Out phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+ if (!curSC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_write(port_base, curSC->request_buffer, curSC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = curSC->use_sg;
+ sglist = curSC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_write(port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0(port_base);
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ curSC->SCp.phase = data_in;
+ VDEB(printk("SYM53C500: Data-In phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+ if (!curSC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_read(port_base, curSC->request_buffer, curSC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = curSC->use_sg;
+ sglist = curSC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_read(port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0(port_base);
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ curSC->SCp.phase = command_ph;
+ printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ curSC->SCp.phase = status_ph;
+ VDEB(printk("SYM53C500: Status phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ outb(INIT_CMD_COMPLETE, port_base + CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("SYM53C500: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("SYM53C500: Message-Out phase\n"));
+ curSC->SCp.phase = message_out;
+ outb(SET_ATN, port_base + CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, port_base + CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ VDEB(printk("SYM53C500: Message-In phase\n"));
+ curSC->SCp.phase = message_in;
+
+ curSC->SCp.Status = inb(port_base + SCSI_FIFO);
+ curSC->SCp.Message = inb(port_base + SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(port_base + FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n", curSC->SCp.Status, curSC->SCp.Message));
+
+ if (curSC->SCp.Message == SAVE_POINTERS || curSC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, port_base + CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, port_base + CMD_REG);
+ break;
+ }
+out:
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+
+idle_out:
+ curSC->SCp.phase = idle;
+ curSC->scsi_done(curSC);
+ goto out;
+}
+
+static void
+SYM53C500_release(dev_link_t *link)
+{
+ struct scsi_info_t *info = link->priv;
+ struct Scsi_Host *shost = info->host;
+
+ DEBUG(0, "SYM53C500_release(0x%p)\n", link);
+
+ /*
+ * Do this before releasing/freeing resources.
+ */
+ scsi_remove_host(shost);
+
+ /*
+ * Interrupts getting hosed on card removal. Try
+ * the following code, mostly from qlogicfas.c.
+ */
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+ scsi_host_put(shost);
+} /* SYM53C500_release */
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void
+SYM53C500_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ struct scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ int irq_level, port_base;
+ unsigned short tuple_data[32];
+ struct Scsi_Host *host;
+ struct scsi_host_template *tpnt = &sym53c500_driver_template;
+ struct sym53c500_data *data;
+
+ DEBUG(0, "SYM53C500_config(0x%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *)tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+ (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+ info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+
+ if (link->io.BasePort1 != 0) {
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /*
+ * That's the trouble with copying liberally from another driver.
+ * Some things probably aren't relevant, and I suspect this entire
+ * section dealing with manufacturer IDs can be scrapped. --rct
+ */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ /* set ATAcmd */
+ outb(0xb4, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+
+ /*
+ * Note fast_pio is global and set at compile time.
+ */
+ port_base = link->io.BasePort1;
+ irq_level = link->irq.AssignedIRQ;
+
+ DEB(printk("SYM53C500: port_base=0x%x, irq=%d, fast_pio=%d\n",
+ port_base, irq_level, fast_pio);)
+
+ chip_init(port_base);
+
+ host = scsi_host_alloc(tpnt, sizeof(struct sym53c500_data));
+ if (!host) {
+ printk("SYM53C500: Unable to register host, giving up.\n");
+ goto err_release;
+ }
+
+ data = (struct sym53c500_data *)host->hostdata;
+
+ if (irq_level > 0) {
+ if (request_irq(irq_level, SYM53C500_intr, 0, "SYM53C500", host)) {
+ printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
+ goto err_free_scsi;
+ }
+ tpnt->can_queue = 1;
+ DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
+ } else if (irq_level == 0) {
+ tpnt->can_queue = 0;
+ DEB(printk("SYM53C500: No interrupts detected\n"));
+ goto err_free_scsi;
+ } else {
+ DEB(printk("SYM53C500: Shouldn't get here!\n"));
+ goto err_free_scsi;
+ }
+
+ host->unique_id = port_base;
+ host->irq = irq_level;
+ host->io_port = port_base;
+ host->n_io_port = 0x10;
+ host->dma_channel = -1;
+
+ snprintf(data->info_msg, sizeof(data->info_msg),
+ "SYM53C500 at 0x%x, IRQ %d, %s PIO mode.",
+ port_base, irq_level, fast_pio ? "fast" : "slow");
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+ if (scsi_add_host(host, NULL))
+ goto err_free_irq;
+
+ scsi_scan_host(host);
+
+ goto out; /* SUCCESS */
+
+err_free_irq:
+ free_irq(irq_level, host);
+err_free_scsi:
+ scsi_host_put(host);
+err_release:
+ release_region(port_base, 0x10);
+ printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
+
+out:
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ SYM53C500_release(link);
+ return;
+} /* SYM53C500_config */
+
+static int
+SYM53C500_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct scsi_info_t *info = link->priv;
+
+ DEBUG(1, "SYM53C500_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ SYM53C500_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle, &link->conf);
+ /* See earlier comment about manufacturer IDs. */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ outb(0x80, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+ /*
+ * If things don't work after a "resume",
+ * this is a good place to start looking.
+ */
+ SYM53C500_int_host_reset(link->io.BasePort1);
+ }
+ break;
+ }
+ return 0;
+} /* SYM53C500_event */
+
+static void
+SYM53C500_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link)
+ break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits. */
+ *linkp = link->next;
+ kfree(link->priv);
+ link->priv = NULL;
+} /* SYM53C500_detach */
+
+static dev_link_t *
+SYM53C500_attach(void)
+{
+ struct scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int i, ret;
+
+ DEBUG(0, "SYM53C500_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link;
+ link->priv = info;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.event_handler = &SYM53C500_event;
+ client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ SYM53C500_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* SYM53C500_attach */
+
+#if PROC_INFO
+/*
+* SYM53C500_proc_info is basically a stub function for now. It
+* wouldn't exist except for the fact there were /proc entries for
+* this driver under 2.4 and earlier kernels, and the author (sole
+* user?) expects to see them.
+*
+* buffer: I/O buffer
+* start: if inout == FALSE, pointer into buffer where user
+* read should start
+* offset: current offset
+* length: length of buffer
+* hostno: Scsi_Host host_no
+* inout: 1 --> user is writing; 0 --> user is reading
+*/
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { \
+ if (length > (pos - buffer)) \
+ pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
+ } while (0)
+
+static int
+SYM53C500_proc_info(struct Scsi_Host *shost, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ char *pos = buffer;
+ int thislength;
+ struct sym53c500_data *data;
+
+ if (inout)
+ return(-ENOSYS); /* look, but don't touch */
+
+ /*
+ * Cheesy, but it's what we had before.
+ */
+ data = (struct sym53c500_data *)shost->hostdata;
+ SPRINTF("%s\n", data->info_msg);
+
+ thislength = pos - (buffer + offset);
+
+ if (thislength < 0) {
+ *start = 0;
+ return 0;
+ }
+
+ thislength = min(thislength, length);
+ *start = buffer + offset;
+
+ return thislength;
+}
+#endif /* PROC_INFO */
+
+static const char*
+SYM53C500_info(struct Scsi_Host *SChost)
+{
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SChost->hostdata;
+
+ DEB(printk("SYM53C500_info called\n"));
+ return (data->info_msg);
+}
+
+static int
+SYM53C500_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ int i;
+ int port_base = SCpnt->device->host->io_port;
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SCpnt->device->host->hostdata;
+
+ VDEB(printk("SYM53C500_queue called\n"));
+
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
+ SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->request_bufflen));
+
+ VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+
+ data->current_SC = SCpnt;
+ data->current_SC->scsi_done = done;
+ data->current_SC->SCp.phase = command_ph;
+ data->current_SC->SCp.Status = 0;
+ data->current_SC->SCp.Message = 0;
+
+ /* We are locked here already by the mid layer */
+ REG0(port_base);
+ outb(SCpnt->device->id, port_base + DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, port_base + CMD_REG); /* reset the fifos */
+
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ outb(SCpnt->cmnd[i], port_base + SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, port_base + CMD_REG);
+
+ return 0;
+}
+
+static int
+SYM53C500_abort(struct scsi_cmnd *SCpnt)
+{
+ DEB(printk("SYM53C500_abort called\n"));
+ return FAILED; /* Don't know how to abort */
+}
+
+static int
+SYM53C500_host_reset(struct scsi_cmnd *SCpnt)
+{
+ int port_base = SCpnt->device->host->io_port;
+
+ DEB(printk("SYM53C500_host_reset called\n"));
+ SYM53C500_int_host_reset(port_base);
+
+ return SUCCESS;
+}
+
+static int
+SYM53C500_biosparm(struct scsi_device *disk,
+ struct block_device *dev,
+ sector_t capacity, int *info_array)
+{
+ int size;
+
+ DEB(printk("SYM53C500_biosparm called\n"));
+
+ size = capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size >> 11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255 * 63);
+ }
+ return 0;
+}
+
+MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
+MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver");
+MODULE_LICENSE("GPL");
+
+static struct pcmcia_driver sym53c500_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "sym53c500_cs",
+ },
+ .attach = SYM53C500_attach,
+ .detach = SYM53C500_detach,
+};
+
+static int __init
+init_sym53c500_cs(void)
+{
+ return pcmcia_register_driver(&sym53c500_cs_driver);
+}
+
+static void __exit
+exit_sym53c500_cs(void)
+{
+ pcmcia_unregister_driver(&sym53c500_cs_driver);
+
+ /*
+ * The standard comment seen at this point in almost
+ * every low-level SCSI driver is:
+ *
+ * XXX: this really needs to move into generic code...
+ *
+ * In 2.6, it evidently has been: a debug printk() will
+ * show dev_list to be NULL on entry into the while().
+ *
+ while (dev_list != NULL)
+ SYM53C500_detach(dev_list);
+ */
+}
+
+module_init(init_sym53c500_cs);
+module_exit(exit_sym53c500_cs);
--- linux/Documentation/scsi/sym53c500_cs.txt.orig Fri Apr 16 16:06:19 2004
+++ linux/Documentation/scsi/sym53c500_cs.txt Sat Apr 24 16:03:14 2004
@@ -0,0 +1,23 @@
+The sym53c500_cs driver originated as an add-on to David Hinds' pcmcia-cs
+package, and was written by Tom Corner (tcorner@via.at). A rewrite was
+long overdue, and the current version addresses the following concerns:
+
+ (1) extensive kernel changes between 2.4 and 2.6.
+ (2) deprecated PCMCIA support outside the kernel.
+
+All the USE_BIOS code has been ripped out. It was never used, and could
+not have worked anyway. The USE_DMA code is likewise gone. Many thanks
+to YOKOTA Hiroshi (nsp_cs driver) and David Hinds (qlogic_cs driver) for
+the code fragments I shamelessly adapted for this work. Thanks also to
+Christoph Hellwig for his patient tutelage while I stumbled about.
+
+The Symbios Logic 53c500 chip was used in the "newer" (circa 1997) version
+of the New Media Bus Toaster PCMCIA SCSI controller. Presumably there are
+other products using this chip, but I've never laid eyes (much less hands)
+on one.
+
+Through the years, there have been a number of downloads of the pcmcia-cs
+version of this driver, and I guess it worked for those users. It worked
+for Tom Corner, and it works for me. Your mileage will probably vary.
+
+--Bob Tracy (rct@frus.com)
--- linux/Documentation/scsi/00-INDEX.orig Fri Apr 16 16:07:11 2004
+++ linux/Documentation/scsi/00-INDEX Fri Apr 9 20:17:36 2004
@@ -62,6 +62,8 @@
- info on API between SCSI layer and low level drivers
st.txt
- info on scsi tape driver
+sym53c500_cs.txt
+ - info on PCMCIA driver for Symbios Logic 53c500 based adapters
sym53c8xx_2.txt
- info on second generation driver for sym53c8xx based adapters
tmscsim.txt
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?)
2004-04-25 3:01 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?) Bob Tracy
@ 2004-04-25 7:36 ` Russell King
2004-04-25 21:26 ` Bob Tracy
2004-04-25 14:34 ` Christoph Hellwig
1 sibling, 1 reply; 15+ messages in thread
From: Russell King @ 2004-04-25 7:36 UTC (permalink / raw)
To: Bob Tracy; +Cc: Christoph Hellwig, linux-kernel, linux-scsi
On Sat, Apr 24, 2004 at 10:01:54PM -0500, Bob Tracy wrote:
> In addition to all the concerns Christoph raised in response to the
> previous two submissions, there was an issue with the code for handling
> a CS_EVENT_CARD_RESET event: the event handler was calling the bus_reset
> function with a NULL scsi_cmnd argument.
Hmm, so what happens if you're in the middle of a transaction, and
you receive a CS_EVENT_CARD_RESET. What happens to the command in
progress ?
Sure, the hardware is reset to a sane state, but what about the
software state in the driver?
--
Russell King
Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/
maintainer of: 2.6 PCMCIA - http://pcmcia.arm.linux.org.uk/
2.6 Serial core
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?)
2004-04-25 3:01 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?) Bob Tracy
2004-04-25 7:36 ` Russell King
@ 2004-04-25 14:34 ` Christoph Hellwig
2004-04-25 21:58 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (round 4) Bob Tracy
1 sibling, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2004-04-25 14:34 UTC (permalink / raw)
To: Bob Tracy; +Cc: linux-scsi
On Sat, Apr 24, 2004 at 10:01:54PM -0500, Bob Tracy wrote:
> Barring any remaining issues (Christoph?), I think we're finally ready
> for prime time :-).
there's still a bunch of minor issues. If you don't have them cleaned
up until James' next push to Linux I'd say include the current variant
and fix it up later, else I'd say wait for the next revision.
> +* proc_info() is deprecated in 2.6. Set this to 1 if you want to
> +* see the entries under /proc that were there in earlier kernels.
> +*/
> +#define PROC_INFO 1
please set this to 0 for submission. in fact I think it should really
go away for submission. if you want it for personal use - fine. But
a new 2.6 driver shouldn't implement ->proc_info anymore but rather
use sysfs if nessecery.
> +#include <linux/stat.h>
I don't think you need this one at all.
> +#include <asm/bitops.h>
use linux/bitops.h instead.
> +/* Static function prototypes: needed for scsi_host_template initializer. */
> +
> +static int SYM53C500_abort(struct scsi_cmnd *);
> +static const char *SYM53C500_info(struct Scsi_Host *);
> +#if PROC_INFO
> +static int SYM53C500_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int);
> +#endif
> +static int SYM53C500_queue(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
> +static int SYM53C500_host_reset(struct scsi_cmnd *);
> +static int SYM53C500_biosparm(struct scsi_device *, struct block_device *, sector_t, int *);
What speaks against removing this and declaring the host template after
all these function but before the pcmcia interface code?
(sorry for not answering your previous mail on this, have been busy)
> +static dev_link_t *dev_list = NULL;
you don't need to initialize this to NULL, the compiler will do for you.
> + if (irq_level > 0) {
> + if (request_irq(irq_level, SYM53C500_intr, 0, "SYM53C500", host)) {
> + printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
> + goto err_free_scsi;
> + }
> + tpnt->can_queue = 1;
> + DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
> + } else if (irq_level == 0) {
> + tpnt->can_queue = 0;
> + DEB(printk("SYM53C500: No interrupts detected\n"));
> + goto err_free_scsi;
> + } else {
> + DEB(printk("SYM53C500: Shouldn't get here!\n"));
> + goto err_free_scsi;
> + }
can_queue = 0 isn't valid anymore in 2.6.
> +static int
> +SYM53C500_abort(struct scsi_cmnd *SCpnt)
> +{
> + DEB(printk("SYM53C500_abort called\n"));
> + return FAILED; /* Don't know how to abort */
> +}
no need to implement it if it returns FAILED always.
> + /*
> + * The standard comment seen at this point in almost
> + * every low-level SCSI driver is:
> + *
> + * XXX: this really needs to move into generic code...
> + *
> + * In 2.6, it evidently has been: a debug printk() will
> + * show dev_list to be NULL on entry into the while().
> + *
> + while (dev_list != NULL)
> + SYM53C500_detach(dev_list);
> + */
I'd say just kill this..
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?)
2004-04-25 7:36 ` Russell King
@ 2004-04-25 21:26 ` Bob Tracy
2004-04-25 21:33 ` Russell King
0 siblings, 1 reply; 15+ messages in thread
From: Bob Tracy @ 2004-04-25 21:26 UTC (permalink / raw)
To: Russell King; +Cc: Christoph Hellwig, linux-kernel, linux-scsi
Russell King wrote:
> Hmm, so what happens if you're in the middle of a transaction, and
> you receive a CS_EVENT_CARD_RESET. What happens to the command in
> progress ?
Candidly, I don't know. A fair question to ask in return is, under
what circumstances might a PCMCIA driver see a CS_EVENT_CARD_RESET?
None of the existing PCMCIA SCSI drivers I saw do anything other than
reset the hardware: evidently the assumption is there's no command in
progress at that point, or we don't care. The nsp_cs driver toggles a
stop flag in the per-instance data to indicate the host is accepting
I/O: the flag is set to block I/O upon receipt of a suspend, physical
reset, or card removal event. The card reset code in the nsp_cs driver,
as in mine, is a subset of (fall-through case for) the resume logic.
Given the above, I'm tempted to believe the mid and/or upper driver
layers are handling the "command in progress" issue, but I haven't
delved into that code deeply enough to know.
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?)
2004-04-25 21:26 ` Bob Tracy
@ 2004-04-25 21:33 ` Russell King
2004-04-25 22:59 ` Bob Tracy
0 siblings, 1 reply; 15+ messages in thread
From: Russell King @ 2004-04-25 21:33 UTC (permalink / raw)
To: Bob Tracy; +Cc: Christoph Hellwig, linux-kernel, linux-scsi
On Sun, Apr 25, 2004 at 04:26:34PM -0500, Bob Tracy wrote:
> Russell King wrote:
> > Hmm, so what happens if you're in the middle of a transaction, and
> > you receive a CS_EVENT_CARD_RESET. What happens to the command in
> > progress ?
>
> Candidly, I don't know. A fair question to ask in return is, under
> what circumstances might a PCMCIA driver see a CS_EVENT_CARD_RESET?
When the user issues "cardctl reset"
> None of the existing PCMCIA SCSI drivers I saw do anything other than
> reset the hardware: evidently the assumption is there's no command in
> progress at that point, or we don't care. The nsp_cs driver toggles a
> stop flag in the per-instance data to indicate the host is accepting
> I/O: the flag is set to block I/O upon receipt of a suspend, physical
> reset, or card removal event. The card reset code in the nsp_cs driver,
> as in mine, is a subset of (fall-through case for) the resume logic.
>
> Given the above, I'm tempted to believe the mid and/or upper driver
> layers are handling the "command in progress" issue, but I haven't
> delved into that code deeply enough to know.
>From the brief look I had, it didn't look like it - I suspect things
will go gaga if someone ever invoked "cardctl reset".
--
Russell King
Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/
maintainer of: 2.6 PCMCIA - http://pcmcia.arm.linux.org.uk/
2.6 Serial core
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH] sym53c500_cs PCMCIA SCSI driver (round 4)
2004-04-25 14:34 ` Christoph Hellwig
@ 2004-04-25 21:58 ` Bob Tracy
0 siblings, 0 replies; 15+ messages in thread
From: Bob Tracy @ 2004-04-25 21:58 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-scsi
[-- Attachment #1: Type: text/plain, Size: 3503 bytes --]
Fourth attempt at a 2.6 driver for PCMCIA SCSI cards using the Symbios
53c500 controller is attached.
Christoph Hellwig wrote:
> > +* proc_info() is deprecated in 2.6. Set this to 1 if you want to
> > +* see the entries under /proc that were there in earlier kernels.
> > +*/
> > +#define PROC_INFO 1
>
> please set this to 0 for submission. in fact I think it should really
> go away for submission. if you want it for personal use - fine. But
> a new 2.6 driver shouldn't implement ->proc_info anymore but rather
> use sysfs if nessecery.
I compromised... PROC_INFO is set to 0 for submission. I intended that
to be the default, but obviously *I* still use the stuff under /proc.
> > +#include <linux/stat.h>
>
> I don't think you need this one at all.
Gone.
> > +#include <asm/bitops.h>
>
> use linux/bitops.h instead.
Done.
> > +/* Static function prototypes: needed for scsi_host_template initializer. */
>
> What speaks against removing this and declaring the host template after
> all these function but before the pcmcia interface code?
Done. Two reasons it wasn't accomplished sooner: (1) initially, I
didn't see a way to avoid at least one forward declaration/prototype;
and (2) beauty being in the eye of the beholder, I frankly thought
having the host template declaration in the middle of the code was
uglier than using the prototypes to place the template declaration
where it was. Item (2) became a non-issue for me when I came to the
realization the template placement is more aesthetically pleasing
*without* the prototypes than it was *with* the prototypes :-).
> > +static dev_link_t *dev_list = NULL;
>
> you don't need to initialize this to NULL, the compiler will do for you.
<Homer_Simpson>Doh!</Homer_Simpson> Done.
> > + if (irq_level > 0) {
> > + if (request_irq(irq_level, SYM53C500_intr, 0, "SYM53C500", host)) {
> > + printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
> > + goto err_free_scsi;
> > + }
> > + tpnt->can_queue = 1;
> > + DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
> > + } else if (irq_level == 0) {
> > + tpnt->can_queue = 0;
> > + DEB(printk("SYM53C500: No interrupts detected\n"));
> > + goto err_free_scsi;
> > + } else {
> > + DEB(printk("SYM53C500: Shouldn't get here!\n"));
> > + goto err_free_scsi;
> > + }
>
> can_queue = 0 isn't valid anymore in 2.6.
Nod. Also eliminated the tpnt->can_queue assignment statements. In
the case of irq_level > 0, the template initializer has already set
that member to 1. Setting it to zero in the case of irq_level == 0
was a waste of object code bytes because we were on the error return
path at that point anyway.
> > +SYM53C500_abort(struct scsi_cmnd *SCpnt)
>
> no need to implement it if it returns FAILED always.
Sorry... Missed that the last go-round.
> > + * XXX: this really needs to move into generic code...
>
> I'd say just kill this..
Done.
Anything else? There's a dangling "unknown" with respect to what the
low-level driver should do upon receipt of CS_EVENT_CARD_RESET: Russell
King asked what happens to the command in progress. Based on what I
think I saw in other PCMCIA SCSI drivers, should I care? Right now, I
do pretty much what the other drivers do -- reset the host instance.
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
[-- Attachment #2: patch05_sym53c500c --]
[-- Type: text/plain, Size: 33202 bytes --]
--- linux/drivers/scsi/pcmcia/Kconfig.orig 2004-04-25 14:44:16.000000000 -0500
+++ linux/drivers/scsi/pcmcia/Kconfig 2004-04-25 14:54:48.000000000 -0500
@@ -69,4 +69,14 @@
To compile this driver as a module, choose M here: the
module will be called qlogic_cs.
+config PCMCIA_SYM53C500
+ tristate "Symbios 53c500 PCMCIA support"
+ depends on m
+ help
+ Say Y here if you have a New Media Bus Toaster or other PCMCIA
+ SCSI adapter based on the Symbios 53c500 controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sym53c500_cs.
+
endmenu
--- linux/drivers/scsi/pcmcia/Makefile.orig 2004-04-25 14:44:17.000000000 -0500
+++ linux/drivers/scsi/pcmcia/Makefile 2004-04-25 14:54:48.000000000 -0500
@@ -6,6 +6,7 @@
obj-$(CONFIG_PCMCIA_FDOMAIN) += fdomain_cs.o
obj-$(CONFIG_PCMCIA_AHA152X) += aha152x_cs.o
obj-$(CONFIG_PCMCIA_NINJA_SCSI) += nsp_cs.o
+obj-$(CONFIG_PCMCIA_SYM53C500) += sym53c500_cs.o
aha152x_cs-objs := aha152x_stub.o aha152x_core.o
fdomain_cs-objs := fdomain_stub.o fdomain_core.o
--- linux/drivers/scsi/pcmcia/sym53c500_cs.c.orig 2004-04-25 14:44:17.000000000 -0500
+++ linux/drivers/scsi/pcmcia/sym53c500_cs.c 2004-04-25 15:34:57.000000000 -0500
@@ -0,0 +1,1061 @@
+/*
+* sym53c500_cs.c Bob Tracy (rct@frus.com)
+*
+* A rewrite of the pcmcia-cs add-on driver for newer (circa 1997)
+* New Media Bus Toaster PCMCIA SCSI cards using the Symbios Logic
+* 53c500 controller: intended for use with 2.6 and later kernels.
+* The pcmcia-cs add-on version of this driver is not supported
+* beyond 2.4. It consisted of three files with history/copyright
+* information as follows:
+*
+* SYM53C500.h
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at).
+* Adapted from NCR53c406a.h which is Copyrighted (C) 1994
+* Normunds Saumanis (normunds@rx.tech.swh.lv)
+*
+* SYM53C500.c
+* Bob Tracy (rct@frus.com)
+* Original driver by Tom Corner (tcorner@via.at) was adapted
+* from NCR53c406a.c which is Copyrighted (C) 1994, 1995, 1996
+* Normunds Saumanis (normunds@fi.ibm.com)
+*
+* sym53c500.c
+* Bob Tracy (rct@frus.com)
+* Original by Tom Corner (tcorner@via.at) was adapted from a
+* driver for the Qlogic SCSI card written by
+* David Hinds (dhinds@allegro.stanford.edu).
+*
+* This program is free software; you can redistribute it and/or modify it
+* under the terms of the GNU General Public License as published by the
+* Free Software Foundation; either version 2, or (at your option) any
+* later version.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*/
+
+#define SYM53C500_DEBUG 0
+#define VERBOSE_SYM53C500_DEBUG 0
+
+/*
+* proc_info() is deprecated in 2.6. Set this to 1 if you want to
+* see the entries under /proc that were there in earlier kernels.
+*/
+#define PROC_INFO 0
+
+/*
+* Set this to 0 if you encounter kernel lockups while transferring
+* data in PIO mode.
+*/
+#define USE_FAST_PIO 1
+
+/* =============== End of user configurable parameters ============== */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ciscode.h>
+
+/* ================================================================== */
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"sym53c500_cs.c 0.9 2004/04/25 15:23:38 (Bob Tracy)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ================================================================== */
+
+/* Parameters that can be set with 'insmod' */
+
+/* Bit map of interrupts to choose from */
+static unsigned int irq_mask = 0xdeb8; /* 3, 6, 7, 9-12, 14, 15 */
+static int irq_list[4] = { -1 };
+static int num_irqs = 1;
+
+module_param(irq_mask, int, 0);
+MODULE_PARM_DESC(irq_mask, "IRQ mask bits (default: 0xdeb8)");
+module_param_array(irq_list, int, num_irqs, 0);
+MODULE_PARM_DESC(irq_list, "Comma-separated list of up to 4 IRQs to try (default: auto select).");
+
+/* ================================================================== */
+
+#define SYNC_MODE 0 /* Synchronous transfer mode */
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#define C3_IMG 0x20 /* CDB */
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xa4 /* ? changed from b6= AA PI SIE POL */
+#define C7_IMG 0x80 /* added for SYM53C500 t. corner */
+
+/* Hardware Registers: offsets from io_port (base) */
+
+/* Control Register Set 0 */
+#define TC_LSB 0x00 /* transfer counter lsb */
+#define TC_MSB 0x01 /* transfer counter msb */
+#define SCSI_FIFO 0x02 /* scsi fifo register */
+#define CMD_REG 0x03 /* command register */
+#define STAT_REG 0x04 /* status register */
+#define DEST_ID 0x04 /* selection/reselection bus id */
+#define INT_REG 0x05 /* interrupt status register */
+#define SRTIMOUT 0x05 /* select/reselect timeout reg */
+#define SEQ_REG 0x06 /* sequence step register */
+#define SYNCPRD 0x06 /* synchronous transfer period */
+#define FIFO_FLAGS 0x07 /* indicates # of bytes in fifo */
+#define SYNCOFF 0x07 /* synchronous offset register */
+#define CONFIG1 0x08 /* configuration register */
+#define CLKCONV 0x09 /* clock conversion register */
+/* #define TESTREG 0x0A */ /* test mode register */
+#define CONFIG2 0x0B /* configuration 2 register */
+#define CONFIG3 0x0C /* configuration 3 register */
+#define CONFIG4 0x0D /* configuration 4 register */
+#define TC_HIGH 0x0E /* transfer counter high */
+/* #define FIFO_BOTTOM 0x0F */ /* reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/* #define JUMPER_SENSE 0x00 */ /* jumper sense port reg (r/w) */
+/* #define SRAM_PTR 0x01 */ /* SRAM address pointer reg (r/w) */
+/* #define SRAM_DATA 0x02 */ /* SRAM data register (r/w) */
+#define PIO_FIFO 0x04 /* PIO FIFO registers (r/w) */
+/* #define PIO_FIFO1 0x05 */ /* */
+/* #define PIO_FIFO2 0x06 */ /* */
+/* #define PIO_FIFO3 0x07 */ /* */
+#define PIO_STATUS 0x08 /* PIO status (r/w) */
+/* #define ATA_CMD 0x09 */ /* ATA command/status reg (r/w) */
+/* #define ATA_ERR 0x0A */ /* ATA features/error reg (r/w) */
+#define PIO_FLAG 0x0B /* PIO flag interrupt enable (r/w) */
+#define CONFIG5 0x09 /* configuration 5 register */
+/* #define SIGNATURE 0x0E */ /* signature register (r) */
+/* #define CONFIG6 0x0F */ /* configuration 6 register (r) */
+#define CONFIG7 0x0d
+
+/* select register set 0 */
+#define REG0(x) (outb(C4_IMG, (x) + CONFIG4))
+/* select register set 1 */
+#define REG1(x) outb(C7_IMG, (x) + CONFIG7); outb(C5_IMG, (x) + CONFIG5)
+
+#if SYM53C500_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_SYM53C500_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(x, count) \
+ outb(count & 0xff, (x) + TC_LSB); \
+ outb((count >> 8) & 0xff, (x) + TC_MSB); \
+ outb((count >> 16) & 0xff, (x) + TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/* ================================================================== */
+
+struct scsi_info_t {
+ dev_link_t link;
+ dev_node_t node;
+ struct Scsi_Host *host;
+ unsigned short manf_id;
+};
+
+/*
+* Repository for per-instance host data.
+*/
+struct sym53c500_data {
+ struct scsi_cmnd *current_SC;
+ char info_msg[256];
+};
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* ================================================================== */
+
+/*
+* Global (within this module) variables other than
+* sym53c500_driver_template (the scsi_host_template).
+*/
+static int fast_pio = USE_FAST_PIO;
+
+static dev_link_t *dev_list;
+static dev_info_t dev_info = "sym53c500_cs";
+
+/* ================================================================== */
+
+static void
+chip_init(int io_port)
+{
+ REG1(io_port);
+ outb(0x01, io_port + PIO_STATUS);
+ outb(0x00, io_port + PIO_FLAG);
+
+ outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */
+ outb(C3_IMG, io_port + CONFIG3);
+ outb(C2_IMG, io_port + CONFIG2);
+ outb(C1_IMG, io_port + CONFIG1);
+
+ outb(0x05, io_port + CLKCONV); /* clock conversion factor */
+ outb(0x9C, io_port + SRTIMOUT); /* Selection timeout */
+ outb(0x05, io_port + SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, io_port + SYNCOFF); /* synchronous mode */
+}
+
+static void
+SYM53C500_int_host_reset(int io_port)
+{
+ outb(C4_IMG, io_port + CONFIG4); /* REG0(io_port); */
+ outb(CHIP_RESET, io_port + CMD_REG);
+ outb(SCSI_NOP, io_port + CMD_REG); /* required after reset */
+ outb(SCSI_RESET, io_port + CMD_REG);
+ chip_init(io_port);
+}
+
+static __inline__ int
+SYM53C500_pio_read(int base, unsigned char *request, unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+
+ REG1(base);
+ while (reqlen) {
+ i = inb(base + PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch (i & 0x1e) {
+ default:
+ case 0x10: /* fifo empty */
+ len = 0;
+ break;
+ case 0x0:
+ len = 1;
+ break;
+ case 0x8: /* fifo 1/3 full */
+ len = 42;
+ break;
+ case 0xc: /* fifo 2/3 full */
+ len = 84;
+ break;
+ case 0xe: /* fifo full */
+ len = 128;
+ break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occurred */
+ return 0;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ insl(base + PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ *request++ = inb(base + PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static __inline__ int
+SYM53C500_pio_write(int base, unsigned char *request, unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+
+ REG1(base);
+ while (reqlen && !(i & 0x40)) {
+ i = inb(base + PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch (i & 0x1e) {
+ case 0x10:
+ len = 128;
+ break;
+ case 0x0:
+ len = 84;
+ break;
+ case 0x8:
+ len = 42;
+ break;
+ case 0xc:
+ len = 1;
+ break;
+ default:
+ case 0xe:
+ len = 0;
+ break;
+ }
+
+ if (len) {
+ if (len > reqlen)
+ len = reqlen;
+
+ if (fast_pio && len > 3) {
+ outsl(base + PIO_FIFO, request, len >> 2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ } else {
+ while (len--) {
+ outb(*request++, base + PIO_FIFO);
+ reqlen--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static irqreturn_t
+SYM53C500_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long flags;
+ struct Scsi_Host *dev = dev_id;
+ DEB(unsigned char fifo_size;)
+ DEB(unsigned char seq_reg;)
+ unsigned char status, int_reg;
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+ int port_base = dev->io_port;
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)dev->hostdata;
+ struct scsi_cmnd *curSC = data->current_SC;
+
+ spin_lock_irqsave(dev->host_lock, flags);
+
+ VDEB(printk("SYM53C500_intr called\n"));
+
+ REG1(port_base);
+ pio_status = inb(port_base + PIO_STATUS);
+ REG0(port_base);
+ status = inb(port_base + STAT_REG);
+ DEB(seq_reg = inb(port_base + SEQ_REG));
+ int_reg = inb(port_base + INT_REG);
+ DEB(fifo_size = inb(port_base + FIFO_FLAGS) & 0x1f);
+
+#if SYM53C500_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x",
+ status, seq_reg, int_reg, fifo_size);
+ printk(", pio=%02x\n", pio_status);
+#endif /* SYM53C500_DEBUG */
+
+ if (int_reg & 0x80) { /* SCSI reset intr */
+ DEB(printk("SYM53C500: reset intr received\n"));
+ curSC->result = DID_RESET << 16;
+ goto idle_out;
+ }
+
+ if (pio_status & 0x80) {
+ printk("SYM53C500: Warning: PIO error!\n");
+ curSC->result = DID_ERROR << 16;
+ goto idle_out;
+ }
+
+ if (status & 0x20) { /* Parity error */
+ printk("SYM53C500: Warning: parity error!\n");
+ curSC->result = DID_PARITY << 16;
+ goto idle_out;
+ }
+
+ if (status & 0x40) { /* Gross error */
+ printk("SYM53C500: Warning: gross error!\n");
+ curSC->result = DID_ERROR << 16;
+ goto idle_out;
+ }
+
+ if (int_reg & 0x20) { /* Disconnect */
+ DEB(printk("SYM53C500: disconnect intr received\n"));
+ if (curSC->SCp.phase != message_in) { /* Unexpected disconnect */
+ curSC->result = DID_NO_CONNECT << 16;
+ } else { /* Command complete, return status and message */
+ curSC->result = (curSC->SCp.Status & 0xff)
+ | ((curSC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+ goto idle_out;
+ }
+
+ switch (status & 0x07) { /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ curSC->SCp.phase = data_out;
+ VDEB(printk("SYM53C500: Data-Out phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+ if (!curSC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_write(port_base, curSC->request_buffer, curSC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = curSC->use_sg;
+ sglist = curSC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_write(port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0(port_base);
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if (int_reg & 0x10) { /* Target requesting info transfer */
+ curSC->SCp.phase = data_in;
+ VDEB(printk("SYM53C500: Data-In phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ LOAD_DMA_COUNT(port_base, curSC->request_bufflen); /* Max transfer size */
+ outb(TRANSFER_INFO | DMA_OP, port_base + CMD_REG);
+ if (!curSC->use_sg) /* Don't use scatter-gather */
+ SYM53C500_pio_read(port_base, curSC->request_buffer, curSC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = curSC->use_sg;
+ sglist = curSC->request_buffer;
+ while (sgcount--) {
+ SYM53C500_pio_read(port_base, page_address(sglist->page) + sglist->offset, sglist->length);
+ sglist++;
+ }
+ }
+ REG0(port_base);
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ curSC->SCp.phase = command_ph;
+ printk("SYM53C500: Warning: Unknown interrupt occurred in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ curSC->SCp.phase = status_ph;
+ VDEB(printk("SYM53C500: Status phase\n"));
+ outb(FLUSH_FIFO, port_base + CMD_REG);
+ outb(INIT_CMD_COMPLETE, port_base + CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("SYM53C500: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("SYM53C500: Message-Out phase\n"));
+ curSC->SCp.phase = message_out;
+ outb(SET_ATN, port_base + CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, port_base + CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ VDEB(printk("SYM53C500: Message-In phase\n"));
+ curSC->SCp.phase = message_in;
+
+ curSC->SCp.Status = inb(port_base + SCSI_FIFO);
+ curSC->SCp.Message = inb(port_base + SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(port_base + FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n", curSC->SCp.Status, curSC->SCp.Message));
+
+ if (curSC->SCp.Message == SAVE_POINTERS || curSC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, port_base + CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, port_base + CMD_REG);
+ break;
+ }
+out:
+ spin_unlock_irqrestore(dev->host_lock, flags);
+ return IRQ_HANDLED;
+
+idle_out:
+ curSC->SCp.phase = idle;
+ curSC->scsi_done(curSC);
+ goto out;
+}
+
+static void
+SYM53C500_release(dev_link_t *link)
+{
+ struct scsi_info_t *info = link->priv;
+ struct Scsi_Host *shost = info->host;
+
+ DEBUG(0, "SYM53C500_release(0x%p)\n", link);
+
+ /*
+ * Do this before releasing/freeing resources.
+ */
+ scsi_remove_host(shost);
+
+ /*
+ * Interrupts getting hosed on card removal. Try
+ * the following code, mostly from qlogicfas.c.
+ */
+ if (shost->irq)
+ free_irq(shost->irq, shost);
+ if (shost->dma_channel != 0xff)
+ free_dma(shost->dma_channel);
+ if (shost->io_port && shost->n_io_port)
+ release_region(shost->io_port, shost->n_io_port);
+
+ link->dev = NULL;
+
+ pcmcia_release_configuration(link->handle);
+ pcmcia_release_io(link->handle, &link->io);
+ pcmcia_release_irq(link->handle, &link->irq);
+
+ link->state &= ~DEV_CONFIG;
+
+ scsi_host_put(shost);
+} /* SYM53C500_release */
+
+static const char*
+SYM53C500_info(struct Scsi_Host *SChost)
+{
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SChost->hostdata;
+
+ DEB(printk("SYM53C500_info called\n"));
+ return (data->info_msg);
+}
+
+static int
+SYM53C500_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *))
+{
+ int i;
+ int port_base = SCpnt->device->host->io_port;
+ struct sym53c500_data *data =
+ (struct sym53c500_data *)SCpnt->device->host->hostdata;
+
+ VDEB(printk("SYM53C500_queue called\n"));
+
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
+ SCpnt->cmnd[0], SCpnt->cmd_len, SCpnt->device->id,
+ SCpnt->device->lun, SCpnt->request_bufflen));
+
+ VDEB(for (i = 0; i < SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+
+ data->current_SC = SCpnt;
+ data->current_SC->scsi_done = done;
+ data->current_SC->SCp.phase = command_ph;
+ data->current_SC->SCp.Status = 0;
+ data->current_SC->SCp.Message = 0;
+
+ /* We are locked here already by the mid layer */
+ REG0(port_base);
+ outb(SCpnt->device->id, port_base + DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, port_base + CMD_REG); /* reset the fifos */
+
+ for (i = 0; i < SCpnt->cmd_len; i++) {
+ outb(SCpnt->cmnd[i], port_base + SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, port_base + CMD_REG);
+
+ return 0;
+}
+
+static int
+SYM53C500_host_reset(struct scsi_cmnd *SCpnt)
+{
+ int port_base = SCpnt->device->host->io_port;
+
+ DEB(printk("SYM53C500_host_reset called\n"));
+ SYM53C500_int_host_reset(port_base);
+
+ return SUCCESS;
+}
+
+static int
+SYM53C500_biosparm(struct scsi_device *disk,
+ struct block_device *dev,
+ sector_t capacity, int *info_array)
+{
+ int size;
+
+ DEB(printk("SYM53C500_biosparm called\n"));
+
+ size = capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size >> 11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255 * 63);
+ }
+ return 0;
+}
+
+#if PROC_INFO
+/*
+* SYM53C500_proc_info is basically a stub function for now. It
+* wouldn't exist except for the fact there were /proc entries for
+* this driver under 2.4 and earlier kernels, and the author (sole
+* user?) expects to see them.
+*
+* buffer: I/O buffer
+* start: if inout == FALSE, pointer into buffer where user
+* read should start
+* offset: current offset
+* length: length of buffer
+* hostno: Scsi_Host host_no
+* inout: 1 --> user is writing; 0 --> user is reading
+*/
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+ do { \
+ if (length > (pos - buffer)) \
+ pos += snprintf(pos, length - (pos - buffer) + 1, ## args); \
+ } while (0)
+
+static int
+SYM53C500_proc_info(struct Scsi_Host *shost, char *buffer,
+ char **start, off_t offset, int length, int inout)
+{
+ char *pos = buffer;
+ int thislength;
+ struct sym53c500_data *data;
+
+ if (inout)
+ return(-ENOSYS); /* look, but don't touch */
+
+ /*
+ * Cheesy, but it's what we had before.
+ */
+ data = (struct sym53c500_data *)shost->hostdata;
+ SPRINTF("%s\n", data->info_msg);
+
+ thislength = pos - (buffer + offset);
+
+ if (thislength < 0) {
+ *start = 0;
+ return 0;
+ }
+
+ thislength = min(thislength, length);
+ *start = buffer + offset;
+
+ return thislength;
+}
+#endif /* PROC_INFO */
+
+/*
+* scsi_host_template initializer
+*
+* Include file defining structure implies proc_info is optional.
+* Trouble is, directory for host adapter won't get created unless
+* there's a proc_info routine, i.e., nothing will appear in /proc.
+* This is different from the 2.4 implementation.
+*/
+static struct scsi_host_template sym53c500_driver_template = {
+ .module = THIS_MODULE,
+ .name = "SYM53C500",
+ .info = SYM53C500_info,
+ .queuecommand = SYM53C500_queue,
+ .eh_host_reset_handler = SYM53C500_host_reset,
+ .bios_param = SYM53C500_biosparm,
+#if PROC_INFO
+ .proc_info = SYM53C500_proc_info,
+#endif
+ .proc_name = "SYM53C500",
+ .can_queue = 1,
+ .this_id = 7,
+ .sg_tablesize = 32,
+ .cmd_per_lun = 1,
+ .use_clustering = ENABLE_CLUSTERING
+};
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void
+SYM53C500_config(dev_link_t *link)
+{
+ client_handle_t handle = link->handle;
+ struct scsi_info_t *info = link->priv;
+ tuple_t tuple;
+ cisparse_t parse;
+ int i, last_ret, last_fn;
+ int irq_level, port_base;
+ unsigned short tuple_data[32];
+ struct Scsi_Host *host;
+ struct scsi_host_template *tpnt = &sym53c500_driver_template;
+ struct sym53c500_data *data;
+
+ DEBUG(0, "SYM53C500_config(0x%p)\n", link);
+
+ tuple.TupleData = (cisdata_t *)tuple_data;
+ tuple.TupleDataMax = 64;
+ tuple.TupleOffset = 0;
+ tuple.DesiredTuple = CISTPL_CONFIG;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+ CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+ link->conf.ConfigBase = parse.config.base;
+
+ tuple.DesiredTuple = CISTPL_MANFID;
+ if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+ (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS))
+ info->manf_id = le16_to_cpu(tuple.TupleData[0]);
+
+ /* Configure card */
+ link->state |= DEV_CONFIG;
+
+ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+ while (1) {
+ if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+ pcmcia_parse_tuple(handle, &tuple, &parse) != 0)
+ goto next_entry;
+ link->conf.ConfigIndex = parse.cftable_entry.index;
+ link->io.BasePort1 = parse.cftable_entry.io.win[0].base;
+ link->io.NumPorts1 = parse.cftable_entry.io.win[0].len;
+
+ if (link->io.BasePort1 != 0) {
+ i = pcmcia_request_io(handle, &link->io);
+ if (i == CS_SUCCESS)
+ break;
+ }
+next_entry:
+ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+ }
+
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+
+ /*
+ * That's the trouble with copying liberally from another driver.
+ * Some things probably aren't relevant, and I suspect this entire
+ * section dealing with manufacturer IDs can be scrapped. --rct
+ */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ /* set ATAcmd */
+ outb(0xb4, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+
+ /*
+ * Note fast_pio is global and set at compile time.
+ *
+ * irq_level == 0 implies tpnt->can_queue == 0, which
+ * is not supported in 2.6. Thus, only irq_level > 0
+ * will be allowed.
+ *
+ * Possible port_base values are as follows:
+ *
+ * 0x130, 0x230, 0x280, 0x290,
+ * 0x320, 0x330, 0x340, 0x350
+ */
+ port_base = link->io.BasePort1;
+ irq_level = link->irq.AssignedIRQ;
+
+ DEB(printk("SYM53C500: port_base=0x%x, irq=%d, fast_pio=%d\n",
+ port_base, irq_level, fast_pio);)
+
+ chip_init(port_base);
+
+ host = scsi_host_alloc(tpnt, sizeof(struct sym53c500_data));
+ if (!host) {
+ printk("SYM53C500: Unable to register host, giving up.\n");
+ goto err_release;
+ }
+
+ data = (struct sym53c500_data *)host->hostdata;
+
+ if (irq_level > 0) {
+ if (request_irq(irq_level, SYM53C500_intr, 0, "SYM53C500", host)) {
+ printk("SYM53C500: unable to allocate IRQ %d\n", irq_level);
+ goto err_free_scsi;
+ }
+ DEB(printk("SYM53C500: allocated IRQ %d\n", irq_level));
+ } else if (irq_level == 0) {
+ DEB(printk("SYM53C500: No interrupts detected\n"));
+ goto err_free_scsi;
+ } else {
+ DEB(printk("SYM53C500: Shouldn't get here!\n"));
+ goto err_free_scsi;
+ }
+
+ host->unique_id = port_base;
+ host->irq = irq_level;
+ host->io_port = port_base;
+ host->n_io_port = 0x10;
+ host->dma_channel = -1;
+
+ snprintf(data->info_msg, sizeof(data->info_msg),
+ "SYM53C500 at 0x%x, IRQ %d, %s PIO mode.",
+ port_base, irq_level, fast_pio ? "fast" : "slow");
+
+ sprintf(info->node.dev_name, "scsi%d", host->host_no);
+ link->dev = &info->node;
+ info->host = host;
+
+ if (scsi_add_host(host, NULL))
+ goto err_free_irq;
+
+ scsi_scan_host(host);
+
+ goto out; /* SUCCESS */
+
+err_free_irq:
+ free_irq(irq_level, host);
+err_free_scsi:
+ scsi_host_put(host);
+err_release:
+ release_region(port_base, 0x10);
+ printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n");
+
+out:
+ link->state &= ~DEV_CONFIG_PENDING;
+ return;
+
+cs_failed:
+ cs_error(link->handle, last_fn, last_ret);
+ SYM53C500_release(link);
+ return;
+} /* SYM53C500_config */
+
+static int
+SYM53C500_event(event_t event, int priority, event_callback_args_t *args)
+{
+ dev_link_t *link = args->client_data;
+ struct scsi_info_t *info = link->priv;
+
+ DEBUG(1, "SYM53C500_event(0x%06x)\n", event);
+
+ switch (event) {
+ case CS_EVENT_CARD_REMOVAL:
+ link->state &= ~DEV_PRESENT;
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+ break;
+ case CS_EVENT_CARD_INSERTION:
+ link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+ SYM53C500_config(link);
+ break;
+ case CS_EVENT_PM_SUSPEND:
+ link->state |= DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_RESET_PHYSICAL:
+ if (link->state & DEV_CONFIG)
+ pcmcia_release_configuration(link->handle);
+ break;
+ case CS_EVENT_PM_RESUME:
+ link->state &= ~DEV_SUSPEND;
+ /* Fall through... */
+ case CS_EVENT_CARD_RESET:
+ if (link->state & DEV_CONFIG) {
+ pcmcia_request_configuration(link->handle, &link->conf);
+ /* See earlier comment about manufacturer IDs. */
+ if ((info->manf_id == MANFID_MACNICA) ||
+ (info->manf_id == MANFID_PIONEER) ||
+ (info->manf_id == 0x0098)) {
+ outb(0x80, link->io.BasePort1 + 0xd);
+ outb(0x24, link->io.BasePort1 + 0x9);
+ outb(0x04, link->io.BasePort1 + 0xd);
+ }
+ /*
+ * If things don't work after a "resume",
+ * this is a good place to start looking.
+ */
+ SYM53C500_int_host_reset(link->io.BasePort1);
+ }
+ break;
+ }
+ return 0;
+} /* SYM53C500_event */
+
+static void
+SYM53C500_detach(dev_link_t *link)
+{
+ dev_link_t **linkp;
+
+ DEBUG(0, "SYM53C500_detach(0x%p)\n", link);
+
+ /* Locate device structure */
+ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+ if (*linkp == link)
+ break;
+ if (*linkp == NULL)
+ return;
+
+ if (link->state & DEV_CONFIG)
+ SYM53C500_release(link);
+
+ if (link->handle)
+ pcmcia_deregister_client(link->handle);
+
+ /* Unlink device structure, free bits. */
+ *linkp = link->next;
+ kfree(link->priv);
+ link->priv = NULL;
+} /* SYM53C500_detach */
+
+static dev_link_t *
+SYM53C500_attach(void)
+{
+ struct scsi_info_t *info;
+ client_reg_t client_reg;
+ dev_link_t *link;
+ int i, ret;
+
+ DEBUG(0, "SYM53C500_attach()\n");
+
+ /* Create new SCSI device */
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+ memset(info, 0, sizeof(*info));
+ link = &info->link;
+ link->priv = info;
+ link->io.NumPorts1 = 16;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 10;
+ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+ if (irq_list[0] == -1)
+ link->irq.IRQInfo2 = irq_mask;
+ else
+ for (i = 0; i < 4; i++)
+ link->irq.IRQInfo2 |= 1 << irq_list[i];
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.Vcc = 50;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+ link->conf.Present = PRESENT_OPTION;
+
+ /* Register with Card Services */
+ link->next = dev_list;
+ dev_list = link;
+ client_reg.dev_info = &dev_info;
+ client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+ client_reg.event_handler = &SYM53C500_event;
+ client_reg.EventMask = CS_EVENT_RESET_REQUEST | CS_EVENT_CARD_RESET |
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = link;
+ ret = pcmcia_register_client(&link->handle, &client_reg);
+ if (ret != 0) {
+ cs_error(link->handle, RegisterClient, ret);
+ SYM53C500_detach(link);
+ return NULL;
+ }
+
+ return link;
+} /* SYM53C500_attach */
+
+MODULE_AUTHOR("Bob Tracy <rct@frus.com>");
+MODULE_DESCRIPTION("SYM53C500 PCMCIA SCSI driver");
+MODULE_LICENSE("GPL");
+
+static struct pcmcia_driver sym53c500_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "sym53c500_cs",
+ },
+ .attach = SYM53C500_attach,
+ .detach = SYM53C500_detach,
+};
+
+static int __init
+init_sym53c500_cs(void)
+{
+ return pcmcia_register_driver(&sym53c500_cs_driver);
+}
+
+static void __exit
+exit_sym53c500_cs(void)
+{
+ pcmcia_unregister_driver(&sym53c500_cs_driver);
+}
+
+module_init(init_sym53c500_cs);
+module_exit(exit_sym53c500_cs);
--- linux/Documentation/scsi/sym53c500_cs.txt.orig 2004-04-25 14:44:17.000000000 -0500
+++ linux/Documentation/scsi/sym53c500_cs.txt 2004-04-25 14:54:48.000000000 -0500
@@ -0,0 +1,23 @@
+The sym53c500_cs driver originated as an add-on to David Hinds' pcmcia-cs
+package, and was written by Tom Corner (tcorner@via.at). A rewrite was
+long overdue, and the current version addresses the following concerns:
+
+ (1) extensive kernel changes between 2.4 and 2.6.
+ (2) deprecated PCMCIA support outside the kernel.
+
+All the USE_BIOS code has been ripped out. It was never used, and could
+not have worked anyway. The USE_DMA code is likewise gone. Many thanks
+to YOKOTA Hiroshi (nsp_cs driver) and David Hinds (qlogic_cs driver) for
+the code fragments I shamelessly adapted for this work. Thanks also to
+Christoph Hellwig for his patient tutelage while I stumbled about.
+
+The Symbios Logic 53c500 chip was used in the "newer" (circa 1997) version
+of the New Media Bus Toaster PCMCIA SCSI controller. Presumably there are
+other products using this chip, but I've never laid eyes (much less hands)
+on one.
+
+Through the years, there have been a number of downloads of the pcmcia-cs
+version of this driver, and I guess it worked for those users. It worked
+for Tom Corner, and it works for me. Your mileage will probably vary.
+
+--Bob Tracy (rct@frus.com)
--- linux/Documentation/scsi/00-INDEX.orig 2004-04-25 14:45:54.000000000 -0500
+++ linux/Documentation/scsi/00-INDEX 2004-04-25 14:54:48.000000000 -0500
@@ -62,6 +62,8 @@
- info on API between SCSI layer and low level drivers
st.txt
- info on scsi tape driver
+sym53c500_cs.txt
+ - info on PCMCIA driver for Symbios Logic 53c500 based adapters
sym53c8xx_2.txt
- info on second generation driver for sym53c8xx based adapters
tmscsim.txt
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?)
2004-04-25 21:33 ` Russell King
@ 2004-04-25 22:59 ` Bob Tracy
0 siblings, 0 replies; 15+ messages in thread
From: Bob Tracy @ 2004-04-25 22:59 UTC (permalink / raw)
To: Russell King; +Cc: Christoph Hellwig, linux-kernel, linux-scsi
(Driver attempt 4 posted to linux-scsi a few minutes ago.)
Russell King wrote:
> On Sun, Apr 25, 2004 at 04:26:34PM -0500, Bob Tracy wrote:
> > Russell King wrote:
> > > Hmm, so what happens if you're in the middle of a transaction, and
> > > you receive a CS_EVENT_CARD_RESET. What happens to the command in
> > > progress ?
> >
> > Candidly, I don't know. A fair question to ask in return is, under
> > what circumstances might a PCMCIA driver see a CS_EVENT_CARD_RESET?
>
> When the user issues "cardctl reset"
Would it then be fair to characterize this action as either a self-
inflicted gunshot wound or a desperate attempt to restore things to
a sane state? I can see someone trying a reset if things got wedged
in the middle of a critical transaction, and I guess the actions that
could be taken by the low-level driver would properly depend on what
can be assumed with respect to the mid and upper layers. In the case
of the self-inflicted wound, the task at hand would be roughly
equivalent to ensuring the integrity of a floppy diskette when the
user ejects it from the drive in the middle of a write.
> From the brief look I had, it didn't look like (the mid and upper
> driver layers were handling the "command in progress" issue) - I
> suspect things will go gaga if someone ever invoked "cardctl reset".
Ouch! I hate to be like the doctor that suggests if something hurts
when you do it, then don't do it, but it sounds like the user is
already hurting if he's reaching for the "card reset" hammer. For a
sequential access device (magnetic tape and such), the user would
probably end up having to restart the job anyway (read or write). For
random access devices, I think the results would be minimally bad in
the case of an interrupted read. If the user is in the middle of a
write operation, it's probably fsck time (or enjoy your new coaster in
the case of one-shot writable media). Mitigating factors? If things
are truly wedged, the attached devices are probably in a pretty much
quiescent state anyway, implying any damage caused by a reset would be
minimal compared to what has already occurred.
Has this been discussed before? If so, I don't recall. Any ideas
as far as what *should* be done (by the drivers at any level)?
Resetting the host hardware is an obviously correct thing to do if
there's anything to be done in response to the card reset event. The
software state is a trickier matter: doing nothing might well be the
best option.
--
-----------------------------------------------------------------------
Bob Tracy WTO + WIPO = DMCA? http://www.anti-dmca.org
rct@frus.com
-----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2004-04-25 22:59 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-04-10 2:17 [PATCH] sym53c500_cs PCMCIA SCSI driver (new) Bob Tracy
2004-04-16 12:05 ` Christoph Hellwig
2004-04-16 14:17 ` Bob Tracy
2004-04-17 9:45 ` Christoph Hellwig
2004-04-20 16:11 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (second round) Bob Tracy
2004-04-20 16:24 ` Christoph Hellwig
2004-04-25 3:01 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (round 3 - the charm?) Bob Tracy
2004-04-25 7:36 ` Russell King
2004-04-25 21:26 ` Bob Tracy
2004-04-25 21:33 ` Russell King
2004-04-25 22:59 ` Bob Tracy
2004-04-25 14:34 ` Christoph Hellwig
2004-04-25 21:58 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (round 4) Bob Tracy
2004-04-22 19:38 ` [PATCH] sym53c500_cs PCMCIA SCSI driver (new) Bill Davidsen
2004-04-22 20:48 ` Bob Tracy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox