Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S261794AbUDJCSw (ORCPT ); Fri, 9 Apr 2004 22:18:52 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S261857AbUDJCSw (ORCPT ); Fri, 9 Apr 2004 22:18:52 -0400 Received: from gherkin.frus.com ([192.158.254.49]:50856 "EHLO gherkin.frus.com") by vger.kernel.org with ESMTP id S261794AbUDJCRH (ORCPT ); Fri, 9 Apr 2004 22:17:07 -0400 Subject: [PATCH] sym53c500_cs PCMCIA SCSI driver (new) To: linux-kernel@vger.kernel.org Date: Fri, 9 Apr 2004 21:17:03 -0500 (CDT) Cc: linux-scsi@vger.kernel.org X-Mailer: ELM [version 2.4ME+ PL82 (25)] MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=ELM767030686-2754-0_ Content-Transfer-Encoding: 7bit Message-Id: <20040410021703.946A9DBE3@gherkin.frus.com> From: rct@gherkin.frus.com (Bob Tracy) Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 40543 Lines: 1411 --ELM767030686-2754-0_ Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII 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 ----------------------------------------------------------------------- --ELM767030686-2754-0_ Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=US-ASCII Content-Disposition: attachment; filename=patch05_sym53c500 --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" + +#include +#include +#include +#include +#include +#include + +#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 "); +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" + +#include +#include +#include +#include +#include +#include + +#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 --ELM767030686-2754-0_-- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/