From abed9c87e1e13b6e6582f90206af423b9dc801ce Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 15 May 2003 17:08:20 +0100 Subject: [ARM] Fix SA1100_ir irqreturn_t. --- drivers/net/irda/sa1100_ir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index c95c70dd1f93..ce059524a7a2 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -636,13 +636,14 @@ static void sa1100_irda_fir_irq(struct net_device *dev) sa1100_irda_rx_dma_start(si); } -static void sa1100_irda_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t sa1100_irda_irq(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = dev_id; if (IS_FIR(((struct sa1100_irda *)dev->priv))) sa1100_irda_fir_irq(dev); else sa1100_irda_hpsir_irq(dev); + return IRQ_HANDLED; } /* -- cgit v1.2.3 From a4870a515eb164b3e0c056f1b051e872b283069f Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 15 May 2003 17:15:21 +0100 Subject: [ARM] Fix RiscPC i2c drivers for device model. These drivers got missed when the i2c subsystem was converted to the device model. --- drivers/acorn/char/i2c.c | 3 ++- drivers/acorn/char/pcf8583.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/acorn/char/i2c.c b/drivers/acorn/char/i2c.c index dc946403ac33..2328f7602295 100644 --- a/drivers/acorn/char/i2c.c +++ b/drivers/acorn/char/i2c.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -306,7 +307,7 @@ static struct i2c_adapter ioc_ops = { .id = I2C_HW_B_IOC, .algo_data = &ioc_data, .client_register = ioc_client_reg, - .client_unregister = ioc_client_unreg + .client_unregister = ioc_client_unreg, .dev = { .name = "IOC/IOMD", }, diff --git a/drivers/acorn/char/pcf8583.c b/drivers/acorn/char/pcf8583.c index 216d290606a9..6cf9e9259cef 100644 --- a/drivers/acorn/char/pcf8583.c +++ b/drivers/acorn/char/pcf8583.c @@ -34,7 +34,7 @@ static struct i2c_client_address_data addr_data = { .force = ignore, }; -#define DAT(x) ((unsigned int)(x->data)) +#define DAT(x) ((unsigned int)(x->dev.driver_data)) static int pcf8583_attach(struct i2c_adapter *adap, int addr, unsigned short flags, @@ -51,13 +51,13 @@ pcf8583_attach(struct i2c_adapter *adap, int addr, unsigned short flags, if (!c) return -ENOMEM; - strcpy(c->name, "PCF8583"); + strcpy(c->dev.name, "PCF8583"); c->id = pcf8583_driver.id; c->flags = 0; c->addr = addr; c->adapter = adap; c->driver = &pcf8583_driver; - c->data = NULL; + c->dev.driver_data = NULL; if (i2c_transfer(c->adapter, msgs, 2) == 2) DAT(c) = buf[0]; -- cgit v1.2.3 From bdf1ad7139c7baad4f47ac558dbb27aba39b93ab Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 16 May 2003 03:22:20 +0100 Subject: [ARM] Update Acorn platform scsi drivers. These were broken by two changes - the removal of the old device model class code, and when scsi device lists appeared. This cset allows these drivers to build again. We also drop some unnecessary code from one of the drivers. --- drivers/acorn/scsi/acornscsi.c | 27 +- drivers/acorn/scsi/acornscsi.h | 1 - drivers/acorn/scsi/arxescsi.c | 31 +-- drivers/acorn/scsi/cumana_1.c | 1 - drivers/acorn/scsi/cumana_2.c | 41 +--- drivers/acorn/scsi/eesox.c | 38 +-- drivers/acorn/scsi/fas216.c | 544 ++++++++++++++--------------------------- drivers/acorn/scsi/fas216.h | 9 +- drivers/acorn/scsi/oak.c | 1 - drivers/acorn/scsi/powertec.c | 32 +-- 10 files changed, 228 insertions(+), 497 deletions(-) diff --git a/drivers/acorn/scsi/acornscsi.c b/drivers/acorn/scsi/acornscsi.c index 01a293ca3bea..0275e8a47f55 100644 --- a/drivers/acorn/scsi/acornscsi.c +++ b/drivers/acorn/scsi/acornscsi.c @@ -2934,23 +2934,21 @@ int acornscsi_proc_info(char *buffer, char **start, off_t offset, p += sprintf(p, "\nAttached devices:\n"); list_for_each_entry(scd, &instance->my_devices, siblings) { - int len; - - proc_print_scsidevice(scd, p, &len, 0); - p += len; - - p += sprintf(p, "Extensions: "); - + p += sprintf(p, "Device/Lun TaggedQ Sync\n"); + p += sprintf(p, " %d/%d ", scd->id, scd->lun); if (scd->tagged_supported) - p += sprintf(p, "TAG %sabled [%d] ", - scd->tagged_queue ? "en" : "dis", scd->current_tag); - p += sprintf(p, "\nTransfers: "); + p += sprintf(p, "%3sabled(%3d) ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + else + p += sprintf(p, "unsupported "); + if (host->device[scd->id].sync_xfer & 15) - p += sprintf(p, "sync, offset %d, %d ns\n", - host->device[scd->id].sync_xfer & 15, - acornscsi_getperiod(host->device[scd->id].sync_xfer)); + p += sprintf(p, "offset %d, %d ns\n", + host->device[scd->id].sync_xfer & 15, + acornscsi_getperiod(host->device[scd->id].sync_xfer)); else - p += sprintf(p, "async\n"); + p += sprintf(p, "async\n"); pos = p - buffer; if (pos + begin < offset) { @@ -3106,7 +3104,6 @@ static struct ecard_driver acornscsi_driver = { .remove = __devexit_p(acornscsi_remove), .id_table = acornscsi_cids, .drv = { - .devclass = &shost_devclass, .name = "acornscsi", }, }; diff --git a/drivers/acorn/scsi/acornscsi.h b/drivers/acorn/scsi/acornscsi.h index 457929b24925..8e4d88d0aba3 100644 --- a/drivers/acorn/scsi/acornscsi.h +++ b/drivers/acorn/scsi/acornscsi.h @@ -298,7 +298,6 @@ typedef struct acornscsi_hostdata { unsigned short last_message; /* last message to be sent */ unsigned char disconnectable:1; /* this command can be disconnected */ - unsigned char interrupt:1; /* interrupt active */ } scsi; /* statistics information */ diff --git a/drivers/acorn/scsi/arxescsi.c b/drivers/acorn/scsi/arxescsi.c index 5e1606280dc7..f46a4c822475 100644 --- a/drivers/acorn/scsi/arxescsi.c +++ b/drivers/acorn/scsi/arxescsi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -238,10 +239,10 @@ static int arxescsi_proc_info(char *buffer, char **start, off_t offset, int length, int host_no, int inout) { - int pos, begin; struct Scsi_Host *host; struct arxescsi_info *info; - Scsi_Device *scd; + char *p = buffer; + int pos; host = scsi_host_hn_get(host_no); if (!host) @@ -251,26 +252,13 @@ arxescsi_proc_info(char *buffer, char **start, off_t offset, int length, if (inout == 1) return -EINVAL; - begin = 0; - pos = sprintf(buffer, "ARXE 16-bit SCSI driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += fas216_print_stats(&info->info, buffer + pos); + p += sprintf(p, "ARXE 16-bit SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); - pos += sprintf (buffer+pos, "\nAttached devices:\n"); - - list_for_each_entry(scd, &host->my_devices, siblings) { - pos += fas216_print_device(&info->info, scd, buffer + pos); - - if (pos + begin < offset) { - begin += pos; - pos = 0; - } - if (pos + begin > offset + length) - break; - } - - *start = buffer + (offset - begin); - pos -= offset - begin; + *start = buffer + offset; + pos = p - buffer - offset; if (pos > length) pos = length; @@ -397,7 +385,6 @@ static struct ecard_driver arxescsi_driver = { .remove = __devexit_p(arxescsi_remove), .id_table = arxescsi_cids, .drv = { - .devclass = &shost_devclass, .name = "arxescsi", }, }; diff --git a/drivers/acorn/scsi/cumana_1.c b/drivers/acorn/scsi/cumana_1.c index 9ccaee561d5e..222c8fc88b56 100644 --- a/drivers/acorn/scsi/cumana_1.c +++ b/drivers/acorn/scsi/cumana_1.c @@ -334,7 +334,6 @@ static struct ecard_driver cumanascsi1_driver = { .remove = __devexit_p(cumanascsi1_remove), .id_table = cumanascsi1_cids, .drv = { - .devclass = &shost_devclass, .name = "cumanascsi1", }, }; diff --git a/drivers/acorn/scsi/cumana_2.c b/drivers/acorn/scsi/cumana_2.c index 5afff2f926ad..e03cc94161ff 100644 --- a/drivers/acorn/scsi/cumana_2.c +++ b/drivers/acorn/scsi/cumana_2.c @@ -356,10 +356,10 @@ cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset, int length, int host_no, int inout) { - int pos, begin; struct Scsi_Host *host; struct cumanascsi2_info *info; - Scsi_Device *scd; + char *p = buffer; + int pos; host = scsi_host_hn_get(host_no); if (!host) @@ -370,38 +370,16 @@ int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset, info = (struct cumanascsi2_info *)host->hostdata; - begin = 0; - pos = sprintf(buffer, "Cumana SCSI II driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += sprintf(buffer + pos, "Term : o%s\n", + p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", info->terms ? "n" : "ff"); - pos += fas216_print_stats(&info->info, buffer + pos); + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); - pos += sprintf(buffer+pos, "\nAttached devices:\n"); - - list_for_each_entry(scd, &host->my_devices, siblings) { - int len; - - proc_print_scsidevice(scd, buffer, &len, pos); - pos += len; - pos += sprintf(buffer+pos, "Extensions: "); - if (scd->tagged_supported) - pos += sprintf(buffer+pos, "TAG %sabled [%d] ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); - pos += sprintf(buffer+pos, "\n"); - - if (pos + begin < offset) { - begin += pos; - pos = 0; - } - if (pos + begin > offset + length) - break; - } - - *start = buffer + (offset - begin); - pos -= offset - begin; + *start = buffer + offset; + pos = p - buffer - offset; if (pos > length) pos = length; @@ -572,7 +550,6 @@ static struct ecard_driver cumanascsi2_driver = { .remove = __devexit_p(cumanascsi2_remove), .id_table = cumanascsi2_cids, .drv = { - .devclass = &shost_devclass, .name = "cumanascsi2", }, }; diff --git a/drivers/acorn/scsi/eesox.c b/drivers/acorn/scsi/eesox.c index 9496944bca18..11edbfb58195 100644 --- a/drivers/acorn/scsi/eesox.c +++ b/drivers/acorn/scsi/eesox.c @@ -430,10 +430,10 @@ eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, int length, int host_no, int inout) { - int pos, begin; struct Scsi_Host *host; struct eesoxscsi_info *info; - Scsi_Device *scd; + char *p = buffer; + int pos; host = scsi_host_hn_get(host_no); if (!host) @@ -444,35 +444,16 @@ int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, info = (struct eesoxscsi_info *)host->hostdata; - begin = 0; - pos = sprintf(buffer, "EESOX SCSI driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += sprintf(buffer + pos, "Term : o%s\n", + p += sprintf(p, "EESOX SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", info->control & EESOX_TERM_ENABLE ? "n" : "ff"); - pos += fas216_print_stats(&info->info, buffer + pos); + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); - pos += sprintf(buffer+pos, "\nAttached devices:\n"); - - list_for_each_entry(scd, &host->my_devices, siblings) { - int len; - - proc_print_scsidevice(scd, buffer, &len, pos); - pos += len; - pos += sprintf(buffer+pos, "Extensions: "); - if (scd->tagged_supported) - pos += sprintf(buffer+pos, "TAG %sabled [%d] ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); - pos += sprintf (buffer+pos, "\n"); - - if (pos + begin < offset) { - begin += pos; - pos = 0; - } - } - *start = buffer + (offset - begin); - pos -= offset - begin; + *start = buffer + offset; + pos = p - buffer - offset; if (pos > length) pos = length; @@ -679,7 +660,6 @@ static struct ecard_driver eesoxscsi_driver = { .remove = __devexit_p(eesoxscsi_remove), .id_table = eesoxscsi_cids, .drv = { - .devclass = &shost_devclass, .name = "eesoxscsi", }, }; diff --git a/drivers/acorn/scsi/fas216.c b/drivers/acorn/scsi/fas216.c index c1b2f360b142..c90d20a99496 100644 --- a/drivers/acorn/scsi/fas216.c +++ b/drivers/acorn/scsi/fas216.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -79,7 +80,6 @@ * I was thinking that this was a good chip until I found this restriction ;( */ #define SCSI2_SYNC -#undef SCSI2_WIDE #undef SCSI2_TAG #undef DEBUG_CONNECT @@ -96,6 +96,8 @@ static int level_mask = LOG_ERROR; +MODULE_PARM(level_mask, "i"); + static int __init fas216_log_setup(char *str) { char *s; @@ -199,10 +201,8 @@ static void fas216_dumpinfo(FAS216_Info *info) info->scsi.io_port, info->scsi.io_shift, info->scsi.irq, info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2], info->scsi.cfg[3]); - printk(" type=%p phase=%X reconnected={ target=%d lun=%d tag=%d }\n", - info->scsi.type, info->scsi.phase, - info->scsi.reconnected.target, - info->scsi.reconnected.lun, info->scsi.reconnected.tag); + printk(" type=%p phase=%X\n", + info->scsi.type, info->scsi.phase); print_SCp(&info->scsi.SCp, " SCp={ ", " }\n"); printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", info->scsi.async_stp, @@ -265,21 +265,24 @@ static const char *fas216_bus_phase(int stat) static const char *fas216_drv_phase(FAS216_Info *info) { - switch (info->scsi.phase) { - case PHASE_IDLE: return "idle"; - case PHASE_SELECTION: return "selection"; - case PHASE_COMMAND: return "command"; - case PHASE_RECONNECTED: return "reconnected"; - case PHASE_DATAOUT: return "data out"; - case PHASE_DATAIN: return "data in"; - case PHASE_MSGIN: return "message in"; - case PHASE_MSGIN_DISCONNECT: return "disconnect"; - case PHASE_MSGOUT_EXPECT: return "expect message out"; - case PHASE_MSGOUT: return "message out"; - case PHASE_STATUS: return "status"; - case PHASE_DONE: return "done"; - default: return "???"; - } + static const char *phases[] = { + [PHASE_IDLE] = "idle", + [PHASE_SELECTION] = "selection", + [PHASE_COMMAND] = "command", + [PHASE_DATAOUT] = "data out", + [PHASE_DATAIN] = "data in", + [PHASE_MSGIN] = "message in", + [PHASE_MSGIN_DISCONNECT]= "disconnect", + [PHASE_MSGOUT_EXPECT] = "expect message out", + [PHASE_MSGOUT] = "message out", + [PHASE_STATUS] = "status", + [PHASE_DONE] = "done", + }; + + if (info->scsi.phase < ARRAY_SIZE(phases) && + phases[info->scsi.phase]) + return phases[info->scsi.phase]; + return "???"; } static char fas216_target(FAS216_Info *info) @@ -406,25 +409,6 @@ static void print_debug_list(void) static void fas216_done(FAS216_Info *info, unsigned int result); -/** - * fas216_clockrate - calculate clock conversion factor - * @clock: clock speed in MHz - * - * Calculate correct value to be written into clock conversion factor - * register. Returns CLKF_ value. - */ -static int fas216_clockrate(int clock) -{ - if (clock <= 10 || clock > 40) { - printk(KERN_CRIT - "fas216: invalid clock rate: check your driver!\n"); - clock = -1; - } else - clock = ((clock - 1) / 5 + 1) & 7; - - return clock; -} - /** * fas216_get_last_msg - retrive last message from the list * @info: interface to search @@ -640,105 +624,6 @@ static void fas216_handlesync(FAS216_Info *info, char *msg) } } -/** - * fas216_handlewide - Handle a wide transfer message - * @info: state structure for interface - * @msg: message from target - * - * Handle a wide transfer message from the target - */ -static void fas216_handlewide(FAS216_Info *info, char *msg) -{ - struct fas216_device *dev = &info->device[info->SCpnt->device->id]; - enum { wide, bit8, none, reject } res = none; - -#ifdef SCSI2_WIDE - switch (msg[0]) { - case MESSAGE_REJECT: - /* Wide transfer request failed. - * Note: SCSI II r10: - * - * SCSI devices that are capable of wide - * data transfers shall not respond to a - * WDTR message with a MESSAGE REJECT message. - * - * Hence, if we get this condition, we never - * reattempt negotiation for this device. - */ - if (dev->wide_state == neg_inprogress) { - dev->wide_state = neg_invalid; - res = bit8; - } - break; - - case EXTENDED_MESSAGE: - switch (dev->wide_state) { - /* We don't accept wide data transfer requests. - * Respond with a MESSAGE REJECT to prevent a - * wide data transfer agreement from being reached. - */ - case neg_invalid: - res = reject; - break; - - /* We were not negotiating a wide data transfer, - * but the device sent is a negotiation request. - * Honour the request by sending back a WDTR - * message containing our capability, limited by - * the targets capability. - */ - default: - fas216_cmd(info, CMD_SETATN); - if (msg[3] > info->ifcfg.wide_max_size) - msg[3] = info->ifcfg.wide_max_size; - - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 4, - EXTENDED_MESSAGE, 2, EXTENDED_WDTR, - msg[3]); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - res = wide; - break; - - /* We initiated the wide data transfer negotiation, - * and have successfully received a response from the - * target. The synchronous transfer agreement has been - * reached. Note: if the values returned are out of our - * bounds, we must reject the message. - */ - case neg_inprogress: - res = reject; - if (msg[3] <= info->ifcfg.wide_max_size) { - dev->wide_state = neg_complete; - res = wide; - } - break; - } - } -#else - res = reject; -#endif - - switch (res) { - case wide: - dev->wide_xfer = msg[3]; - break; - - case reject: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - - case bit8: - dev->wide_xfer = 0; - break; - - case none: - break; - } -} - /** * fas216_updateptrs - update data pointers after transfer suspended/paused * @info: interface's local pointer to update @@ -993,6 +878,8 @@ static void fas216_aborttransfer(FAS216_Info *info) fas216_cmd(info, CMD_FLUSHFIFO); } +static void fas216_kick(FAS216_Info *info); + /** * fas216_disconnected_intr - handle device disconnection * @info: interface from which device disconnected from @@ -1001,6 +888,8 @@ static void fas216_aborttransfer(FAS216_Info *info) */ static void fas216_disconnect_intr(FAS216_Info *info) { + unsigned long flags; + fas216_checkmagic(info); fas216_log(info, LOG_CONNECT, "disconnect phase=%02x", @@ -1008,8 +897,6 @@ static void fas216_disconnect_intr(FAS216_Info *info) msgqueue_flush(&info->scsi.msgs); - fas216_cmd(info, CMD_ENABLESEL); - switch (info->scsi.phase) { case PHASE_SELECTION: /* while selecting - no target */ case PHASE_SELSTEPS: @@ -1018,9 +905,12 @@ static void fas216_disconnect_intr(FAS216_Info *info) case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ info->scsi.disconnectable = 1; - info->scsi.reconnected.tag = 0; info->scsi.phase = PHASE_IDLE; info->stats.disconnects += 1; + spin_lock_irqsave(&info->host_lock, flags); + if (info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + spin_unlock_irqrestore(&info->host_lock, flags); break; case PHASE_DONE: /* at end of command - complete */ @@ -1053,122 +943,61 @@ static void fas216_disconnect_intr(FAS216_Info *info) static void fas216_reselected_intr(FAS216_Info *info) { - unsigned char target, identify_msg, ok; + unsigned int cfis, i; + unsigned char msg[4]; + unsigned char target, lun, tag; fas216_checkmagic(info); - if ((info->scsi.phase == PHASE_SELECTION || - info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) { - Scsi_Cmnd *SCpnt = info->SCpnt; + WARN_ON(info->scsi.phase == PHASE_SELECTION || + info->scsi.phase == PHASE_SELSTEPS); - info->origSCpnt = SCpnt; - info->SCpnt = NULL; + cfis = fas216_readb(info, REG_CFIS); - if (info->device[SCpnt->device->id].wide_state == neg_inprogress) - info->device[SCpnt->device->id].wide_state = neg_wait; - if (info->device[SCpnt->device->id].sync_state == neg_inprogress) - info->device[SCpnt->device->id].sync_state = neg_wait; - } + fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x", + info->scsi.phase, cfis); - fas216_log(info, LOG_CONNECT, "reconnect phase=%02X", info->scsi.phase); + cfis &= CFIS_CF; - if ((fas216_readb(info, REG_CFIS) & CFIS_CF) != 2) { + if (cfis < 2 || cfis > 4) { printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", info->host->host_no); - goto initiator_error; - } - - target = fas216_readb(info, REG_FF); - identify_msg = fas216_readb(info, REG_FF); - - ok = 1; - if (!(target & (1 << info->host->this_id))) { - printk(KERN_ERR "scsi%d.H: invalid host id on reselect\n", info->host->host_no); - ok = 0; + goto bad_message; } - if (!(identify_msg & 0x80)) { - printk(KERN_ERR "scsi%d.H: no IDENTIFY message on reselect, got msg %02X\n", - info->host->host_no, identify_msg); - ok = 0; - } + for (i = 0; i < cfis; i++) + msg[i] = fas216_readb(info, REG_FF); - if (!ok) { - /* - * Something went wrong - send an initiator error to - * the target. - */ + if (!(msg[0] & (1 << info->host->this_id)) || + !(msg[1] & 0x80)) goto initiator_error; - } - target &= ~(1 << info->host->this_id); + target = msg[0] & ~(1 << info->host->this_id); target = ffs(target) - 1; + lun = msg[1] & 7; + tag = 0; - identify_msg &= 7; - info->scsi.reconnected.target = target; - info->scsi.reconnected.lun = identify_msg; - info->scsi.reconnected.tag = 0; + if (cfis >= 3) { + if (msg[2] != SIMPLE_QUEUE_TAG) + goto initiator_error; - /* set up for synchronous transfers */ - fas216_set_sync(info, target); - - ok = 0; - if (info->scsi.disconnectable && info->SCpnt && - info->SCpnt->device->id == target && info->SCpnt->device->lun == identify_msg) - ok = 1; - - if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg)) - ok = 1; - - msgqueue_flush(&info->scsi.msgs); - if (ok) { - info->scsi.phase = PHASE_RECONNECTED; - fas216_writeb(info, REG_SDID, target); - } else { - /* - * Our command structure not found - abort the - * command on the target. Since we have no - * record of this command, we can't send - * an INITIATOR DETECTED ERROR message. - */ - fas216_cmd(info, CMD_SETATN); - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; + tag = msg[3]; } - fas216_cmd(info, CMD_MSGACCEPTED); - return; - - initiator_error: - fas216_cmd(info, CMD_SETATN); + /* set up for synchronous transfers */ + fas216_writeb(info, REG_SDID, target); + fas216_set_sync(info, target); msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - fas216_cmd(info, CMD_MSGACCEPTED); -} - -/** - * fas216_finish_reconnect - finish reconnection sequence for device - * @info: interface which caused function done interrupt - * - * Finish reconnection sequence for device - */ -static void -fas216_finish_reconnect(FAS216_Info *info) -{ - fas216_checkmagic(info); - fas216_log(info, LOG_CONNECT, "Connected: %1x %1x %02x, reconnected: %1x %1x %02x", - info->SCpnt->device->id, info->SCpnt->device->lun, info->SCpnt->tag, - info->scsi.reconnected.target, info->scsi.reconnected.lun, - info->scsi.reconnected.tag); + fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x", + target, lun, tag); if (info->scsi.disconnectable && info->SCpnt) { info->scsi.disconnectable = 0; - if (info->SCpnt->device->id == info->scsi.reconnected.target && - info->SCpnt->device->lun == info->scsi.reconnected.lun && - info->SCpnt->tag == info->scsi.reconnected.tag) { - fas216_log(info, LOG_CONNECT, "reconnected"); + if (info->SCpnt->device->id == target && + info->SCpnt->device->lun == lun && + info->SCpnt->tag == tag) { + fas216_log(info, LOG_CONNECT, "reconnected previously executing command"); } else { queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue"); @@ -1177,32 +1006,53 @@ fas216_finish_reconnect(FAS216_Info *info) } if (!info->SCpnt) { info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected, - info->scsi.reconnected.target, - info->scsi.reconnected.lun, - info->scsi.reconnected.tag); + target, lun, tag); fas216_log(info, LOG_CONNECT, "had to get command"); } - if (!info->SCpnt) { + + if (info->SCpnt) { + /* + * Restore data pointer from SAVED data pointer + */ + info->scsi.SCp = info->SCpnt->SCp; + + fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + info->scsi.phase = PHASE_MSGIN; + } else { + /* + * Our command structure not found - abort the + * command on the target. Since we have no + * record of this command, we can't send + * an INITIATOR DETECTED ERROR message. + */ fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); #if 0 - if (info->scsi.reconnected.tag) - msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag); + if (tag) + msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, tag); else #endif msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; info->scsi.aborting = 1; - } else { - /* - * Restore data pointer from SAVED data pointer - */ - info->scsi.SCp = info->SCpnt->SCp; - fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); } + + fas216_cmd(info, CMD_MSGACCEPTED); + return; + + initiator_error: + printk(KERN_ERR "scsi%d.H: error during reselection: bytes", + info->host->host_no); + for (i = 0; i < cfis; i++) + printk(" %02x", msg[i]); + printk("\n"); + bad_message: + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + fas216_cmd(info, CMD_MSGACCEPTED); } static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen) @@ -1267,10 +1117,6 @@ static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int fas216_handlesync(info, message); break; - case EXTENDED_MESSAGE | EXTENDED_WDTR << 8: - fas216_handlewide(info, message); - break; - default: fas216_log(info, 0, "reject, last message 0x%04x", fas216_get_last_msg(info, info->scsi.msgin_fifo)); @@ -1280,14 +1126,6 @@ static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int case NOP: break; - case SIMPLE_QUEUE_TAG: - if (msglen < 2) - goto unrecognised; - - /* handled above - print a warning since this is untested */ - fas216_log(info, 0, "reconnect queue tag 0x%02x", message[1]); - break; - case EXTENDED_MESSAGE: if (msglen < 3) goto unrecognised; @@ -1297,10 +1135,6 @@ static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int fas216_handlesync(info, message); break; - case EXTENDED_WDTR: /* Wide transfer negotiation request/reply */ - fas216_handlewide(info, message); - break; - default: goto unrecognised; } @@ -1437,13 +1271,6 @@ static void fas216_message(FAS216_Info *info) } #endif - if (info->scsi.phase == PHASE_RECONNECTED) { - if (message[0] == SIMPLE_QUEUE_TAG) - info->scsi.reconnected.tag = message[1]; - fas216_finish_reconnect(info); - info->scsi.phase = PHASE_MSGIN; - } - fas216_parse_message(info, message, msglen); fas216_cmd(info, CMD_MSGACCEPTED); return; @@ -1521,26 +1348,26 @@ static void fas216_send_messageout(FAS216_Info *info, int start) * fas216_busservice_intr - handle bus service interrupt from FAS216 chip * @info: interface which caused bus service interrupt * @stat: Status register contents - * @ssr: SCSI Status register contents + * @is: SCSI Status register contents * * Handle a bus service interrupt from FAS216 chip */ -static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) +static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int is) { fas216_checkmagic(info); fas216_log(info, LOG_BUSSERVICE, - "bus service: stat=%02x ssr=%02x phase=%02x", - stat, ssr, info->scsi.phase); + "bus service: stat=%02x is=%02x phase=%02x", + stat, is, info->scsi.phase); switch (info->scsi.phase) { case PHASE_SELECTION: - if ((ssr & IS_BITS) != IS_MSGBYTESENT) + if ((is & IS_BITS) != IS_MSGBYTESENT) goto bad_is; break; case PHASE_SELSTEPS: - switch (ssr & IS_BITS) { + switch (is & IS_BITS) { case IS_SELARB: case IS_MSGBYTESENT: goto bad_is; @@ -1566,9 +1393,6 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne * as described by the SCSI II spec. */ switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { - /* Reselmsgin -> Data In */ - case STATE(STAT_DATAIN, PHASE_RECONNECTED): - fas216_finish_reconnect(info); case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ @@ -1583,9 +1407,6 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne fas216_transfer(info); return; - /* Reselmsgin -> Data Out */ - case STATE(STAT_DATAOUT, PHASE_RECONNECTED): - fas216_finish_reconnect(info); case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ @@ -1595,10 +1416,6 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne fas216_transfer(info); return; - /* Reselmsgin -> Status */ - case STATE(STAT_STATUS, PHASE_RECONNECTED): - fas216_finish_reconnect(info); - goto status; case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ fas216_stoptransfer(info); @@ -1606,7 +1423,6 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ - status: fas216_cmd(info, CMD_INITCMDCOMPLETE); info->scsi.phase = PHASE_STATUS; return; @@ -1623,16 +1439,11 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne info->scsi.phase = PHASE_MSGIN; return; - /* Reselmsgin -> Message In */ - case STATE(STAT_MESGIN, PHASE_RECONNECTED): case STATE(STAT_MESGIN, PHASE_MSGIN): info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; fas216_cmd(info, CMD_TRANSFERINFO); return; - /* Reselmsgin -> Command */ - case STATE(STAT_COMMAND, PHASE_RECONNECTED): - fas216_finish_reconnect(info); case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ fas216_send_command(info); @@ -1722,7 +1533,7 @@ static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigne return; bad_is: - fas216_log(info, 0, "bus service at step %d?", ssr & IS_BITS); + fas216_log(info, 0, "bus service at step %d?", is & IS_BITS); fas216_dumpstate(info); print_debug_list(); @@ -1733,37 +1544,40 @@ bad_is: * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip * @info: interface which caused function done interrupt * @stat: Status register contents - * @ssr: SCSI Status register contents + * @is: SCSI Status register contents * * Handle a function done interrupt from FAS216 chip */ -static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) +static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is) { unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF; - unsigned int status, message; fas216_checkmagic(info); fas216_log(info, LOG_FUNCTIONDONE, - "function done: stat=%02x ssr=%02x phase=%02x", - stat, ssr, info->scsi.phase); + "function done: stat=%02x is=%02x phase=%02x", + stat, is, info->scsi.phase); switch (info->scsi.phase) { case PHASE_STATUS: /* status phase - read status and msg */ if (fifo_len != 2) { fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len); } - status = fas216_readb(info, REG_FF); - message = fas216_readb(info, REG_FF); - info->scsi.SCp.Message = message; - info->scsi.SCp.Status = status; + /* + * Read status then message byte. + */ + info->scsi.SCp.Status = fas216_readb(info, REG_FF); + info->scsi.SCp.Message = fas216_readb(info, REG_FF); info->scsi.phase = PHASE_DONE; fas216_cmd(info, CMD_MSGACCEPTED); break; - case PHASE_IDLE: /* reselected? */ + case PHASE_IDLE: + case PHASE_SELECTION: + case PHASE_SELSTEPS: + break; + case PHASE_MSGIN: /* message in phase */ - case PHASE_RECONNECTED: /* reconnected command */ if ((stat & STAT_BUSMASK) == STAT_MESGIN) { info->scsi.msgin_fifo = fifo_len; fas216_message(info); @@ -1779,22 +1593,13 @@ static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned static void fas216_bus_reset(FAS216_Info *info) { - neg_t sync_state, wide_state; + neg_t sync_state; int i; msgqueue_flush(&info->scsi.msgs); - info->scsi.reconnected.target = 0; - info->scsi.reconnected.lun = 0; - info->scsi.reconnected.tag = 0; - - wide_state = neg_invalid; sync_state = neg_invalid; -#ifdef SCSI2_WIDE - if (info->ifcfg.wide_max_size != 0) - wide_state = neg_wait; -#endif #ifdef SCSI2_SYNC if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA)) sync_state = neg_wait; @@ -1807,7 +1612,6 @@ static void fas216_bus_reset(FAS216_Info *info) for (i = 0; i < 8; i++) { info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; info->device[i].sync_state = sync_state; - info->device[i].wide_state = wide_state; info->device[i].period = info->ifcfg.asyncperiod / 4; info->device[i].stp = info->scsi.async_stp; info->device[i].sof = 0; @@ -1826,38 +1630,38 @@ static void fas216_bus_reset(FAS216_Info *info) */ irqreturn_t fas216_intr(FAS216_Info *info) { - unsigned char isr, ssr, stat; + unsigned char inst, is, stat; int handled = IRQ_NONE; fas216_checkmagic(info); stat = fas216_readb(info, REG_STAT); - ssr = fas216_readb(info, REG_IS); - isr = fas216_readb(info, REG_INST); + is = fas216_readb(info, REG_IS); + inst = fas216_readb(info, REG_INST); - add_debug_list(stat, ssr, isr, info->scsi.phase); + add_debug_list(stat, is, inst, info->scsi.phase); if (stat & STAT_INT) { - if (isr & INST_BUSRESET) { + if (inst & INST_BUSRESET) { fas216_log(info, 0, "bus reset detected"); fas216_bus_reset(info); scsi_report_bus_reset(info->host, 0); - } else if (isr & INST_ILLEGALCMD) { + } else if (inst & INST_ILLEGALCMD) { fas216_log(info, LOG_ERROR, "illegal command given\n"); fas216_dumpstate(info); print_debug_list(); - } else if (isr & INST_DISCONNECT) + } else if (inst & INST_DISCONNECT) fas216_disconnect_intr(info); - else if (isr & INST_RESELECTED) /* reselected */ + else if (inst & INST_RESELECTED) /* reselected */ fas216_reselected_intr(info); - else if (isr & INST_BUSSERVICE) /* bus service request */ - fas216_busservice_intr(info, stat, ssr); - else if (isr & INST_FUNCDONE) /* function done */ - fas216_funcdone_intr(info, stat, ssr); + else if (inst & INST_BUSSERVICE) /* bus service request */ + fas216_busservice_intr(info, stat, is); + else if (inst & INST_FUNCDONE) /* function done */ + fas216_funcdone_intr(info, stat, is); else fas216_log(info, 0, "unknown interrupt received:" - " phase %s isr %02X ssr %02X stat %02X", - fas216_drv_phase(info), isr, ssr, stat); + " phase %s inst %02X is %02X stat %02X", + fas216_drv_phase(info), inst, is, stat); handled = IRQ_HANDLED; } return handled; @@ -1948,7 +1752,7 @@ static int parity_test(FAS216_Info *info, int target) { #if 0 if (target == 3) { - info->device[3].parity_check = 0; + info->device[target].parity_check = 0; return 1; } #endif @@ -1991,15 +1795,6 @@ static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); do { -#ifdef SCSI2_WIDE - if (info->device[SCpnt->device->id].wide_state == neg_wait) { - info->device[SCpnt->device->id].wide_state = neg_inprogress; - msgqueue_addmsg(&info->scsi.msgs, 4, - EXTENDED_MESSAGE, 2, EXTENDED_WDTR, - info->ifcfg.wide_max_size); - break; - } -#endif #ifdef SCSI2_SYNC if ((info->device[SCpnt->device->id].sync_state == neg_wait || info->device[SCpnt->device->id].sync_state == neg_complete) && @@ -2140,8 +1935,18 @@ static void fas216_kick(FAS216_Info *info) } } while (0); - if (!SCpnt) /* no command pending - just exit */ + if (!SCpnt) { + /* + * no command pending, so enable reselection. + */ + fas216_cmd(info, CMD_ENABLESEL); return; + } + + /* + * We're going to start a command, so disable reselection + */ + fas216_cmd(info, CMD_DISABLESEL); if (info->scsi.disconnectable && info->SCpnt) { fas216_log(info, LOG_CONNECT, @@ -2804,6 +2609,8 @@ int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) clear_bit(SDpnt->id * 8 + i, info->busyluns); } + info->scsi.phase = PHASE_IDLE; + /* * Reset the SCSI bus. Device cleanup happens in * the interrupt handler. @@ -2833,7 +2640,8 @@ int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) */ static void fas216_init_chip(FAS216_Info *info) { - fas216_writeb(info, REG_CLKF, fas216_clockrate(info->ifcfg.clockrate)); + unsigned int clock = ((info->ifcfg.clockrate - 1) / 5 + 1) & 7; + fas216_writeb(info, REG_CLKF, clock); fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]); fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); @@ -3042,7 +2850,7 @@ int fas216_init(struct Scsi_Host *host) info->scsi.cfg[0] = host->this_id | CNTL1_PERE; info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE; info->scsi.cfg[2] = info->ifcfg.cntl3 | - CNTL3_ADIDCHK | CNTL3_G2CB | CNTL3_LBTM; + CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM; info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); info->rst_dev_status = -1; @@ -3082,6 +2890,12 @@ int fas216_add(struct Scsi_Host *host, struct device *dev) FAS216_Info *info = (FAS216_Info *)host->hostdata; int type, ret; + if (info->ifcfg.clockrate <= 10 || info->ifcfg.clockrate > 40) { + printk(KERN_CRIT "fas216: invalid clock rate %u MHz\n", + info->ifcfg.clockrate); + return -EINVAL; + } + fas216_reset_state(info); type = fas216_detect_type(info); info->scsi.type = chip_types[type]; @@ -3181,32 +2995,32 @@ int fas216_print_stats(FAS216_Info *info, char *buffer) return p - buffer; } -int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer) +int fas216_print_devices(FAS216_Info *info, char *buffer) { - struct fas216_device *dev = &info->device[scd->id]; - int len = 0; - char *p; - - proc_print_scsidevice(scd, buffer, &len, 0); - p = buffer + len; - - p += sprintf(p, " Extensions: "); + struct fas216_device *dev; + Scsi_Device *scd; + char *p = buffer; - if (scd->tagged_supported) - p += sprintf(p, "TAG %sabled [%d] ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); + p += sprintf(p, "Device/Lun TaggedQ Parity Sync\n"); - p += sprintf(p, "%s\n", dev->parity_enabled ? "parity" : ""); + list_for_each_entry(scd, &info->host->my_devices, siblings) { + dev = &info->device[scd->id]; + p += sprintf(p, " %d/%d ", scd->id, scd->lun); + if (scd->tagged_supported) + p += sprintf(p, "%3sabled(%3d) ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + else + p += sprintf(p, "unsupported "); - p += sprintf(p, " Transfers : %d-bit ", - 8 << dev->wide_xfer); + p += sprintf(p, "%3sabled ", dev->parity_enabled ? "en" : "dis"); - if (dev->sof) - p += sprintf(p, "sync offset %d, %d ns\n", - dev->sof, dev->period * 4); - else - p += sprintf(p, "async\n"); + if (dev->sof) + p += sprintf(p, "offset %d, %d ns\n", + dev->sof, dev->period * 4); + else + p += sprintf(p, "async\n"); + } return p - buffer; } @@ -3224,7 +3038,7 @@ EXPORT_SYMBOL(fas216_eh_bus_reset); EXPORT_SYMBOL(fas216_eh_host_reset); EXPORT_SYMBOL(fas216_print_host); EXPORT_SYMBOL(fas216_print_stats); -EXPORT_SYMBOL(fas216_print_device); +EXPORT_SYMBOL(fas216_print_devices); MODULE_AUTHOR("Russell King"); MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core"); diff --git a/drivers/acorn/scsi/fas216.h b/drivers/acorn/scsi/fas216.h index e1ba80b30281..d6b6322f9d53 100644 --- a/drivers/acorn/scsi/fas216.h +++ b/drivers/acorn/scsi/fas216.h @@ -177,7 +177,6 @@ typedef enum { PHASE_SELSTEPS, /* selection with command steps */ PHASE_COMMAND, /* command sent */ PHASE_MESSAGESENT, /* selected, and we're sending cmd */ - PHASE_RECONNECTED, /* reconnected */ PHASE_DATAOUT, /* data out to device */ PHASE_DATAIN, /* data in from device */ PHASE_MSGIN, /* message in from device */ @@ -244,12 +243,6 @@ typedef struct { const char *type; /* chip type */ unsigned int irq; /* interrupt */ - struct { - unsigned char target; /* reconnected target */ - unsigned char lun; /* reconnected lun */ - unsigned char tag; /* reconnected tag */ - } reconnected; - Scsi_Pointer SCp; /* current commands data pointer */ MsgQueue_t msgs; /* message queue for connected device */ @@ -368,7 +361,7 @@ extern void fas216_release (struct Scsi_Host *instance); extern int fas216_print_host(FAS216_Info *info, char *buffer); extern int fas216_print_stats(FAS216_Info *info, char *buffer); -extern int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer); +extern int fas216_print_devices(FAS216_Info *info, char *buffer); /* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) * Purpose : abort this command diff --git a/drivers/acorn/scsi/oak.c b/drivers/acorn/scsi/oak.c index 0246942f9345..6afc155ea856 100644 --- a/drivers/acorn/scsi/oak.c +++ b/drivers/acorn/scsi/oak.c @@ -192,7 +192,6 @@ static struct ecard_driver oakscsi_driver = { .remove = __devexit_p(oakscsi_remove), .id_table = oakscsi_cids, .drv = { - .devclass = &shost_devclass, .name = "oakscsi", }, }; diff --git a/drivers/acorn/scsi/powertec.c b/drivers/acorn/scsi/powertec.c index df55dd8e0e7f..10c342dae444 100644 --- a/drivers/acorn/scsi/powertec.c +++ b/drivers/acorn/scsi/powertec.c @@ -242,10 +242,10 @@ powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) int powertecscsi_proc_info(char *buffer, char **start, off_t offset, int length, int host_no, int inout) { - int pos, begin; struct Scsi_Host *host; struct powertec_info *info; - Scsi_Device *scd; + char *p = buffer; + int pos; host = scsi_host_hn_get(host_no); if (!host) @@ -256,29 +256,16 @@ int powertecscsi_proc_info(char *buffer, char **start, off_t offset, info = (struct powertec_info *)host->hostdata; - begin = 0; - pos = sprintf(buffer, "PowerTec SCSI driver v%s\n", VERSION); - pos += fas216_print_host(&info->info, buffer + pos); - pos += sprintf(buffer + pos, "Term : o%s\n", + p += sprintf(p, "PowerTec SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", info->term_ctl ? "n" : "ff"); - pos += fas216_print_stats(&info->info, buffer + pos); + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); - pos += sprintf(buffer+pos, "\nAttached devices:\n"); - - list_for_each_entry(scd, &host->my_devices, siblings) { - pos += fas216_print_device(&info->info, scd, buffer + pos); - - if (pos + begin < offset) { - begin += pos; - pos = 0; - } - if (pos + begin > offset + length) - break; - } - - *start = buffer + (offset - begin); - pos -= offset - begin; + *start = buffer + offset; + pos = p - buffer - offset; if (pos > length) pos = length; @@ -475,7 +462,6 @@ static struct ecard_driver powertecscsi_driver = { .remove = __devexit_p(powertecscsi_remove), .id_table = powertecscsi_cids, .drv = { - .devclass = &shost_devclass, .name = "powertecscsi", }, }; -- cgit v1.2.3 From 4a6dbb82d63f87430927a6cd8e999268ad6b2439 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 17 May 2003 16:53:41 +0100 Subject: [ARM] Relocate ARM SCSI and Net drivers Move Acorn and ARM SCSI and net drivers to drivers/{net,scsi}/arm. This also allows us to find a reasonable home for other ARM specific net drivers. --- drivers/acorn/net/Kconfig | 25 - drivers/acorn/net/Makefile | 8 - drivers/acorn/net/ether1.c | 1105 ------------- drivers/acorn/net/ether1.h | 278 ---- drivers/acorn/net/ether3.c | 947 ----------- drivers/acorn/net/ether3.h | 168 -- drivers/acorn/net/etherh.c | 763 --------- drivers/acorn/scsi/Kconfig | 89 -- drivers/acorn/scsi/Makefile | 14 - drivers/acorn/scsi/acornscsi-io.S | 145 -- drivers/acorn/scsi/acornscsi.c | 3126 ------------------------------------- drivers/acorn/scsi/acornscsi.h | 358 ----- drivers/acorn/scsi/arxescsi.c | 408 ----- drivers/acorn/scsi/cumana_1.c | 355 ----- drivers/acorn/scsi/cumana_2.c | 574 ------- drivers/acorn/scsi/ecoscsi.c | 283 ---- drivers/acorn/scsi/eesox.c | 685 -------- drivers/acorn/scsi/fas216.c | 3045 ------------------------------------ drivers/acorn/scsi/fas216.h | 394 ----- drivers/acorn/scsi/msgqueue.c | 171 -- drivers/acorn/scsi/msgqueue.h | 82 - drivers/acorn/scsi/oak.c | 215 --- drivers/acorn/scsi/powertec.c | 486 ------ drivers/acorn/scsi/queue.c | 319 ---- drivers/acorn/scsi/queue.h | 105 -- drivers/acorn/scsi/scsi.h | 115 -- drivers/net/Kconfig | 9 +- drivers/net/Makefile | 3 +- drivers/net/am79c961a.c | 685 -------- drivers/net/am79c961a.h | 130 -- drivers/net/arm/Kconfig | 45 + drivers/net/arm/Makefile | 10 + drivers/net/arm/am79c961a.c | 685 ++++++++ drivers/net/arm/am79c961a.h | 130 ++ drivers/net/arm/ether00.c | 1025 ++++++++++++ drivers/net/arm/ether1.c | 1105 +++++++++++++ drivers/net/arm/ether1.h | 278 ++++ drivers/net/arm/ether3.c | 947 +++++++++++ drivers/net/arm/ether3.h | 168 ++ drivers/net/arm/etherh.c | 763 +++++++++ drivers/scsi/Kconfig | 2 +- drivers/scsi/Makefile | 2 +- drivers/scsi/arm/Kconfig | 89 ++ drivers/scsi/arm/Makefile | 14 + drivers/scsi/arm/acornscsi-io.S | 145 ++ drivers/scsi/arm/acornscsi.c | 3126 +++++++++++++++++++++++++++++++++++++ drivers/scsi/arm/acornscsi.h | 358 +++++ drivers/scsi/arm/arxescsi.c | 408 +++++ drivers/scsi/arm/cumana_1.c | 355 +++++ drivers/scsi/arm/cumana_2.c | 574 +++++++ drivers/scsi/arm/ecoscsi.c | 283 ++++ drivers/scsi/arm/eesox.c | 685 ++++++++ drivers/scsi/arm/fas216.c | 3045 ++++++++++++++++++++++++++++++++++++ drivers/scsi/arm/fas216.h | 394 +++++ drivers/scsi/arm/msgqueue.c | 171 ++ drivers/scsi/arm/msgqueue.h | 82 + drivers/scsi/arm/oak.c | 215 +++ drivers/scsi/arm/powertec.c | 486 ++++++ drivers/scsi/arm/queue.c | 319 ++++ drivers/scsi/arm/queue.h | 105 ++ drivers/scsi/arm/scsi.h | 115 ++ 61 files changed, 16129 insertions(+), 15090 deletions(-) delete mode 100644 drivers/acorn/net/Kconfig delete mode 100644 drivers/acorn/net/Makefile delete mode 100644 drivers/acorn/net/ether1.c delete mode 100644 drivers/acorn/net/ether1.h delete mode 100644 drivers/acorn/net/ether3.c delete mode 100644 drivers/acorn/net/ether3.h delete mode 100644 drivers/acorn/net/etherh.c delete mode 100644 drivers/acorn/scsi/Kconfig delete mode 100644 drivers/acorn/scsi/Makefile delete mode 100644 drivers/acorn/scsi/acornscsi-io.S delete mode 100644 drivers/acorn/scsi/acornscsi.c delete mode 100644 drivers/acorn/scsi/acornscsi.h delete mode 100644 drivers/acorn/scsi/arxescsi.c delete mode 100644 drivers/acorn/scsi/cumana_1.c delete mode 100644 drivers/acorn/scsi/cumana_2.c delete mode 100644 drivers/acorn/scsi/ecoscsi.c delete mode 100644 drivers/acorn/scsi/eesox.c delete mode 100644 drivers/acorn/scsi/fas216.c delete mode 100644 drivers/acorn/scsi/fas216.h delete mode 100644 drivers/acorn/scsi/msgqueue.c delete mode 100644 drivers/acorn/scsi/msgqueue.h delete mode 100644 drivers/acorn/scsi/oak.c delete mode 100644 drivers/acorn/scsi/powertec.c delete mode 100644 drivers/acorn/scsi/queue.c delete mode 100644 drivers/acorn/scsi/queue.h delete mode 100644 drivers/acorn/scsi/scsi.h delete mode 100644 drivers/net/am79c961a.c delete mode 100644 drivers/net/am79c961a.h create mode 100644 drivers/net/arm/Kconfig create mode 100644 drivers/net/arm/Makefile create mode 100644 drivers/net/arm/am79c961a.c create mode 100644 drivers/net/arm/am79c961a.h create mode 100644 drivers/net/arm/ether00.c create mode 100644 drivers/net/arm/ether1.c create mode 100644 drivers/net/arm/ether1.h create mode 100644 drivers/net/arm/ether3.c create mode 100644 drivers/net/arm/ether3.h create mode 100644 drivers/net/arm/etherh.c create mode 100644 drivers/scsi/arm/Kconfig create mode 100644 drivers/scsi/arm/Makefile create mode 100644 drivers/scsi/arm/acornscsi-io.S create mode 100644 drivers/scsi/arm/acornscsi.c create mode 100644 drivers/scsi/arm/acornscsi.h create mode 100644 drivers/scsi/arm/arxescsi.c create mode 100644 drivers/scsi/arm/cumana_1.c create mode 100644 drivers/scsi/arm/cumana_2.c create mode 100644 drivers/scsi/arm/ecoscsi.c create mode 100644 drivers/scsi/arm/eesox.c create mode 100644 drivers/scsi/arm/fas216.c create mode 100644 drivers/scsi/arm/fas216.h create mode 100644 drivers/scsi/arm/msgqueue.c create mode 100644 drivers/scsi/arm/msgqueue.h create mode 100644 drivers/scsi/arm/oak.c create mode 100644 drivers/scsi/arm/powertec.c create mode 100644 drivers/scsi/arm/queue.c create mode 100644 drivers/scsi/arm/queue.h create mode 100644 drivers/scsi/arm/scsi.h diff --git a/drivers/acorn/net/Kconfig b/drivers/acorn/net/Kconfig deleted file mode 100644 index d895d7541351..000000000000 --- a/drivers/acorn/net/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -# -# Acorn Network device configuration -# These are for Acorn's Expansion card network interfaces -# -config ARM_ETHER1 - tristate "Acorn Ether1 support" - depends on NET_ETHERNET && ARM && ARCH_ACORN - help - If you have an Acorn system with one of these (AKA25) network cards, - you should say Y to this option if you wish to use it with Linux. - -config ARM_ETHER3 - tristate "Acorn/ANT Ether3 support" - depends on NET_ETHERNET && ARM && ARCH_ACORN - help - If you have an Acorn system with one of these network cards, you - should say Y to this option if you wish to use it with Linux. - -config ARM_ETHERH - tristate "I-cubed EtherH/ANT EtherM support" - depends on NET_ETHERNET && ARM && ARCH_ACORN - help - If you have an Acorn system with one of these network cards, you - should say Y to this option if you wish to use it with Linux. - diff --git a/drivers/acorn/net/Makefile b/drivers/acorn/net/Makefile deleted file mode 100644 index a567f4cdd75b..000000000000 --- a/drivers/acorn/net/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# File: drivers/acorn/net/Makefile -# -# Makefile for the Acorn ethercard network device drivers -# - -obj-$(CONFIG_ARM_ETHERH) += etherh.o -obj-$(CONFIG_ARM_ETHER3) += ether3.o -obj-$(CONFIG_ARM_ETHER1) += ether1.o diff --git a/drivers/acorn/net/ether1.c b/drivers/acorn/net/ether1.c deleted file mode 100644 index 758ff8bb8231..000000000000 --- a/drivers/acorn/net/ether1.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * linux/drivers/acorn/net/ether1.c - * - * Copyright (C) 1996-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Acorn ether1 driver (82586 chip) for Acorn machines - * - * We basically keep two queues in the cards memory - one for transmit - * and one for receive. Each has a head and a tail. The head is where - * we/the chip adds packets to be transmitted/received, and the tail - * is where the transmitter has got to/where the receiver will stop. - * Both of these queues are circular, and since the chip is running - * all the time, we have to be careful when we modify the pointers etc - * so that the buffer memory contents is valid all the time. - * - * Change log: - * 1.00 RMK Released - * 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now. - * 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready - * 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt. - * Should prevent lockup. - * 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong. - * TDR now only reports failure when chip reports non-zero - * TDR time-distance. - * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1 - * 1.06 RMK 10/02/2000 Updated for 2.3.43 - * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define __ETHER1_C -#include "ether1.h" - -static unsigned int net_debug = NET_DEBUG; - -#define BUFFER_SIZE 0x10000 -#define TX_AREA_START 0x00100 -#define TX_AREA_END 0x05000 -#define RX_AREA_START 0x05000 -#define RX_AREA_END 0x0fc00 - -static int ether1_open(struct net_device *dev); -static int ether1_sendpacket(struct sk_buff *skb, struct net_device *dev); -static irqreturn_t ether1_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int ether1_close(struct net_device *dev); -static struct net_device_stats *ether1_getstats(struct net_device *dev); -static void ether1_setmulticastlist(struct net_device *dev); -static void ether1_timeout(struct net_device *dev); - -/* ------------------------------------------------------------------------- */ - -static char version[] __initdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; - -#define BUS_16 16 -#define BUS_8 8 - -/* ------------------------------------------------------------------------- */ - -#define DISABLEIRQS 1 -#define NORMALIRQS 0 - -#define ether1_inw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs) -#define ether1_outw(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs) - -static inline unsigned short -ether1_inw_p (struct net_device *dev, int addr, int svflgs) -{ - unsigned long flags; - unsigned short ret; - - if (svflgs) - local_irq_save (flags); - - outb (addr >> 12, REG_PAGE); - ret = inw (ETHER1_RAM + ((addr & 4095) >> 1)); - if (svflgs) - local_irq_restore (flags); - return ret; -} - -static inline void -ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs) -{ - unsigned long flags; - - if (svflgs) - local_irq_save (flags); - - outb (addr >> 12, REG_PAGE); - outw (val, ETHER1_RAM + ((addr & 4095) >> 1)); - if (svflgs) - local_irq_restore (flags); -} - -/* - * Some inline assembler to allow fast transfers on to/off of the card. - * Since this driver depends on some features presented by the ARM - * specific architecture, and that you can't configure this driver - * without specifiing ARM mode, this is not a problem. - * - * This routine is essentially an optimised memcpy from the card's - * onboard RAM to kernel memory. - */ -static void -ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) -{ - unsigned int page, thislen, offset, addr; - - offset = start & 4095; - page = start >> 12; - addr = ioaddr(ETHER1_RAM + (offset >> 1)); - - if (offset + length > 4096) - thislen = 4096 - offset; - else - thislen = length; - - do { - int used; - - outb(page, REG_PAGE); - length -= thislen; - - __asm__ __volatile__( - "subs %3, %3, #2 - bmi 2f -1: ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bmi 2f - ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bmi 2f - ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bmi 2f - ldr %0, [%1], #2 - mov %0, %0, lsl #16 - orr %0, %0, %0, lsr #16 - str %0, [%2], #4 - subs %3, %3, #2 - bpl 1b -2: adds %3, %3, #1 - ldreqb %0, [%1] - streqb %0, [%2]" - : "=&r" (used), "=&r" (data) - : "r" (addr), "r" (thislen), "1" (data)); - - addr = ioaddr(ETHER1_RAM); - - thislen = length; - if (thislen > 4096) - thislen = 4096; - page++; - } while (thislen); -} - -static void -ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) -{ - unsigned int page, thislen, offset, addr; - - offset = start & 4095; - page = start >> 12; - addr = ioaddr(ETHER1_RAM + (offset >> 1)); - - if (offset + length > 4096) - thislen = 4096 - offset; - else - thislen = length; - - do { - int used; - - outb(page, REG_PAGE); - length -= thislen; - - __asm__ __volatile__( - "subs %3, %3, #2 - bmi 2f -1: ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bmi 2f - ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bmi 2f - ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bmi 2f - ldr %0, [%2], #4 - strb %0, [%1], #1 - mov %0, %0, lsr #8 - strb %0, [%1], #1 - subs %3, %3, #2 - bpl 1b -2: adds %3, %3, #1 - ldreqb %0, [%2] - streqb %0, [%1]" - : "=&r" (used), "=&r" (data) - : "r" (addr), "r" (thislen), "1" (data)); - - addr = ioaddr(ETHER1_RAM); - - thislen = length; - if (thislen > 4096) - thislen = 4096; - page++; - } while (thislen); -} - -static int __init -ether1_ramtest(struct net_device *dev, unsigned char byte) -{ - unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); - int i, ret = BUFFER_SIZE; - int max_errors = 15; - int bad = -1; - int bad_start = 0; - - if (!buffer) - return 1; - - memset (buffer, byte, BUFFER_SIZE); - ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE); - memset (buffer, byte ^ 0xff, BUFFER_SIZE); - ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE); - - for (i = 0; i < BUFFER_SIZE; i++) { - if (buffer[i] != byte) { - if (max_errors >= 0 && bad != buffer[i]) { - if (bad != -1) - printk ("\n"); - printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X", - dev->name, buffer[i], byte, i); - ret = -ENODEV; - max_errors --; - bad = buffer[i]; - bad_start = i; - } - } else { - if (bad != -1) { - if (bad_start == i - 1) - printk ("\n"); - else - printk (" - 0x%04X\n", i - 1); - bad = -1; - } - } - } - - if (bad != -1) - printk (" - 0x%04X\n", BUFFER_SIZE); - kfree (buffer); - - return ret; -} - -static int -ether1_reset (struct net_device *dev) -{ - outb (CTRL_RST|CTRL_ACK, REG_CONTROL); - return BUS_16; -} - -static int __init -ether1_init_2(struct net_device *dev) -{ - int i; - dev->mem_start = 0; - - i = ether1_ramtest (dev, 0x5a); - - if (i > 0) - i = ether1_ramtest (dev, 0x1e); - - if (i <= 0) - return -ENODEV; - - dev->mem_end = i; - return 0; -} - -/* - * These are the structures that are loaded into the ether RAM card to - * initialise the 82586 - */ - -/* at 0x0100 */ -#define NOP_ADDR (TX_AREA_START) -#define NOP_SIZE (0x06) -static nop_t init_nop = { - 0, - CMD_NOP, - NOP_ADDR -}; - -/* at 0x003a */ -#define TDR_ADDR (0x003a) -#define TDR_SIZE (0x08) -static tdr_t init_tdr = { - 0, - CMD_TDR | CMD_INTR, - NOP_ADDR, - 0 -}; - -/* at 0x002e */ -#define MC_ADDR (0x002e) -#define MC_SIZE (0x0c) -static mc_t init_mc = { - 0, - CMD_SETMULTICAST, - TDR_ADDR, - 0, - { { 0, } } -}; - -/* at 0x0022 */ -#define SA_ADDR (0x0022) -#define SA_SIZE (0x0c) -static sa_t init_sa = { - 0, - CMD_SETADDRESS, - MC_ADDR, - { 0, } -}; - -/* at 0x0010 */ -#define CFG_ADDR (0x0010) -#define CFG_SIZE (0x12) -static cfg_t init_cfg = { - 0, - CMD_CONFIG, - SA_ADDR, - 8, - 8, - CFG8_SRDY, - CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6), - 0, - 0x60, - 0, - CFG13_RETRY(15) | CFG13_SLOTH(2), - 0, -}; - -/* at 0x0000 */ -#define SCB_ADDR (0x0000) -#define SCB_SIZE (0x10) -static scb_t init_scb = { - 0, - SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX, - CFG_ADDR, - RX_AREA_START, - 0, - 0, - 0, - 0 -}; - -/* at 0xffee */ -#define ISCP_ADDR (0xffee) -#define ISCP_SIZE (0x08) -static iscp_t init_iscp = { - 1, - SCB_ADDR, - 0x0000, - 0x0000 -}; - -/* at 0xfff6 */ -#define SCP_ADDR (0xfff6) -#define SCP_SIZE (0x0a) -static scp_t init_scp = { - SCP_SY_16BBUS, - { 0, 0 }, - ISCP_ADDR, - 0 -}; - -#define RFD_SIZE (0x16) -static rfd_t init_rfd = { - 0, - 0, - 0, - 0, - { 0, }, - { 0, }, - 0 -}; - -#define RBD_SIZE (0x0a) -static rbd_t init_rbd = { - 0, - 0, - 0, - 0, - ETH_FRAME_LEN + 8 -}; - -#define TX_SIZE (0x08) -#define TBD_SIZE (0x08) - -static int -ether1_init_for_open (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int i, status, addr, next, next2; - int failures = 0; - - outb (CTRL_RST|CTRL_ACK, REG_CONTROL); - - for (i = 0; i < 6; i++) - init_sa.sa_addr[i] = dev->dev_addr[i]; - - /* load data structures into ether1 RAM */ - ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE); - ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE); - ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE); - ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE); - ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE); - ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE); - ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE); - ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE); - - if (ether1_inw (dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) { - printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n", - dev->name); - return 1; - } - - /* - * setup circularly linked list of { rfd, rbd, buffer }, with - * all rfds circularly linked, rbds circularly linked. - * First rfd is linked to scp, first rbd is linked to first - * rfd. Last rbd has a suspend command. - */ - addr = RX_AREA_START; - do { - next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; - next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; - - if (next2 >= RX_AREA_END) { - next = RX_AREA_START; - init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND; - priv->rx_tail = addr; - } else - init_rfd.rfd_command = 0; - if (addr == RX_AREA_START) - init_rfd.rfd_rbdoffset = addr + RFD_SIZE; - else - init_rfd.rfd_rbdoffset = 0; - init_rfd.rfd_link = next; - init_rbd.rbd_link = next + RFD_SIZE; - init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE; - - ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE); - ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE); - addr = next; - } while (next2 < RX_AREA_END); - - priv->tx_link = NOP_ADDR; - priv->tx_head = NOP_ADDR + NOP_SIZE; - priv->tx_tail = TDR_ADDR; - priv->rx_head = RX_AREA_START; - - /* release reset & give 586 a prod */ - priv->resetting = 1; - priv->initialising = 1; - outb (CTRL_RST, REG_CONTROL); - outb (0, REG_CONTROL); - outb (CTRL_CA, REG_CONTROL); - - /* 586 should now unset iscp.busy */ - i = jiffies + HZ/2; - while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) { - if (time_after(jiffies, i)) { - printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name); - return 1; - } - } - - /* check status of commands that we issued */ - i += HZ/10; - while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - failures += 1; - } - - i += HZ/10; - while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - failures += 1; - } - - i += HZ/10; - while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - failures += 1; - } - - i += HZ; - while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) - & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) - break; - } - - if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { - printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name); - printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), - ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); - } else { - status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS); - if (status & TDR_XCVRPROB) - printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name); - else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) { -#ifdef FANCY - printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name, - status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10, - (status & TDR_TIME) % 10); -#else - printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name, - status & TDR_SHORT ? "short" : "open", (status & TDR_TIME)); -#endif - } - } - - if (failures) - ether1_reset (dev); - return failures ? 1 : 0; -} - -/* ------------------------------------------------------------------------- */ - -static int -ether1_txalloc (struct net_device *dev, int size) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int start, tail; - - size = (size + 1) & ~1; - tail = priv->tx_tail; - - if (priv->tx_head + size > TX_AREA_END) { - if (tail > priv->tx_head) - return -1; - start = TX_AREA_START; - if (start + size > tail) - return -1; - priv->tx_head = start + size; - } else { - if (priv->tx_head < tail && (priv->tx_head + size) > tail) - return -1; - start = priv->tx_head; - priv->tx_head += size; - } - - return start; -} - -static int -ether1_open (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - - if (!is_valid_ether_addr(dev->dev_addr)) { - printk(KERN_WARNING "%s: invalid ethernet MAC address\n", - dev->name); - return -EINVAL; - } - - if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev)) - return -EAGAIN; - - memset (&priv->stats, 0, sizeof (struct net_device_stats)); - - if (ether1_init_for_open (dev)) { - free_irq (dev->irq, dev); - return -EAGAIN; - } - - netif_start_queue(dev); - - return 0; -} - -static void -ether1_timeout(struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - - printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n", - dev->name); - printk(KERN_WARNING "%s: resetting device\n", dev->name); - - ether1_reset (dev); - - if (ether1_init_for_open (dev)) - printk (KERN_ERR "%s: unable to restart interface\n", dev->name); - - priv->stats.tx_errors++; - netif_wake_queue(dev); -} - -static int -ether1_sendpacket (struct sk_buff *skb, struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; - int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr; - unsigned long flags; - tx_t tx; - tbd_t tbd; - nop_t nop; - - if (priv->restart) { - printk(KERN_WARNING "%s: resetting device\n", dev->name); - - ether1_reset(dev); - - if (ether1_init_for_open(dev)) - printk(KERN_ERR "%s: unable to restart interface\n", dev->name); - else - priv->restart = 0; - } - - /* - * insert packet followed by a nop - */ - txaddr = ether1_txalloc (dev, TX_SIZE); - tbdaddr = ether1_txalloc (dev, TBD_SIZE); - dataddr = ether1_txalloc (dev, len); - nopaddr = ether1_txalloc (dev, NOP_SIZE); - - tx.tx_status = 0; - tx.tx_command = CMD_TX | CMD_INTR; - tx.tx_link = nopaddr; - tx.tx_tbdoffset = tbdaddr; - tbd.tbd_opts = TBD_EOL | len; - tbd.tbd_link = I82586_NULL; - tbd.tbd_bufl = dataddr; - tbd.tbd_bufh = 0; - nop.nop_status = 0; - nop.nop_command = CMD_NOP; - nop.nop_link = nopaddr; - - local_irq_save(flags); - ether1_writebuffer (dev, &tx, txaddr, TX_SIZE); - ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE); - ether1_writebuffer (dev, skb->data, dataddr, len); - ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE); - tmp = priv->tx_link; - priv->tx_link = nopaddr; - - /* now reset the previous nop pointer */ - ether1_outw (dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS); - - local_irq_restore(flags); - - /* handle transmit */ - dev->trans_start = jiffies; - - /* check to see if we have room for a full sized ether frame */ - tmp = priv->tx_head; - tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); - priv->tx_head = tmp; - dev_kfree_skb (skb); - - if (tst == -1) - netif_stop_queue(dev); - - return 0; -} - -static void -ether1_xmit_done (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - nop_t nop; - int caddr, tst; - - caddr = priv->tx_tail; - -again: - ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); - - switch (nop.nop_command & CMD_MASK) { - case CMD_TDR: - /* special case */ - if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) - != (unsigned short)I82586_NULL) { - ether1_outw(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, - scb_command, NORMALIRQS); - outb (CTRL_CA, REG_CONTROL); - } - priv->tx_tail = NOP_ADDR; - return; - - case CMD_NOP: - if (nop.nop_link == caddr) { - if (priv->initialising == 0) - printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name); - else - priv->initialising = 0; - return; - } - if (caddr == nop.nop_link) - return; - caddr = nop.nop_link; - goto again; - - case CMD_TX: - if (nop.nop_status & STAT_COMPLETE) - break; - printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name); - priv->restart = 1; - return; - - default: - printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name, - nop.nop_command & CMD_MASK, caddr); - priv->restart = 1; - return; - } - - while (nop.nop_status & STAT_COMPLETE) { - if (nop.nop_status & STAT_OK) { - priv->stats.tx_packets ++; - priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS); - } else { - priv->stats.tx_errors ++; - - if (nop.nop_status & STAT_COLLAFTERTX) - priv->stats.collisions ++; - if (nop.nop_status & STAT_NOCARRIER) - priv->stats.tx_carrier_errors ++; - if (nop.nop_status & STAT_TXLOSTCTS) - printk (KERN_WARNING "%s: cts lost\n", dev->name); - if (nop.nop_status & STAT_TXSLOWDMA) - priv->stats.tx_fifo_errors ++; - if (nop.nop_status & STAT_COLLEXCESSIVE) - priv->stats.collisions += 16; - } - - if (nop.nop_link == caddr) { - printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name); - break; - } - - caddr = nop.nop_link; - ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); - if ((nop.nop_command & CMD_MASK) != CMD_NOP) { - printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name); - break; - } - - if (caddr == nop.nop_link) - break; - - caddr = nop.nop_link; - ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); - if ((nop.nop_command & CMD_MASK) != CMD_TX) { - printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name); - break; - } - } - priv->tx_tail = caddr; - - caddr = priv->tx_head; - tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); - priv->tx_head = caddr; - if (tst != -1) - netif_wake_queue(dev); -} - -static void -ether1_recv_done (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int status; - int nexttail, rbdaddr; - rbd_t rbd; - - do { - status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status, NORMALIRQS); - if ((status & RFD_COMPLETE) == 0) - break; - - rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS); - ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE); - - if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) { - int length = rbd.rbd_status & RBD_ACNT; - struct sk_buff *skb; - - length = (length + 1) & ~1; - skb = dev_alloc_skb (length + 2); - - if (skb) { - skb->dev = dev; - skb_reserve (skb, 2); - - ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); - - skb->protocol = eth_type_trans (skb, dev); - netif_rx (skb); - priv->stats.rx_packets ++; - } else - priv->stats.rx_dropped ++; - } else { - printk(KERN_WARNING "%s: %s\n", dev->name, - (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid"); - priv->stats.rx_dropped ++; - } - - nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link, NORMALIRQS); - /* nexttail should be rx_head */ - if (nexttail != priv->rx_head) - printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n", - dev->name, nexttail, priv->rx_head); - ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS); - ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command, NORMALIRQS); - ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status, NORMALIRQS); - ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS); - - priv->rx_tail = nexttail; - priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link, NORMALIRQS); - } while (1); -} - -static irqreturn_t -ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - int status; - - status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS); - - if (status) { - ether1_outw(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX), - SCB_ADDR, scb_t, scb_command, NORMALIRQS); - outb (CTRL_CA | CTRL_ACK, REG_CONTROL); - if (status & SCB_STCX) { - ether1_xmit_done (dev); - } - if (status & SCB_STCNA) { - if (priv->resetting == 0) - printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name); - else - priv->resetting += 1; - if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) - != (unsigned short)I82586_NULL) { - ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS); - outb (CTRL_CA, REG_CONTROL); - } - if (priv->resetting == 2) - priv->resetting = 0; - } - if (status & SCB_STFR) { - ether1_recv_done (dev); - } - if (status & SCB_STRNR) { - if (ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) { - printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name); - ether1_outw (dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS); - outb (CTRL_CA, REG_CONTROL); - priv->stats.rx_dropped ++; /* we suspended due to lack of buffer space */ - } else - printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name, - ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS)); - printk (KERN_WARNING "RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, - NORMALIRQS)); - } - } else - outb (CTRL_ACK, REG_CONTROL); - - return IRQ_HANDLED; -} - -static int -ether1_close (struct net_device *dev) -{ - ether1_reset (dev); - - free_irq(dev->irq, dev); - - return 0; -} - -static struct net_device_stats * -ether1_getstats (struct net_device *dev) -{ - struct ether1_priv *priv = (struct ether1_priv *)dev->priv; - return &priv->stats; -} - -/* - * Set or clear the multicast filter for this adaptor. - * num_addrs == -1 Promiscuous mode, receive all packets. - * num_addrs == 0 Normal mode, clear multicast list. - * num_addrs > 0 Multicast mode, receive normal and MC packets, and do - * best-effort filtering. - */ -static void -ether1_setmulticastlist (struct net_device *dev) -{ -} - -/* ------------------------------------------------------------------------- */ - -static void __init ether1_banner(void) -{ - static unsigned int version_printed = 0; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -static int __devinit -ether1_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct net_device *dev; - struct ether1_priv *priv; - int i, ret = 0; - - ether1_banner(); - - dev = init_etherdev(NULL, sizeof(struct ether1_priv)); - if (!dev) { - ret = -ENOMEM; - goto out; - } - - SET_MODULE_OWNER(dev); - - dev->base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); - dev->irq = ec->irq; - - /* - * these will not fail - the nature of the bus ensures this - */ - request_region(dev->base_addr, 16, dev->name); - request_region(dev->base_addr + 0x800, 4096, dev->name); - - priv = (struct ether1_priv *)dev->priv; - if ((priv->bus_type = ether1_reset(dev)) == 0) { - ret = -ENODEV; - goto release; - } - - printk(KERN_INFO "%s: ether1 in slot %d, ", - dev->name, ec->slot_no); - - for (i = 0; i < 6; i++) { - dev->dev_addr[i] = inb(IDPROM_ADDRESS + i); - printk ("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - } - - if (ether1_init_2(dev)) { - ret = -ENODEV; - goto release; - } - - dev->open = ether1_open; - dev->stop = ether1_close; - dev->hard_start_xmit = ether1_sendpacket; - dev->get_stats = ether1_getstats; - dev->set_multicast_list = ether1_setmulticastlist; - dev->tx_timeout = ether1_timeout; - dev->watchdog_timeo = 5 * HZ / 100; - - ecard_set_drvdata(ec, dev); - return 0; - -release: - release_region(dev->base_addr, 16); - release_region(dev->base_addr + 0x800, 4096); - unregister_netdev(dev); - kfree(dev); -out: - return ret; -} - -static void __devexit ether1_remove(struct expansion_card *ec) -{ - struct net_device *dev = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - - unregister_netdev(dev); - - release_region(dev->base_addr, 16); - release_region(dev->base_addr + 0x800, 4096); - kfree(dev); -} - -static const struct ecard_id ether1_ids[] = { - { MANU_ACORN, PROD_ACORN_ETHER1 }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver ether1_driver = { - .probe = ether1_probe, - .remove = __devexit_p(ether1_remove), - .id_table = ether1_ids, - .drv = { - .name = "ether1", - }, -}; - -static int __init ether1_init(void) -{ - return ecard_register_driver(ðer1_driver); -} - -static void __exit ether1_exit(void) -{ - ecard_remove_driver(ðer1_driver); -} - -module_init(ether1_init); -module_exit(ether1_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/net/ether1.h b/drivers/acorn/net/ether1.h deleted file mode 100644 index 790bb97cac29..000000000000 --- a/drivers/acorn/net/ether1.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * linux/drivers/acorn/net/ether1.h - * - * Copyright (C) 1996 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Network driver for Acorn Ether1 cards. - */ - -#ifndef _LINUX_ether1_H -#define _LINUX_ether1_H - -#ifdef __ETHER1_C -/* use 0 for production, 1 for verification, >2 for debug */ -#ifndef NET_DEBUG -#define NET_DEBUG 0 -#endif - -/* Page register */ -#define REG_PAGE (dev->base_addr + 0x00) - -/* Control register */ -#define REG_CONTROL (dev->base_addr + 0x01) -#define CTRL_RST 0x01 -#define CTRL_LOOPBACK 0x02 -#define CTRL_CA 0x04 -#define CTRL_ACK 0x08 - -#define ETHER1_RAM (dev->base_addr + 0x800) - -/* HW address */ -#define IDPROM_ADDRESS (dev->base_addr + 0x09) - -struct ether1_priv { - struct net_device_stats stats; - unsigned int tx_link; - unsigned int tx_head; - volatile unsigned int tx_tail; - volatile unsigned int rx_head; - volatile unsigned int rx_tail; - unsigned char bus_type; - unsigned char resetting; - unsigned char initialising : 1; - unsigned char restart : 1; -}; - -#define I82586_NULL (-1) - -typedef struct { /* tdr */ - unsigned short tdr_status; - unsigned short tdr_command; - unsigned short tdr_link; - unsigned short tdr_result; -#define TDR_TIME (0x7ff) -#define TDR_SHORT (1 << 12) -#define TDR_OPEN (1 << 13) -#define TDR_XCVRPROB (1 << 14) -#define TDR_LNKOK (1 << 15) -} tdr_t; - -typedef struct { /* transmit */ - unsigned short tx_status; - unsigned short tx_command; - unsigned short tx_link; - unsigned short tx_tbdoffset; -} tx_t; - -typedef struct { /* tbd */ - unsigned short tbd_opts; -#define TBD_CNT (0x3fff) -#define TBD_EOL (1 << 15) - unsigned short tbd_link; - unsigned short tbd_bufl; - unsigned short tbd_bufh; -} tbd_t; - -typedef struct { /* rfd */ - unsigned short rfd_status; -#define RFD_NOEOF (1 << 6) -#define RFD_FRAMESHORT (1 << 7) -#define RFD_DMAOVRN (1 << 8) -#define RFD_NORESOURCES (1 << 9) -#define RFD_ALIGNERROR (1 << 10) -#define RFD_CRCERROR (1 << 11) -#define RFD_OK (1 << 13) -#define RFD_FDCONSUMED (1 << 14) -#define RFD_COMPLETE (1 << 15) - unsigned short rfd_command; -#define RFD_CMDSUSPEND (1 << 14) -#define RFD_CMDEL (1 << 15) - unsigned short rfd_link; - unsigned short rfd_rbdoffset; - unsigned char rfd_dest[6]; - unsigned char rfd_src[6]; - unsigned short rfd_len; -} rfd_t; - -typedef struct { /* rbd */ - unsigned short rbd_status; -#define RBD_ACNT (0x3fff) -#define RBD_ACNTVALID (1 << 14) -#define RBD_EOF (1 << 15) - unsigned short rbd_link; - unsigned short rbd_bufl; - unsigned short rbd_bufh; - unsigned short rbd_len; -} rbd_t; - -typedef struct { /* nop */ - unsigned short nop_status; - unsigned short nop_command; - unsigned short nop_link; -} nop_t; - -typedef struct { /* set multicast */ - unsigned short mc_status; - unsigned short mc_command; - unsigned short mc_link; - unsigned short mc_cnt; - unsigned char mc_addrs[1][6]; -} mc_t; - -typedef struct { /* set address */ - unsigned short sa_status; - unsigned short sa_command; - unsigned short sa_link; - unsigned char sa_addr[6]; -} sa_t; - -typedef struct { /* config command */ - unsigned short cfg_status; - unsigned short cfg_command; - unsigned short cfg_link; - unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */ - unsigned char cfg_fifolim; /* FIFO threshold */ - unsigned char cfg_byte8; -#define CFG8_SRDY (1 << 6) -#define CFG8_SAVEBADF (1 << 7) - unsigned char cfg_byte9; -#define CFG9_ADDRLEN(x) (x) -#define CFG9_ADDRLENBUF (1 << 3) -#define CFG9_PREAMB2 (0 << 4) -#define CFG9_PREAMB4 (1 << 4) -#define CFG9_PREAMB8 (2 << 4) -#define CFG9_PREAMB16 (3 << 4) -#define CFG9_ILOOPBACK (1 << 6) -#define CFG9_ELOOPBACK (1 << 7) - unsigned char cfg_byte10; -#define CFG10_LINPRI(x) (x) -#define CFG10_ACR(x) (x << 4) -#define CFG10_BOFMET (1 << 7) - unsigned char cfg_ifs; - unsigned char cfg_slotl; - unsigned char cfg_byte13; -#define CFG13_SLOTH(x) (x) -#define CFG13_RETRY(x) (x << 4) - unsigned char cfg_byte14; -#define CFG14_PROMISC (1 << 0) -#define CFG14_DISBRD (1 << 1) -#define CFG14_MANCH (1 << 2) -#define CFG14_TNCRS (1 << 3) -#define CFG14_NOCRC (1 << 4) -#define CFG14_CRC16 (1 << 5) -#define CFG14_BTSTF (1 << 6) -#define CFG14_FLGPAD (1 << 7) - unsigned char cfg_byte15; -#define CFG15_CSTF(x) (x) -#define CFG15_ICSS (1 << 3) -#define CFG15_CDTF(x) (x << 4) -#define CFG15_ICDS (1 << 7) - unsigned short cfg_minfrmlen; -} cfg_t; - -typedef struct { /* scb */ - unsigned short scb_status; /* status of 82586 */ -#define SCB_STRXMASK (7 << 4) /* Receive unit status */ -#define SCB_STRXIDLE (0 << 4) /* Idle */ -#define SCB_STRXSUSP (1 << 4) /* Suspended */ -#define SCB_STRXNRES (2 << 4) /* No resources */ -#define SCB_STRXRDY (4 << 4) /* Ready */ -#define SCB_STCUMASK (7 << 8) /* Command unit status */ -#define SCB_STCUIDLE (0 << 8) /* Idle */ -#define SCB_STCUSUSP (1 << 8) /* Suspended */ -#define SCB_STCUACTV (2 << 8) /* Active */ -#define SCB_STRNR (1 << 12) /* Receive unit not ready */ -#define SCB_STCNA (1 << 13) /* Command unit not ready */ -#define SCB_STFR (1 << 14) /* Frame received */ -#define SCB_STCX (1 << 15) /* Command completed */ - unsigned short scb_command; /* Next command */ -#define SCB_CMDRXSTART (1 << 4) /* Start (at rfa_offset) */ -#define SCB_CMDRXRESUME (2 << 4) /* Resume reception */ -#define SCB_CMDRXSUSPEND (3 << 4) /* Suspend reception */ -#define SCB_CMDRXABORT (4 << 4) /* Abort reception */ -#define SCB_CMDCUCSTART (1 << 8) /* Start (at cbl_offset) */ -#define SCB_CMDCUCRESUME (2 << 8) /* Resume execution */ -#define SCB_CMDCUCSUSPEND (3 << 8) /* Suspend execution */ -#define SCB_CMDCUCABORT (4 << 8) /* Abort execution */ -#define SCB_CMDACKRNR (1 << 12) /* Ack RU not ready */ -#define SCB_CMDACKCNA (1 << 13) /* Ack CU not ready */ -#define SCB_CMDACKFR (1 << 14) /* Ack Frame received */ -#define SCB_CMDACKCX (1 << 15) /* Ack Command complete */ - unsigned short scb_cbl_offset; /* Offset of first command unit */ - unsigned short scb_rfa_offset; /* Offset of first receive frame area */ - unsigned short scb_crc_errors; /* Properly aligned frame with CRC error*/ - unsigned short scb_aln_errors; /* Misaligned frames */ - unsigned short scb_rsc_errors; /* Frames lost due to no space */ - unsigned short scb_ovn_errors; /* Frames lost due to slow bus */ -} scb_t; - -typedef struct { /* iscp */ - unsigned short iscp_busy; /* set by CPU before CA */ - unsigned short iscp_offset; /* offset of SCB */ - unsigned short iscp_basel; /* base of SCB */ - unsigned short iscp_baseh; -} iscp_t; - - /* this address must be 0xfff6 */ -typedef struct { /* scp */ - unsigned short scp_sysbus; /* bus size */ -#define SCP_SY_16BBUS 0x00 -#define SCP_SY_8BBUS 0x01 - unsigned short scp_junk[2]; /* junk */ - unsigned short scp_iscpl; /* lower 16 bits of iscp */ - unsigned short scp_iscph; /* upper 16 bits of iscp */ -} scp_t; - -/* commands */ -#define CMD_NOP 0 -#define CMD_SETADDRESS 1 -#define CMD_CONFIG 2 -#define CMD_SETMULTICAST 3 -#define CMD_TX 4 -#define CMD_TDR 5 -#define CMD_DUMP 6 -#define CMD_DIAGNOSE 7 - -#define CMD_MASK 7 - -#define CMD_INTR (1 << 13) -#define CMD_SUSP (1 << 14) -#define CMD_EOL (1 << 15) - -#define STAT_COLLISIONS (15) -#define STAT_COLLEXCESSIVE (1 << 5) -#define STAT_COLLAFTERTX (1 << 6) -#define STAT_TXDEFERRED (1 << 7) -#define STAT_TXSLOWDMA (1 << 8) -#define STAT_TXLOSTCTS (1 << 9) -#define STAT_NOCARRIER (1 << 10) -#define STAT_FAIL (1 << 11) -#define STAT_ABORTED (1 << 12) -#define STAT_OK (1 << 13) -#define STAT_BUSY (1 << 14) -#define STAT_COMPLETE (1 << 15) -#endif -#endif - -/* - * Ether1 card definitions: - * - * FAST accesses: - * +0 Page register - * 16 pages - * +4 Control - * '1' = reset - * '2' = loopback - * '4' = CA - * '8' = int ack - * - * RAM at address + 0x2000 - * Pod. Prod id = 3 - * Words after ID block [base + 8 words] - * +0 pcb issue (0x0c and 0xf3 invalid) - * +1 - +6 eth hw address - */ diff --git a/drivers/acorn/net/ether3.c b/drivers/acorn/net/ether3.c deleted file mode 100644 index 2a2e8f6cc948..000000000000 --- a/drivers/acorn/net/ether3.c +++ /dev/null @@ -1,947 +0,0 @@ -/* - * linux/drivers/acorn/net/ether3.c - * - * Copyright (C) 1995-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card - * for Acorn machines - * - * By Russell King, with some suggestions from borris@ant.co.uk - * - * Changelog: - * 1.04 RMK 29/02/1996 Won't pass packets that are from our ethernet - * address up to the higher levels - they're - * silently ignored. I/F can now be put into - * multicast mode. Receiver routine optimised. - * 1.05 RMK 30/02/1996 Now claims interrupt at open when part of - * the kernel rather than when a module. - * 1.06 RMK 02/03/1996 Various code cleanups - * 1.07 RMK 13/10/1996 Optimised interrupt routine and transmit - * routines. - * 1.08 RMK 14/10/1996 Fixed problem with too many packets, - * prevented the kernel message about dropped - * packets appearing too many times a second. - * Now does not disable all IRQs, only the IRQ - * used by this card. - * 1.09 RMK 10/11/1996 Only enables TX irq when buffer space is low, - * but we still service the TX queue if we get a - * RX interrupt. - * 1.10 RMK 15/07/1997 Fixed autoprobing of NQ8004. - * 1.11 RMK 16/11/1997 Fixed autoprobing of NQ8005A. - * 1.12 RMK 31/12/1997 Removed reference to dev_tint for Linux 2.1. - * RMK 27/06/1998 Changed asm/delay.h to linux/delay.h. - * 1.13 RMK 29/06/1998 Fixed problem with transmission of packets. - * Chip seems to have a bug in, whereby if the - * packet starts two bytes from the end of the - * buffer, it corrupts the receiver chain, and - * never updates the transmit status correctly. - * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. - * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy - * hardware. - * 1.16 RMK 10/02/2000 Updated for 2.3.43 - * 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static char version[] __initdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; - -#include "ether3.h" - -static unsigned int net_debug = NET_DEBUG; - -static void ether3_setmulticastlist(struct net_device *dev); -static int ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt); -static void ether3_tx(struct net_device *dev, struct dev_priv *priv); -static int ether3_open (struct net_device *dev); -static int ether3_sendpacket (struct sk_buff *skb, struct net_device *dev); -static irqreturn_t ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs); -static int ether3_close (struct net_device *dev); -static struct net_device_stats *ether3_getstats (struct net_device *dev); -static void ether3_setmulticastlist (struct net_device *dev); -static void ether3_timeout(struct net_device *dev); - -#define BUS_16 2 -#define BUS_8 1 -#define BUS_UNKNOWN 0 - -/* --------------------------------------------------------------------------- */ - -typedef enum { - buffer_write, - buffer_read -} buffer_rw_t; - -/* - * ether3 read/write. Slow things down a bit... - * The SEEQ8005 doesn't like us writing to its registers - * too quickly. - */ -static inline void ether3_outb(int v, const int r) -{ - outb(v, r); - udelay(1); -} - -static inline void ether3_outw(int v, const int r) -{ - outw(v, r); - udelay(1); -} -#define ether3_inb(r) ({ unsigned int __v = inb((r)); udelay(1); __v; }) -#define ether3_inw(r) ({ unsigned int __v = inw((r)); udelay(1); __v; }) - -static int -ether3_setbuffer(struct net_device *dev, buffer_rw_t read, int start) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int timeout = 1000; - - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); - ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); - - while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) { - if (!timeout--) { - printk("%s: setbuffer broken\n", dev->name); - priv->broken = 1; - return 1; - } - udelay(1); - } - - if (read == buffer_read) { - ether3_outw(start, REG_DMAADDR); - ether3_outw(priv->regs.command | CMD_FIFOREAD, REG_COMMAND); - } else { - ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); - ether3_outw(start, REG_DMAADDR); - } - return 0; -} - -/* - * write data to the buffer memory - */ -#define ether3_writebuffer(dev,data,length) \ - outsw(REG_BUFWIN, (data), (length) >> 1) - -#define ether3_writeword(dev,data) \ - outw((data), REG_BUFWIN) - -#define ether3_writelong(dev,data) { \ - unsigned long reg_bufwin = REG_BUFWIN; \ - outw((data), reg_bufwin); \ - outw((data) >> 16, reg_bufwin); \ -} - -/* - * read data from the buffer memory - */ -#define ether3_readbuffer(dev,data,length) \ - insw(REG_BUFWIN, (data), (length) >> 1) - -#define ether3_readword(dev) \ - inw(REG_BUFWIN) - -#define ether3_readlong(dev) \ - inw(REG_BUFWIN) | (inw(REG_BUFWIN) << 16) - -/* - * Switch LED off... - */ -static void -ether3_ledoff(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct dev_priv *priv = (struct dev_priv *)dev->priv; - ether3_outw(priv->regs.config2 |= CFG2_CTRLO, REG_CONFIG2); -} - -/* - * switch LED on... - */ -static inline void -ether3_ledon(struct net_device *dev, struct dev_priv *priv) -{ - del_timer(&priv->timer); - priv->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */ - priv->timer.data = (unsigned long)dev; - priv->timer.function = ether3_ledoff; - add_timer(&priv->timer); - if (priv->regs.config2 & CFG2_CTRLO) - ether3_outw(priv->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2); -} - -/* - * Read the ethernet address string from the on board rom. - * This is an ascii string!!! - */ -static int __init -ether3_addr(char *addr, struct expansion_card *ec) -{ - struct in_chunk_dir cd; - char *s; - - if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { - int i; - for (i = 0; i<6; i++) { - addr[i] = simple_strtoul(s + 1, &s, 0x10); - if (*s != (i==5?')' : ':' )) - break; - } - if (i == 6) - return 0; - } - /* I wonder if we should even let the user continue in this case - * - no, it would be better to disable the device - */ - printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n"); - return -ENODEV; -} - -/* --------------------------------------------------------------------------- */ - -static int __init -ether3_ramtest(struct net_device *dev, unsigned char byte) -{ - unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL); - int i,ret = 0; - int max_errors = 4; - int bad = -1; - - if (!buffer) - return 1; - - memset(buffer, byte, RX_END); - ether3_setbuffer(dev, buffer_write, 0); - ether3_writebuffer(dev, buffer, TX_END); - ether3_setbuffer(dev, buffer_write, RX_START); - ether3_writebuffer(dev, buffer + RX_START, RX_LEN); - memset(buffer, byte ^ 0xff, RX_END); - ether3_setbuffer(dev, buffer_read, 0); - ether3_readbuffer(dev, buffer, TX_END); - ether3_setbuffer(dev, buffer_read, RX_START); - ether3_readbuffer(dev, buffer + RX_START, RX_LEN); - - for (i = 0; i < RX_END; i++) { - if (buffer[i] != byte) { - if (max_errors > 0 && bad != buffer[i]) { - printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X", - dev->name, buffer[i], byte, i); - ret = 2; - max_errors--; - bad = i; - } - } else { - if (bad != -1) { - if (bad != i - 1) - printk(" - 0x%04X\n", i - 1); - printk("\n"); - bad = -1; - } - } - } - if (bad != -1) - printk(" - 0xffff\n"); - kfree(buffer); - - return ret; -} - -/* ------------------------------------------------------------------------------- */ - -static int __init -ether3_init_2(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int i; - - priv->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8; - priv->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC; - priv->regs.command = 0; - - /* - * Set up our hardware address - */ - ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); - for (i = 0; i < 6; i++) - ether3_outb(dev->dev_addr[i], REG_BUFWIN); - - if (dev->flags & IFF_PROMISC) - priv->regs.config1 |= CFG1_RECVPROMISC; - else if (dev->flags & IFF_MULTICAST) - priv->regs.config1 |= CFG1_RECVSPECBRMULTI; - else - priv->regs.config1 |= CFG1_RECVSPECBROAD; - - /* - * There is a problem with the NQ8005 in that it occasionally loses the - * last two bytes. To get round this problem, we receive the CRC as - * well. That way, if we do lose the last two, then it doesn't matter. - */ - ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); - ether3_outw((TX_END>>8) - 1, REG_BUFWIN); - ether3_outw(priv->rx_head, REG_RECVPTR); - ether3_outw(0, REG_TRANSMITPTR); - ether3_outw(priv->rx_head >> 8, REG_RECVEND); - ether3_outw(priv->regs.config2, REG_CONFIG2); - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); - ether3_outw(priv->regs.command, REG_COMMAND); - - i = ether3_ramtest(dev, 0x5A); - if(i) - return i; - i = ether3_ramtest(dev, 0x1E); - if(i) - return i; - - ether3_setbuffer(dev, buffer_write, 0); - ether3_writelong(dev, 0); - return 0; -} - -static void -ether3_init_for_open(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int i; - - memset(&priv->stats, 0, sizeof(struct net_device_stats)); - - /* Reset the chip */ - ether3_outw(CFG2_RESET, REG_CONFIG2); - udelay(4); - - priv->regs.command = 0; - ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); - while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); - - ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); - for (i = 0; i < 6; i++) - ether3_outb(dev->dev_addr[i], REG_BUFWIN); - - priv->tx_head = 0; - priv->tx_tail = 0; - priv->regs.config2 |= CFG2_CTRLO; - priv->rx_head = RX_START; - - ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); - ether3_outw((TX_END>>8) - 1, REG_BUFWIN); - ether3_outw(priv->rx_head, REG_RECVPTR); - ether3_outw(priv->rx_head >> 8, REG_RECVEND); - ether3_outw(0, REG_TRANSMITPTR); - ether3_outw(priv->regs.config2, REG_CONFIG2); - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); - - ether3_setbuffer(dev, buffer_write, 0); - ether3_writelong(dev, 0); - - priv->regs.command = CMD_ENINTRX | CMD_ENINTTX; - ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); -} - -static inline int -ether3_probe_bus_8(struct net_device *dev, int val) -{ - int write_low, write_high, read_low, read_high; - - write_low = val & 255; - write_high = val >> 8; - - printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low); - - ether3_outb(write_low, REG_RECVPTR); - ether3_outb(write_high, REG_RECVPTR + 1); - - read_low = ether3_inb(REG_RECVPTR); - read_high = ether3_inb(REG_RECVPTR + 1); - - printk(", read8 [%02X:%02X]\n", read_high, read_low); - - return read_low == write_low && read_high == write_high; -} - -static inline int -ether3_probe_bus_16(struct net_device *dev, int val) -{ - int read_val; - - ether3_outw(val, REG_RECVPTR); - read_val = ether3_inw(REG_RECVPTR); - - printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val); - - return read_val == val; -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ -static int -ether3_open(struct net_device *dev) -{ - if (!is_valid_ether_addr(dev->dev_addr)) { - printk(KERN_WARNING "%s: invalid ethernet MAC address\n", - dev->name); - return -EINVAL; - } - - if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) - return -EAGAIN; - - ether3_init_for_open(dev); - - netif_start_queue(dev); - - return 0; -} - -/* - * The inverse routine to ether3_open(). - */ -static int -ether3_close(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - - netif_stop_queue(dev); - - disable_irq(dev->irq); - - ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); - priv->regs.command = 0; - while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); - ether3_outb(0x80, REG_CONFIG2 + 1); - ether3_outw(0, REG_COMMAND); - - free_irq(dev->irq, dev); - - return 0; -} - -/* - * Get the current statistics. This may be called with the card open or - * closed. - */ -static struct net_device_stats *ether3_getstats(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - return &priv->stats; -} - -/* - * Set or clear promiscuous/multicast mode filter for this adaptor. - * - * We don't attempt any packet filtering. The card may have a SEEQ 8004 - * in which does not have the other ethernet address registers present... - */ -static void ether3_setmulticastlist(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - - priv->regs.config1 &= ~CFG1_RECVPROMISC; - - if (dev->flags & IFF_PROMISC) { - /* promiscuous mode */ - priv->regs.config1 |= CFG1_RECVPROMISC; - } else if (dev->flags & IFF_ALLMULTI) { - priv->regs.config1 |= CFG1_RECVSPECBRMULTI; - } else - priv->regs.config1 |= CFG1_RECVSPECBROAD; - - ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); -} - -static void -ether3_timeout(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - - del_timer(&priv->timer); - - local_irq_save(flags); - printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name); - printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name, - ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); - printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name, - ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR)); - printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name, - priv->tx_head, priv->tx_tail); - ether3_setbuffer(dev, buffer_read, priv->tx_tail); - printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); - local_irq_restore(flags); - - priv->regs.config2 |= CFG2_CTRLO; - priv->stats.tx_errors += 1; - ether3_outw(priv->regs.config2, REG_CONFIG2); - priv->tx_head = priv->tx_tail = 0; - - netif_wake_queue(dev); -} - -/* - * Transmit a packet - */ -static int -ether3_sendpacket(struct sk_buff *skb, struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned int ptr, next_ptr; - - length = (length + 1) & ~1; - - if (priv->broken) { - dev_kfree_skb(skb); - priv->stats.tx_dropped ++; - netif_start_queue(dev); - return 0; - } - - next_ptr = (priv->tx_head + 1) & 15; - - local_irq_save(flags); - - if (priv->tx_tail == next_ptr) { - local_irq_restore(flags); - return 1; /* unable to queue */ - } - - dev->trans_start = jiffies; - ptr = 0x600 * priv->tx_head; - priv->tx_head = next_ptr; - next_ptr *= 0x600; - -#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS) - - ether3_setbuffer(dev, buffer_write, next_ptr); - ether3_writelong(dev, 0); - ether3_setbuffer(dev, buffer_write, ptr); - ether3_writelong(dev, 0); - ether3_writebuffer(dev, skb->data, length); - ether3_writeword(dev, htons(next_ptr)); - ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16); - ether3_setbuffer(dev, buffer_write, ptr); - ether3_writeword(dev, htons((ptr + length + 4))); - ether3_writeword(dev, TXHDR_FLAGS >> 16); - ether3_ledon(dev, priv); - - if (!(ether3_inw(REG_STATUS) & STAT_TXON)) { - ether3_outw(ptr, REG_TRANSMITPTR); - ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND); - } - - next_ptr = (priv->tx_head + 1) & 15; - local_irq_restore(flags); - - dev_kfree_skb(skb); - - if (priv->tx_tail == next_ptr) - netif_stop_queue(dev); - - return 0; -} - -static irqreturn_t -ether3_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct dev_priv *priv; - unsigned int status, handled = IRQ_NONE; - -#if NET_DEBUG > 1 - if(net_debug & DEBUG_INT) - printk("eth3irq: %d ", irq); -#endif - - priv = (struct dev_priv *)dev->priv; - - status = ether3_inw(REG_STATUS); - - if (status & STAT_INTRX) { - ether3_outw(CMD_ACKINTRX | priv->regs.command, REG_COMMAND); - ether3_rx(dev, priv, 12); - handled = IRQ_HANDLED; - } - - if (status & STAT_INTTX) { - ether3_outw(CMD_ACKINTTX | priv->regs.command, REG_COMMAND); - ether3_tx(dev, priv); - handled = IRQ_HANDLED; - } - -#if NET_DEBUG > 1 - if(net_debug & DEBUG_INT) - printk("done\n"); -#endif - return handled; -} - -/* - * If we have a good packet(s), get it/them out of the buffers. - */ -static int -ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt) -{ - unsigned int next_ptr = priv->rx_head, received = 0; - ether3_ledon(dev, priv); - - do { - unsigned int this_ptr, status; - unsigned char addrs[16]; - - /* - * read the first 16 bytes from the buffer. - * This contains the status bytes etc and ethernet addresses, - * and we also check the source ethernet address to see if - * it originated from us. - */ - { - unsigned int temp_ptr; - ether3_setbuffer(dev, buffer_read, next_ptr); - temp_ptr = ether3_readword(dev); - status = ether3_readword(dev); - if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) != - (RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr) - break; - - this_ptr = next_ptr + 4; - next_ptr = ntohs(temp_ptr); - } - ether3_setbuffer(dev, buffer_read, this_ptr); - ether3_readbuffer(dev, addrs+2, 12); - -if (next_ptr < RX_START || next_ptr >= RX_END) { - int i; - printk("%s: bad next pointer @%04X: ", dev->name, priv->rx_head); - printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8); - for (i = 2; i < 14; i++) - printk("%02X ", addrs[i]); - printk("\n"); - next_ptr = priv->rx_head; - break; -} - /* - * ignore our own packets... - */ - if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) && - !(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) { - maxcnt ++; /* compensate for loopedback packet */ - ether3_outw(next_ptr >> 8, REG_RECVEND); - } else - if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) { - unsigned int length = next_ptr - this_ptr; - struct sk_buff *skb; - - if (next_ptr <= this_ptr) - length += RX_END - RX_START; - - skb = dev_alloc_skb(length + 2); - if (skb) { - unsigned char *buf; - - skb->dev = dev; - skb_reserve(skb, 2); - buf = skb_put(skb, length); - ether3_readbuffer(dev, buf + 12, length - 12); - ether3_outw(next_ptr >> 8, REG_RECVEND); - *(unsigned short *)(buf + 0) = *(unsigned short *)(addrs + 2); - *(unsigned long *)(buf + 2) = *(unsigned long *)(addrs + 4); - *(unsigned long *)(buf + 6) = *(unsigned long *)(addrs + 8); - *(unsigned short *)(buf + 10) = *(unsigned short *)(addrs + 12); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - received ++; - } else - goto dropping; - } else { - struct net_device_stats *stats = &priv->stats; - ether3_outw(next_ptr >> 8, REG_RECVEND); - if (status & RXSTAT_OVERSIZE) stats->rx_over_errors ++; - if (status & RXSTAT_CRCERROR) stats->rx_crc_errors ++; - if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++; - if (status & RXSTAT_SHORTPACKET) stats->rx_length_errors ++; - stats->rx_errors++; - } - } - while (-- maxcnt); - -done: - priv->stats.rx_packets += received; - priv->rx_head = next_ptr; - /* - * If rx went off line, then that means that the buffer may be full. We - * have dropped at least one packet. - */ - if (!(ether3_inw(REG_STATUS) & STAT_RXON)) { - priv->stats.rx_dropped ++; - ether3_outw(next_ptr, REG_RECVPTR); - ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); - } - - return maxcnt; - -dropping:{ - static unsigned long last_warned; - - ether3_outw(next_ptr >> 8, REG_RECVEND); - /* - * Don't print this message too many times... - */ - if (time_after(jiffies, last_warned + 10 * HZ)) { - last_warned = jiffies; - printk("%s: memory squeeze, dropping packet.\n", dev->name); - } - priv->stats.rx_dropped ++; - goto done; - } -} - -/* - * Update stats for the transmitted packet(s) - */ -static void -ether3_tx(struct net_device *dev, struct dev_priv *priv) -{ - unsigned int tx_tail = priv->tx_tail; - int max_work = 14; - - do { - unsigned long status; - - /* - * Read the packet header - */ - ether3_setbuffer(dev, buffer_read, tx_tail * 0x600); - status = ether3_readlong(dev); - - /* - * Check to see if this packet has been transmitted - */ - if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) != - (TXSTAT_DONE | TXHDR_TRANSMIT)) - break; - - /* - * Update errors - */ - if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS))) - priv->stats.tx_packets++; - else { - priv->stats.tx_errors ++; - if (status & TXSTAT_16COLLISIONS) priv->stats.collisions += 16; - if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++; - } - - tx_tail = (tx_tail + 1) & 15; - } while (--max_work); - - if (priv->tx_tail != tx_tail) { - priv->tx_tail = tx_tail; - netif_wake_queue(dev); - } -} - -static void __init ether3_banner(void) -{ - static unsigned version_printed = 0; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -static const char * __init -ether3_get_dev(struct net_device *dev, struct expansion_card *ec) -{ - const char *name = "ether3"; - - dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); - dev->irq = ec->irq; - - if (ec->cid.manufacturer == MANU_ANT && - ec->cid.product == PROD_ANT_ETHERB) { - dev->base_addr += 0x200; - name = "etherb"; - } - - ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); - ec->irqmask = 0xf0; - - ether3_addr(dev->dev_addr, ec); - - return name; -} - -static int __devinit -ether3_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct net_device *dev; - struct dev_priv *priv; - const char *name; - int i, bus_type, ret; - - ether3_banner(); - - dev = init_etherdev(NULL, sizeof(struct dev_priv)); - if (!dev) { - ret = -ENOMEM; - goto out; - } - - SET_MODULE_OWNER(dev); - - name = ether3_get_dev(dev, ec); - if (!name) { - ret = -ENODEV; - goto free; - } - - /* - * this will not fail - the nature of the bus ensures this - */ - if (!request_region(dev->base_addr, 128, dev->name)) { - ret = -EBUSY; - goto free; - } - - priv = (struct dev_priv *) dev->priv; - init_timer(&priv->timer); - - /* Reset card... - */ - ether3_outb(0x80, REG_CONFIG2 + 1); - bus_type = BUS_UNKNOWN; - udelay(4); - - /* Test using Receive Pointer (16-bit register) to find out - * how the ether3 is connected to the bus... - */ - if (ether3_probe_bus_8(dev, 0x100) && - ether3_probe_bus_8(dev, 0x201)) - bus_type = BUS_8; - - if (bus_type == BUS_UNKNOWN && - ether3_probe_bus_16(dev, 0x101) && - ether3_probe_bus_16(dev, 0x201)) - bus_type = BUS_16; - - switch (bus_type) { - case BUS_UNKNOWN: - printk(KERN_ERR "%s: unable to identify bus width\n", dev->name); - ret = -ENODEV; - goto failed; - - case BUS_8: - printk(KERN_ERR "%s: %s found, but is an unsupported " - "8-bit card\n", dev->name, name); - ret = -ENODEV; - goto failed; - - default: - break; - } - - printk("%s: %s in slot %d, ", dev->name, name, ec->slot_no); - for (i = 0; i < 6; i++) - printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - - if (ether3_init_2(dev)) { - ret = -ENODEV; - goto failed; - } - - dev->open = ether3_open; - dev->stop = ether3_close; - dev->hard_start_xmit = ether3_sendpacket; - dev->get_stats = ether3_getstats; - dev->set_multicast_list = ether3_setmulticastlist; - dev->tx_timeout = ether3_timeout; - dev->watchdog_timeo = 5 * HZ / 100; - - ecard_set_drvdata(ec, dev); - return 0; - -failed: - release_region(dev->base_addr, 128); -free: - unregister_netdev(dev); - kfree(dev); -out: - return ret; -} - -static void __devexit ether3_remove(struct expansion_card *ec) -{ - struct net_device *dev = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - - unregister_netdev(dev); - release_region(dev->base_addr, 128); - kfree(dev); -} - -static const struct ecard_id ether3_ids[] = { - { MANU_ANT2, PROD_ANT_ETHER3 }, - { MANU_ANT, PROD_ANT_ETHER3 }, - { MANU_ANT, PROD_ANT_ETHERB }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver ether3_driver = { - .probe = ether3_probe, - .remove = __devexit_p(ether3_remove), - .id_table = ether3_ids, - .drv = { - .name = "ether3", - }, -}; - -static int __init ether3_init(void) -{ - return ecard_register_driver(ðer3_driver); -} - -static void __exit ether3_exit(void) -{ - ecard_remove_driver(ðer3_driver); -} - -module_init(ether3_init); -module_exit(ether3_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/net/ether3.h b/drivers/acorn/net/ether3.h deleted file mode 100644 index fd0c2edf57fe..000000000000 --- a/drivers/acorn/net/ether3.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - * linux/drivers/acorn/net/ether3.h - * - * Copyright (C) 1995-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * network driver for Acorn/ANT Ether3 cards - */ - -#ifndef _LINUX_ether3_H -#define _LINUX_ether3_H - -/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ -#define DEBUG_TX 2 -#define DEBUG_RX 4 -#define DEBUG_INT 8 -#define DEBUG_IC 16 -#ifndef NET_DEBUG -#define NET_DEBUG 0 -#endif - -/* Command register definitions & bits */ -#define REG_COMMAND (dev->base_addr + 0x00) -#define CMD_ENINTDMA 0x0001 -#define CMD_ENINTRX 0x0002 -#define CMD_ENINTTX 0x0004 -#define CMD_ENINTBUFWIN 0x0008 -#define CMD_ACKINTDMA 0x0010 -#define CMD_ACKINTRX 0x0020 -#define CMD_ACKINTTX 0x0040 -#define CMD_ACKINTBUFWIN 0x0080 -#define CMD_DMAON 0x0100 -#define CMD_RXON 0x0200 -#define CMD_TXON 0x0400 -#define CMD_DMAOFF 0x0800 -#define CMD_RXOFF 0x1000 -#define CMD_TXOFF 0x2000 -#define CMD_FIFOREAD 0x4000 -#define CMD_FIFOWRITE 0x8000 - -/* status register */ -#define REG_STATUS (dev->base_addr + 0x00) -#define STAT_ENINTSTAT 0x0001 -#define STAT_ENINTRX 0x0002 -#define STAT_ENINTTX 0x0004 -#define STAT_ENINTBUFWIN 0x0008 -#define STAT_INTDMA 0x0010 -#define STAT_INTRX 0x0020 -#define STAT_INTTX 0x0040 -#define STAT_INTBUFWIN 0x0080 -#define STAT_DMAON 0x0100 -#define STAT_RXON 0x0200 -#define STAT_TXON 0x0400 -#define STAT_FIFOFULL 0x2000 -#define STAT_FIFOEMPTY 0x4000 -#define STAT_FIFODIR 0x8000 - -/* configuration register 1 */ -#define REG_CONFIG1 (dev->base_addr + 0x10) -#define CFG1_BUFSELSTAT0 0x0000 -#define CFG1_BUFSELSTAT1 0x0001 -#define CFG1_BUFSELSTAT2 0x0002 -#define CFG1_BUFSELSTAT3 0x0003 -#define CFG1_BUFSELSTAT4 0x0004 -#define CFG1_BUFSELSTAT5 0x0005 -#define CFG1_ADDRPROM 0x0006 -#define CFG1_TRANSEND 0x0007 -#define CFG1_LOCBUFMEM 0x0008 -#define CFG1_INTVECTOR 0x0009 -#define CFG1_RECVSPECONLY 0x0000 -#define CFG1_RECVSPECBROAD 0x4000 -#define CFG1_RECVSPECBRMULTI 0x8000 -#define CFG1_RECVPROMISC 0xC000 - -/* The following aren't in 8004 */ -#define CFG1_DMABURSTCONT 0x0000 -#define CFG1_DMABURST800NS 0x0010 -#define CFG1_DMABURST1600NS 0x0020 -#define CFG1_DMABURST3200NS 0x0030 -#define CFG1_DMABURST1 0x0000 -#define CFG1_DMABURST4 0x0040 -#define CFG1_DMABURST8 0x0080 -#define CFG1_DMABURST16 0x00C0 -#define CFG1_RECVCOMPSTAT0 0x0100 -#define CFG1_RECVCOMPSTAT1 0x0200 -#define CFG1_RECVCOMPSTAT2 0x0400 -#define CFG1_RECVCOMPSTAT3 0x0800 -#define CFG1_RECVCOMPSTAT4 0x1000 -#define CFG1_RECVCOMPSTAT5 0x2000 - -/* configuration register 2 */ -#define REG_CONFIG2 (dev->base_addr + 0x20) -#define CFG2_BYTESWAP 0x0001 -#define CFG2_ERRENCRC 0x0008 -#define CFG2_ERRENDRIBBLE 0x0010 -#define CFG2_ERRSHORTFRAME 0x0020 -#define CFG2_SLOTSELECT 0x0040 -#define CFG2_PREAMSELECT 0x0080 -#define CFG2_ADDRLENGTH 0x0100 -#define CFG2_RECVCRC 0x0200 -#define CFG2_XMITNOCRC 0x0400 -#define CFG2_LOOPBACK 0x0800 -#define CFG2_CTRLO 0x1000 -#define CFG2_RESET 0x8000 - -#define REG_RECVEND (dev->base_addr + 0x30) - -#define REG_BUFWIN (dev->base_addr + 0x40) - -#define REG_RECVPTR (dev->base_addr + 0x50) - -#define REG_TRANSMITPTR (dev->base_addr + 0x60) - -#define REG_DMAADDR (dev->base_addr + 0x70) - -/* - * Cards transmit/receive headers - */ -#define TX_NEXT (0xffff) -#define TXHDR_ENBABBLEINT (1 << 16) -#define TXHDR_ENCOLLISIONINT (1 << 17) -#define TXHDR_EN16COLLISION (1 << 18) -#define TXHDR_ENSUCCESS (1 << 19) -#define TXHDR_DATAFOLLOWS (1 << 21) -#define TXHDR_CHAINCONTINUE (1 << 22) -#define TXHDR_TRANSMIT (1 << 23) -#define TXSTAT_BABBLED (1 << 24) -#define TXSTAT_COLLISION (1 << 25) -#define TXSTAT_16COLLISIONS (1 << 26) -#define TXSTAT_DONE (1 << 31) - -#define RX_NEXT (0xffff) -#define RXHDR_CHAINCONTINUE (1 << 6) -#define RXHDR_RECEIVE (1 << 7) -#define RXSTAT_OVERSIZE (1 << 8) -#define RXSTAT_CRCERROR (1 << 9) -#define RXSTAT_DRIBBLEERROR (1 << 10) -#define RXSTAT_SHORTPACKET (1 << 11) -#define RXSTAT_DONE (1 << 15) - - -#define TX_START 0x0000 -#define TX_END 0x6000 -#define RX_START 0x6000 -#define RX_LEN 0xA000 -#define RX_END 0x10000 -/* must be a power of 2 and greater than MAX_TX_BUFFERED */ -#define MAX_TXED 16 -#define MAX_TX_BUFFERED 10 - -struct dev_priv { - struct { - unsigned int command; - unsigned int config1; - unsigned int config2; - } regs; - unsigned char tx_head; /* buffer nr to insert next packet */ - unsigned char tx_tail; /* buffer nr of transmitting packet */ - unsigned int rx_head; /* address to fetch next packet from */ - struct net_device_stats stats; - struct timer_list timer; - int broken; /* 0 = ok, 1 = something went wrong */ -}; - -#endif diff --git a/drivers/acorn/net/etherh.c b/drivers/acorn/net/etherh.c deleted file mode 100644 index 039c74d71dd4..000000000000 --- a/drivers/acorn/net/etherh.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * linux/drivers/acorn/net/etherh.c - * - * Copyright (C) 2000-2002 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * NS8390 I-cubed EtherH and ANT EtherM specific driver - * Thanks to I-Cubed for information on their cards. - * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton - * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan) - * EtherM integration re-engineered by Russell King. - * - * Changelog: - * 08-12-1996 RMK 1.00 Created - * RMK 1.03 Added support for EtherLan500 cards - * 23-11-1997 RMK 1.04 Added media autodetection - * 16-04-1998 RMK 1.05 Improved media autodetection - * 10-02-2000 RMK 1.06 Updated for 2.3.43 - * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8 - * 12-10-1999 CK/TEW EtherM driver first release - * 21-12-2000 TTC EtherH/EtherM integration - * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver. - * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../net/8390.h" - -#define NET_DEBUG 0 -#define DEBUG_INIT 2 - -static unsigned int net_debug = NET_DEBUG; - -struct etherh_priv { - struct ei_device eidev; - unsigned int id; - unsigned int ctrl_port; - unsigned int ctrl; -}; - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("EtherH/EtherM driver"); -MODULE_LICENSE("GPL"); - -static char version[] __initdata = - "EtherH/EtherM Driver (c) 2002 Russell King v1.09\n"; - -#define ETHERH500_DATAPORT 0x200 /* MEMC */ -#define ETHERH500_NS8390 0x000 /* MEMC */ -#define ETHERH500_CTRLPORT 0x200 /* IOC */ - -#define ETHERH600_DATAPORT 16 /* MEMC */ -#define ETHERH600_NS8390 0x200 /* MEMC */ -#define ETHERH600_CTRLPORT 0x080 /* MEMC */ - -#define ETHERH_CP_IE 1 -#define ETHERH_CP_IF 2 -#define ETHERH_CP_HEARTBEAT 2 - -#define ETHERH_TX_START_PAGE 1 -#define ETHERH_STOP_PAGE 127 - -/* - * These came from CK/TEW - */ -#define ETHERM_DATAPORT 0x080 /* MEMC */ -#define ETHERM_NS8390 0x200 /* MEMC */ -#define ETHERM_CTRLPORT 0x08f /* MEMC */ - -#define ETHERM_TX_START_PAGE 64 -#define ETHERM_STOP_PAGE 127 - -/* ------------------------------------------------------------------------ */ - -static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned int mask) -{ - eh->ctrl |= mask; - outb(eh->ctrl, eh->ctrl_port); -} - -static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned int mask) -{ - eh->ctrl &= ~mask; - outb(eh->ctrl, eh->ctrl_port); -} - -static inline unsigned int etherh_get_stat(struct etherh_priv *eh) -{ - return inb(eh->ctrl_port); -} - - - - -static void etherh_irq_enable(ecard_t *ec, int irqnr) -{ - struct etherh_priv *eh = ec->irq_data; - - etherh_set_ctrl(eh, ETHERH_CP_IE); -} - -static void etherh_irq_disable(ecard_t *ec, int irqnr) -{ - struct etherh_priv *eh = ec->irq_data; - - etherh_clr_ctrl(eh, ETHERH_CP_IE); -} - -static expansioncard_ops_t etherh_ops = { - .irqenable = etherh_irq_enable, - .irqdisable = etherh_irq_disable, -}; - - - - -static void -etherh_setif(struct net_device *dev) -{ - struct etherh_priv *eh = (struct etherh_priv *)dev->priv; - struct ei_device *ei_local = &eh->eidev; - unsigned long addr, flags; - - local_irq_save(flags); - - /* set the interface type */ - switch (eh->id) { - case PROD_I3_ETHERLAN600: - case PROD_I3_ETHERLAN600A: - addr = dev->base_addr + EN0_RCNTHI; - - switch (dev->if_port) { - case IF_PORT_10BASE2: - outb((inb(addr) & 0xf8) | 1, addr); - break; - case IF_PORT_10BASET: - outb((inb(addr) & 0xf8), addr); - break; - } - break; - - case PROD_I3_ETHERLAN500: - switch (dev->if_port) { - case IF_PORT_10BASE2: - etherh_clr_ctrl(eh, ETHERH_CP_IF); - break; - - case IF_PORT_10BASET: - etherh_set_ctrl(eh, ETHERH_CP_IF); - break; - } - break; - - default: - break; - } - - local_irq_restore(flags); -} - -static int -etherh_getifstat(struct net_device *dev) -{ - struct etherh_priv *eh = (struct etherh_priv *)dev->priv; - struct ei_device *ei_local = &eh->eidev; - int stat = 0; - - switch (eh->id) { - case PROD_I3_ETHERLAN600: - case PROD_I3_ETHERLAN600A: - switch (dev->if_port) { - case IF_PORT_10BASE2: - stat = 1; - break; - case IF_PORT_10BASET: - stat = inb(dev->base_addr+EN0_RCNTHI) & 4; - break; - } - break; - - case PROD_I3_ETHERLAN500: - switch (dev->if_port) { - case IF_PORT_10BASE2: - stat = 1; - break; - case IF_PORT_10BASET: - stat = etherh_get_stat(eh) & ETHERH_CP_HEARTBEAT; - break; - } - break; - - default: - stat = 0; - break; - } - - return stat != 0; -} - -/* - * Configure the interface. Note that we ignore the other - * parts of ifmap, since its mostly meaningless for this driver. - */ -static int etherh_set_config(struct net_device *dev, struct ifmap *map) -{ - switch (map->port) { - case IF_PORT_10BASE2: - case IF_PORT_10BASET: - /* - * If the user explicitly sets the interface - * media type, turn off automedia detection. - */ - dev->flags &= ~IFF_AUTOMEDIA; - dev->if_port = map->port; - break; - - default: - return -EINVAL; - } - - etherh_setif(dev); - - return 0; -} - -/* - * Reset the 8390 (hard reset). Note that we can't actually do this. - */ -static void -etherh_reset(struct net_device *dev) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - - outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, dev->base_addr); - - /* - * See if we need to change the interface type. - * Note that we use 'interface_num' as a flag - * to indicate that we need to change the media. - */ - if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) { - ei_local->interface_num = 0; - - if (dev->if_port == IF_PORT_10BASET) - dev->if_port = IF_PORT_10BASE2; - else - dev->if_port = IF_PORT_10BASET; - - etherh_setif(dev); - } -} - -/* - * Write a block of data out to the 8390 - */ -static void -etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - unsigned int addr, dma_addr; - unsigned long dma_start; - - if (ei_local->dmaing) { - printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " - " DMAstat %d irqlock %d\n", dev->name, - ei_local->dmaing, ei_local->irqlock); - return; - } - - /* - * Make sure we have a round number of bytes if we're in word mode. - */ - if (count & 1 && ei_local->word16) - count++; - - ei_local->dmaing = 1; - - addr = dev->base_addr; - dma_addr = dev->mem_start; - - count = (count + 1) & ~1; - outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); - - outb (0x42, addr + EN0_RCNTLO); - outb (0x00, addr + EN0_RCNTHI); - outb (0x42, addr + EN0_RSARLO); - outb (0x00, addr + EN0_RSARHI); - outb (E8390_RREAD | E8390_START, addr + E8390_CMD); - - udelay (1); - - outb (ENISR_RDC, addr + EN0_ISR); - outb (count, addr + EN0_RCNTLO); - outb (count >> 8, addr + EN0_RCNTHI); - outb (0, addr + EN0_RSARLO); - outb (start_page, addr + EN0_RSARHI); - outb (E8390_RWRITE | E8390_START, addr + E8390_CMD); - - if (ei_local->word16) - outsw (dma_addr, buf, count >> 1); - else - outsb (dma_addr, buf, count); - - dma_start = jiffies; - - while ((inb (addr + EN0_ISR) & ENISR_RDC) == 0) - if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ - printk(KERN_ERR "%s: timeout waiting for TX RDC\n", - dev->name); - etherh_reset (dev); - NS8390_init (dev, 1); - break; - } - - outb (ENISR_RDC, addr + EN0_ISR); - ei_local->dmaing = 0; -} - -/* - * Read a block of data from the 8390 - */ -static void -etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - unsigned int addr, dma_addr; - unsigned char *buf; - - if (ei_local->dmaing) { - printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " - " DMAstat %d irqlock %d\n", dev->name, - ei_local->dmaing, ei_local->irqlock); - return; - } - - ei_local->dmaing = 1; - - addr = dev->base_addr; - dma_addr = dev->mem_start; - - buf = skb->data; - outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); - outb (count, addr + EN0_RCNTLO); - outb (count >> 8, addr + EN0_RCNTHI); - outb (ring_offset, addr + EN0_RSARLO); - outb (ring_offset >> 8, addr + EN0_RSARHI); - outb (E8390_RREAD | E8390_START, addr + E8390_CMD); - - if (ei_local->word16) { - insw (dma_addr, buf, count >> 1); - if (count & 1) - buf[count - 1] = inb (dma_addr); - } else - insb (dma_addr, buf, count); - - outb (ENISR_RDC, addr + EN0_ISR); - ei_local->dmaing = 0; -} - -/* - * Read a header from the 8390 - */ -static void -etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - unsigned int addr, dma_addr; - - if (ei_local->dmaing) { - printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: " - " DMAstat %d irqlock %d\n", dev->name, - ei_local->dmaing, ei_local->irqlock); - return; - } - - ei_local->dmaing = 1; - - addr = dev->base_addr; - dma_addr = dev->mem_start; - - outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); - outb (sizeof (*hdr), addr + EN0_RCNTLO); - outb (0, addr + EN0_RCNTHI); - outb (0, addr + EN0_RSARLO); - outb (ring_page, addr + EN0_RSARHI); - outb (E8390_RREAD | E8390_START, addr + E8390_CMD); - - if (ei_local->word16) - insw (dma_addr, hdr, sizeof (*hdr) >> 1); - else - insb (dma_addr, hdr, sizeof (*hdr)); - - outb (ENISR_RDC, addr + EN0_ISR); - ei_local->dmaing = 0; -} - -/* - * Open/initialize the board. This is called (in the current kernel) - * sometime after booting when the 'ifconfig' program is run. - * - * This routine should set everything up anew at each open, even - * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. - */ -static int -etherh_open(struct net_device *dev) -{ - struct ei_device *ei_local = (struct ei_device *) dev->priv; - - if (!is_valid_ether_addr(dev->dev_addr)) { - printk(KERN_WARNING "%s: invalid ethernet MAC address\n", - dev->name); - return -EINVAL; - } - - if (request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)) - return -EAGAIN; - - /* - * Make sure that we aren't going to change the - * media type on the next reset - we are about to - * do automedia manually now. - */ - ei_local->interface_num = 0; - - /* - * If we are doing automedia detection, do it now. - * This is more reliable than the 8390's detection. - */ - if (dev->flags & IFF_AUTOMEDIA) { - dev->if_port = IF_PORT_10BASET; - etherh_setif(dev); - mdelay(1); - if (!etherh_getifstat(dev)) { - dev->if_port = IF_PORT_10BASE2; - etherh_setif(dev); - } - } else - etherh_setif(dev); - - etherh_reset(dev); - ei_open(dev); - - return 0; -} - -/* - * The inverse routine to etherh_open(). - */ -static int -etherh_close(struct net_device *dev) -{ - ei_close (dev); - free_irq (dev->irq, dev); - return 0; -} - -/* - * Initialisation - */ - -static void __init etherh_banner(void) -{ - static int version_printed; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -/* - * Read the ethernet address string from the on board rom. - * This is an ascii string... - */ -static int __init etherh_addr(char *addr, struct expansion_card *ec) -{ - struct in_chunk_dir cd; - char *s; - - if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { - int i; - for (i = 0; i < 6; i++) { - addr[i] = simple_strtoul(s + 1, &s, 0x10); - if (*s != (i == 5? ')' : ':')) - break; - } - if (i == 6) - return 0; - } - return -ENODEV; -} - -/* - * Create an ethernet address from the system serial number. - */ -static int __init etherm_addr(char *addr) -{ - unsigned int serial; - - if (system_serial_low == 0 && system_serial_high == 0) - return -ENODEV; - - serial = system_serial_low | system_serial_high; - - addr[0] = 0; - addr[1] = 0; - addr[2] = 0xa4; - addr[3] = 0x10 + (serial >> 24); - addr[4] = serial >> 16; - addr[5] = serial >> 8; - return 0; -} - -static u32 etherh_regoffsets[16]; -static u32 etherm_regoffsets[16]; - -static int __init -etherh_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct ei_device *ei_local; - struct net_device *dev; - struct etherh_priv *eh; - const char *dev_type; - int i, size, ret; - - etherh_banner(); - - dev = init_etherdev(NULL, sizeof(struct etherh_priv)); - if (!dev) { - ret = -ENOMEM; - goto out; - } - - /* - * init_etherdev allocs and zeros dev->priv - */ - eh = dev->priv; - - spin_lock_init(&eh->eidev.page_lock); - - SET_MODULE_OWNER(dev); - - dev->open = etherh_open; - dev->stop = etherh_close; - dev->set_config = etherh_set_config; - dev->irq = ec->irq; - dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); - - /* - * IRQ and control port handling - */ - if (ec->irq != 11) { - ec->ops = ðerh_ops; - ec->irq_data = eh; - } - eh->ctrl = 0; - eh->id = ec->cid.product; - - switch (ec->cid.product) { - case PROD_ANT_ETHERM: - etherm_addr(dev->dev_addr); - dev->base_addr += ETHERM_NS8390; - dev->mem_start = dev->base_addr + ETHERM_DATAPORT; - eh->ctrl_port = dev->base_addr + ETHERM_CTRLPORT; - break; - - case PROD_I3_ETHERLAN500: - etherh_addr(dev->dev_addr, ec); - dev->base_addr += ETHERH500_NS8390; - dev->mem_start = dev->base_addr + ETHERH500_DATAPORT; - eh->ctrl_port = ecard_address (ec, ECARD_IOC, ECARD_FAST) - + ETHERH500_CTRLPORT; - break; - - case PROD_I3_ETHERLAN600: - case PROD_I3_ETHERLAN600A: - etherh_addr(dev->dev_addr, ec); - dev->base_addr += ETHERH600_NS8390; - dev->mem_start = dev->base_addr + ETHERH600_DATAPORT; - eh->ctrl_port = dev->base_addr + ETHERH600_CTRLPORT; - break; - - default: - printk(KERN_ERR "%s: unknown card type %x\n", - dev->name, ec->cid.product); - ret = -ENODEV; - goto free; - } - - size = 16; - if (ec->cid.product == PROD_ANT_ETHERM) - size <<= 3; - - if (!request_region(dev->base_addr, size, dev->name)) { - ret = -EBUSY; - goto free; - } - - if (ethdev_init(dev)) { - ret = -ENODEV; - goto release; - } - - /* - * If we're in the NIC slot, make sure the IRQ is enabled - */ - if (dev->irq == 11) - etherh_set_ctrl(eh, ETHERH_CP_IE); - - /* - * Unfortunately, ethdev_init eventually calls - * ether_setup, which re-writes dev->flags. - */ - switch (ec->cid.product) { - case PROD_ANT_ETHERM: - dev_type = "ANT EtherM"; - dev->if_port = IF_PORT_UNKNOWN; - break; - - case PROD_I3_ETHERLAN500: - dev_type = "i3 EtherH 500"; - dev->if_port = IF_PORT_UNKNOWN; - break; - - case PROD_I3_ETHERLAN600: - dev_type = "i3 EtherH 600"; - dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; - dev->if_port = IF_PORT_10BASET; - break; - - case PROD_I3_ETHERLAN600A: - dev_type = "i3 EtherH 600A"; - dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; - dev->if_port = IF_PORT_10BASET; - break; - - default: - dev_type = "unknown"; - break; - } - - printk(KERN_INFO "%s: %s in slot %d, ", - dev->name, dev_type, ec->slot_no); - - for (i = 0; i < 6; i++) - printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - - ei_local = (struct ei_device *) dev->priv; - if (ec->cid.product == PROD_ANT_ETHERM) { - ei_local->tx_start_page = ETHERM_TX_START_PAGE; - ei_local->stop_page = ETHERM_STOP_PAGE; - ei_local->reg_offset = etherm_regoffsets; - } else { - ei_local->tx_start_page = ETHERH_TX_START_PAGE; - ei_local->stop_page = ETHERH_STOP_PAGE; - ei_local->reg_offset = etherh_regoffsets; - } - - ei_local->name = dev->name; - ei_local->word16 = 1; - ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES; - ei_local->reset_8390 = etherh_reset; - ei_local->block_input = etherh_block_input; - ei_local->block_output = etherh_block_output; - ei_local->get_8390_hdr = etherh_get_header; - ei_local->interface_num = 0; - - etherh_reset(dev); - NS8390_init(dev, 0); - - ecard_set_drvdata(ec, dev); - - return 0; - -release: - release_region(dev->base_addr, 16); -free: - unregister_netdev(dev); - kfree(dev->priv); - kfree(dev); -out: - return ret; -} - -static void __devexit etherh_remove(struct expansion_card *ec) -{ - struct net_device *dev = ecard_get_drvdata(ec); - int size = 16; - - ecard_set_drvdata(ec, NULL); - - unregister_netdev(dev); - if (ec->cid.product == PROD_ANT_ETHERM) - size <<= 3; - release_region(dev->base_addr, size); - kfree(dev); - - ec->ops = NULL; - kfree(ec->irq_data); -} - -static const struct ecard_id etherh_ids[] = { - { MANU_ANT, PROD_ANT_ETHERM }, - { MANU_I3, PROD_I3_ETHERLAN500 }, - { MANU_I3, PROD_I3_ETHERLAN600 }, - { MANU_I3, PROD_I3_ETHERLAN600A }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver etherh_driver = { - .probe = etherh_probe, - .remove = __devexit_p(etherh_remove), - .id_table = etherh_ids, - .drv = { - .name = "etherh", - }, -}; - -static int __init etherh_init(void) -{ - int i; - - for (i = 0; i < 16; i++) { - etherh_regoffsets[i] = i; - etherm_regoffsets[i] = i << 3; - } - - return ecard_register_driver(ðerh_driver); -} - -static void __exit etherh_exit(void) -{ - ecard_remove_driver(ðerh_driver); -} - -module_init(etherh_init); -module_exit(etherh_exit); diff --git a/drivers/acorn/scsi/Kconfig b/drivers/acorn/scsi/Kconfig deleted file mode 100644 index 54b32868aaf7..000000000000 --- a/drivers/acorn/scsi/Kconfig +++ /dev/null @@ -1,89 +0,0 @@ -# -# SCSI driver configuration for Acorn -# -config SCSI_ACORNSCSI_3 - tristate "Acorn SCSI card (aka30) support" - depends on ARCH_ACORN && SCSI - help - This enables support for the Acorn SCSI card (aka30). If you have an - Acorn system with one of these, say Y. If unsure, say N. - -config SCSI_ACORNSCSI_TAGGED_QUEUE - bool "Support SCSI 2 Tagged queueing" - depends on SCSI_ACORNSCSI_3 - help - Say Y here to enable tagged queuing support on the Acorn SCSI card. - - This is a feature of SCSI-2 which improves performance: the host - adapter can send several SCSI commands to a device's queue even if - previous commands haven't finished yet. Some SCSI devices don't - implement this properly, so the safe answer is N. - -config SCSI_ACORNSCSI_SYNC - bool "Support SCSI 2 Synchronous Transfers" - depends on SCSI_ACORNSCSI_3 - help - Say Y here to enable synchronous transfer negotiation with all - targets on the Acorn SCSI card. - - In general, this improves performance; however some SCSI devices - don't implement it properly, so the safe answer is N. - -config SCSI_ARXESCSI - tristate "ARXE SCSI support" - depends on ARCH_ACORN && SCSI - help - Around 1991, Arxe Systems Limited released a high density floppy - disc interface for the Acorn Archimedes range, to allow the use of - HD discs from the then new A5000 on earlier models. This interface - was either sold on its own or with an integral SCSI controller. - Technical details on this NCR53c94-based device are available at - - Say Y here to compile in support for the SCSI controller. - -config SCSI_CUMANA_2 - tristate "CumanaSCSI II support" - depends on ARCH_ACORN && SCSI - help - This enables support for the Cumana SCSI II card. If you have an - Acorn system with one of these, say Y. If unsure, say N. - -config SCSI_EESOXSCSI - tristate "EESOX support" - depends on ARCH_ACORN && SCSI - help - This enables support for the EESOX SCSI card. If you have an Acorn - system with one of these, say Y, otherwise say N. - -config SCSI_POWERTECSCSI - tristate "PowerTec support" - depends on ARCH_ACORN && SCSI - help - This enables support for the Powertec SCSI card on Acorn systems. If - you have one of these, say Y. If unsure, say N. - -comment "The following drivers are not fully supported" - depends on ARCH_ACORN && EXPERIMENTAL - -config SCSI_CUMANA_1 - tristate "CumanaSCSI I support (EXPERIMENTAL)" - depends on ARCH_ACORN && EXPERIMENTAL && SCSI - help - This enables support for the Cumana SCSI I card. If you have an - Acorn system with one of these, say Y. If unsure, say N. - -config SCSI_ECOSCSI - tristate "EcoScsi support (EXPERIMENTAL)" - depends on ARCH_ACORN && EXPERIMENTAL && (ARCH_ARC || ARCH_A5K) && SCSI - help - This enables support for the EcoSCSI card -- a small card that sits - in the Econet socket. If you have an Acorn system with one of these, - say Y. If unsure, say N. - -config SCSI_OAK1 - tristate "Oak SCSI support (EXPERIMENTAL)" - depends on ARCH_ACORN && EXPERIMENTAL && SCSI - help - This enables support for the Oak SCSI card. If you have an Acorn - system with one of these, say Y. If unsure, say N. - diff --git a/drivers/acorn/scsi/Makefile b/drivers/acorn/scsi/Makefile deleted file mode 100644 index aa143604372e..000000000000 --- a/drivers/acorn/scsi/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# Makefile for drivers/acorn/scsi -# - -acornscsi_mod-objs := acornscsi.o acornscsi-io.o - -obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_ARXESCSI) += arxescsi.o fas216.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_CUMANA_1) += cumana_1.o -obj-$(CONFIG_SCSI_CUMANA_2) += cumana_2.o fas216.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_ECOSCSI) += ecoscsi.o -obj-$(CONFIG_SCSI_OAK1) += oak.o -obj-$(CONFIG_SCSI_POWERTECSCSI) += powertec.o fas216.o queue.o msgqueue.o -obj-$(CONFIG_SCSI_EESOXSCSI) += eesox.o fas216.o queue.o msgqueue.o diff --git a/drivers/acorn/scsi/acornscsi-io.S b/drivers/acorn/scsi/acornscsi-io.S deleted file mode 100644 index 93467e6ac923..000000000000 --- a/drivers/acorn/scsi/acornscsi-io.S +++ /dev/null @@ -1,145 +0,0 @@ -/* - * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include - -#include -#include - -#if (IO_BASE == (PCIO_BASE & 0xff000000)) -#define ADDR(off,reg) \ - tst off, $0x80000000 ;\ - mov reg, $IO_BASE ;\ - orreq reg, reg, $(PCIO_BASE & 0x00ff0000) -#else -#define ADDR(off,reg) \ - tst off, $0x80000000 ;\ - movne reg, $IO_BASE ;\ - moveq reg, $(PCIO_BASE & 0xff000000) ;\ - orreq reg, reg, $(PCIO_BASE & 0x00ff0000) -#endif - -@ Purpose: transfer a block of data from the acorn scsi card to memory -@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) -@ Returns: nothing - - .align -ENTRY(__acornscsi_in) - stmfd sp!, {r4 - r7, lr} - bic r0, r0, #3 - mov lr, #0xff - orr lr, lr, #0xff00 -acornscsi_in16lp: - subs r2, r2, #16 - bmi acornscsi_in8 - ldmia r0!, {r3, r4, r5, r6} - and r3, r3, lr - orr r3, r3, r4, lsl #16 - and r4, r5, lr - orr r4, r4, r6, lsl #16 - ldmia r0!, {r5, r6, r7, ip} - and r5, r5, lr - orr r5, r5, r6, lsl #16 - and r6, r7, lr - orr r6, r6, ip, lsl #16 - stmia r1!, {r3 - r6} - bne acornscsi_in16lp - LOADREGS(fd, sp!, {r4 - r7, pc}) - -acornscsi_in8: adds r2, r2, #8 - bmi acornscsi_in4 - ldmia r0!, {r3, r4, r5, r6} - and r3, r3, lr - orr r3, r3, r4, lsl #16 - and r4, r5, lr - orr r4, r4, r6, lsl #16 - stmia r1!, {r3 - r4} - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r2, r2, #8 - -acornscsi_in4: adds r2, r2, #4 - bmi acornscsi_in2 - ldmia r0!, {r3, r4} - and r3, r3, lr - orr r3, r3, r4, lsl #16 - str r3, [r1], #4 - LOADREGS(eqfd, sp!, {r4 - r7, pc}) - sub r2, r2, #4 - -acornscsi_in2: adds r2, r2, #2 - ldr r3, [r0], #4 - and r3, r3, lr - strb r3, [r1], #1 - mov r3, r3, lsr #8 - strplb r3, [r1], #1 - LOADREGS(fd, sp!, {r4 - r7, pc}) - -@ Purpose: transfer a block of data from memory to the acorn scsi card -@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) -@ Returns: nothing - -ENTRY(__acornscsi_out) - stmfd sp!, {r4 - r6, lr} - bic r0, r0, #3 -acornscsi_out16lp: - subs r2, r2, #16 - bmi acornscsi_out8 - ldmia r1!, {r4, r6, ip, lr} - mov r3, r4, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, r4, lsr #16 - orr r4, r4, r4, lsl #16 - mov r5, r6, lsl #16 - orr r5, r5, r5, lsr #16 - mov r6, r6, lsr #16 - orr r6, r6, r6, lsl #16 - stmia r0!, {r3, r4, r5, r6} - mov r3, ip, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, ip, lsr #16 - orr r4, r4, r4, lsl #16 - mov ip, lr, lsl #16 - orr ip, ip, ip, lsr #16 - mov lr, lr, lsr #16 - orr lr, lr, lr, lsl #16 - stmia r0!, {r3, r4, ip, lr} - bne acornscsi_out16lp - LOADREGS(fd, sp!, {r4 - r6, pc}) - -acornscsi_out8: adds r2, r2, #8 - bmi acornscsi_out4 - ldmia r1!, {r4, r6} - mov r3, r4, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, r4, lsr #16 - orr r4, r4, r4, lsl #16 - mov r5, r6, lsl #16 - orr r5, r5, r5, lsr #16 - mov r6, r6, lsr #16 - orr r6, r6, r6, lsl #16 - stmia r0!, {r3, r4, r5, r6} - LOADREGS(eqfd, sp!, {r4 - r6, pc}) - - sub r2, r2, #8 -acornscsi_out4: adds r2, r2, #4 - bmi acornscsi_out2 - ldr r4, [r1], #4 - mov r3, r4, lsl #16 - orr r3, r3, r3, lsr #16 - mov r4, r4, lsr #16 - orr r4, r4, r4, lsl #16 - stmia r0!, {r3, r4} - LOADREGS(eqfd, sp!, {r4 - r6, pc}) - - sub r2, r2, #4 -acornscsi_out2: adds r2, r2, #2 - ldr r3, [r1], #2 - strb r3, [r0], #1 - mov r3, r3, lsr #8 - strplb r3, [r0], #1 - LOADREGS(fd, sp!, {r4 - r6, pc}) - diff --git a/drivers/acorn/scsi/acornscsi.c b/drivers/acorn/scsi/acornscsi.c deleted file mode 100644 index 0275e8a47f55..000000000000 --- a/drivers/acorn/scsi/acornscsi.c +++ /dev/null @@ -1,3126 +0,0 @@ -/* - * linux/drivers/acorn/scsi/acornscsi.c - * - * Acorn SCSI 3 driver - * By R.M.King. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Abandoned using the Select and Transfer command since there were - * some nasty races between our software and the target devices that - * were not easy to solve, and the device errata had a lot of entries - * for this command, some of them quite nasty... - * - * Changelog: - * 26-Sep-1997 RMK Re-jigged to use the queue module. - * Re-coded state machine to be based on driver - * state not scsi state. Should be easier to debug. - * Added acornscsi_release to clean up properly. - * Updated proc/scsi reporting. - * 05-Oct-1997 RMK Implemented writing to SCSI devices. - * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/ - * reconnect race condition causing a warning message. - * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. - * 15-Oct-1997 RMK Improved handling of commands. - * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. - * 13-Dec-1998 RMK Better abort code and command handling. Extra state - * transitions added to allow dodgy devices to work. - */ -#define DEBUG_NO_WRITE 1 -#define DEBUG_QUEUES 2 -#define DEBUG_DMA 4 -#define DEBUG_ABORT 8 -#define DEBUG_DISCON 16 -#define DEBUG_CONNECT 32 -#define DEBUG_PHASES 64 -#define DEBUG_WRITE 128 -#define DEBUG_LINK 256 -#define DEBUG_MESSAGES 512 -#define DEBUG_RESET 1024 -#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ - DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ - DEBUG_DMA|DEBUG_QUEUES) - -/* DRIVER CONFIGURATION - * - * SCSI-II Tagged queue support. - * - * I don't have any SCSI devices that support it, so it is totally untested - * (except to make sure that it doesn't interfere with any non-tagging - * devices). It is not fully implemented either - what happens when a - * tagging device reconnects??? - * - * You can tell if you have a device that supports tagged queueing my - * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported - * as '2 TAG'. - * - * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config - * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug, - * comment out the undef. - */ -#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE -/* - * SCSI-II Linked command support. - * - * The higher level code doesn't support linked commands yet, and so the option - * is undef'd here. - */ -#undef CONFIG_SCSI_ACORNSCSI_LINK -/* - * SCSI-II Synchronous transfer support. - * - * Tried and tested... - * - * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max) - * SDTR_PERIOD - period of REQ signal (min=125, max=1020) - * DEFAULT_PERIOD - default REQ period. - */ -#define SDTR_SIZE 12 -#define SDTR_PERIOD 125 -#define DEFAULT_PERIOD 500 - -/* - * Debugging information - * - * DEBUG - bit mask from list above - * DEBUG_TARGET - is defined to the target number if you want to debug - * a specific target. [only recon/write/dma]. - */ -#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE) -/* only allow writing to SCSI device 0 */ -#define NO_WRITE 0xFE -/*#define DEBUG_TARGET 2*/ -/* - * Select timeout time (in 10ms units) - * - * This is the timeout used between the start of selection and the WD33C93 - * chip deciding that the device isn't responding. - */ -#define TIMEOUT_TIME 10 -/* - * Define this if you want to have verbose explaination of SCSI - * status/messages. - */ -#undef CONFIG_ACORNSCSI_CONSTANTS -/* - * Define this if you want to use the on board DMAC [don't remove this option] - * If not set, then use PIO mode (not currently supported). - */ -#define USE_DMAC - -/* - * ==================================================================================== - */ - -#ifdef DEBUG_TARGET -#define DBG(cmd,xxx...) \ - if (cmd->device->id == DEBUG_TARGET) { \ - xxx; \ - } -#else -#define DBG(cmd,xxx...) xxx -#endif - -#ifndef STRINGIFY -#define STRINGIFY(x) #x -#endif -#define STRx(x) STRINGIFY(x) -#define NO_WRITE_STR STRx(NO_WRITE) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "acornscsi.h" -#include "msgqueue.h" -#include "scsi.h" - -#include - -#define VER_MAJOR 2 -#define VER_MINOR 0 -#define VER_PATCH 6 - -#ifndef ABORT_TAG -#define ABORT_TAG 0xd -#else -#error "Yippee! ABORT TAG is now defined! Remove this error!" -#endif - -#ifdef CONFIG_SCSI_ACORNSCSI_LINK -#error SCSI2 LINKed commands not supported (yet)! -#endif - -#ifdef USE_DMAC -/* - * DMAC setup parameters - */ -#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP) -#define INIT_DEVCON1 (DEVCON1_BHLD) -#define DMAC_READ (MODECON_READ) -#define DMAC_WRITE (MODECON_WRITE) -#define INIT_SBICDMA (CTRL_DMABURST) - -#define scsi_xferred have_data_in - -/* - * Size of on-board DMA buffer - */ -#define DMAC_BUFFER_SIZE 65536 -#endif - -#define STATUS_BUFFER_TO_PRINT 24 - -unsigned int sdtr_period = SDTR_PERIOD; -unsigned int sdtr_size = SDTR_SIZE; - -static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); -static int acornscsi_reconnect_finish(AS_Host *host); -static void acornscsi_dma_cleanup(AS_Host *host); -static void acornscsi_abortcmd(AS_Host *host, unsigned char tag); - -/* ==================================================================================== - * Miscellaneous - */ - -static inline void -sbic_arm_write(unsigned int io_port, int reg, int value) -{ - __raw_writeb(reg, io_port); - __raw_writeb(value, io_port + 4); -} - -#define sbic_arm_writenext(io,val) \ - __raw_writeb((val), (io) + 4) - -static inline -int sbic_arm_read(unsigned int io_port, int reg) -{ - if(reg == ASR) - return __raw_readl(io_port) & 255; - __raw_writeb(reg, io_port); - return __raw_readl(io_port + 4) & 255; -} - -#define sbic_arm_readnext(io) \ - __raw_readb((io) + 4) - -#ifdef USE_DMAC -#define dmac_read(io_port,reg) \ - inb((io_port) + (reg)) - -#define dmac_write(io_port,reg,value) \ - ({ outb((value), (io_port) + (reg)); }) - -#define dmac_clearintr(io_port) \ - ({ outb(0, (io_port)); }) - -static inline -unsigned int dmac_address(unsigned int io_port) -{ - return dmac_read(io_port, TXADRHI) << 16 | - dmac_read(io_port, TXADRMD) << 8 | - dmac_read(io_port, TXADRLO); -} - -static -void acornscsi_dumpdma(AS_Host *host, char *where) -{ - unsigned int mode, addr, len; - - mode = dmac_read(host->dma.io_port, MODECON); - addr = dmac_address(host->dma.io_port); - len = dmac_read(host->dma.io_port, TXCNTHI) << 8 | - dmac_read(host->dma.io_port, TXCNTLO); - - printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", - host->host->host_no, where, - mode, addr, (len + 1) & 0xffff, - dmac_read(host->dma.io_port, MASKREG)); - - printk("DMA @%06x, ", host->dma.start_addr); - printk("BH @%p +%04x, ", host->scsi.SCp.ptr, - host->scsi.SCp.this_residual); - printk("DT @+%04x ST @+%04x", host->dma.transferred, - host->scsi.SCp.scsi_xferred); - printk("\n"); -} -#endif - -static -unsigned long acornscsi_sbic_xfcount(AS_Host *host) -{ - unsigned long length; - - length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16; - length |= sbic_arm_readnext(host->scsi.io_port) << 8; - length |= sbic_arm_readnext(host->scsi.io_port); - - return length; -} - -static int -acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg) -{ - int asr; - - do { - asr = sbic_arm_read(host->scsi.io_port, ASR); - - if ((asr & stat_mask) == stat) - return 0; - - udelay(1); - } while (--timeout); - - printk("scsi%d: timeout while %s\n", host->host->host_no, msg); - - return -1; -} - -static -int acornscsi_sbic_issuecmd(AS_Host *host, int command) -{ - if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) - return -1; - - sbic_arm_write(host->scsi.io_port, CMND, command); - - return 0; -} - -static void -acornscsi_csdelay(unsigned int cs) -{ - unsigned long target_jiffies, flags; - - target_jiffies = jiffies + 1 + cs * HZ / 100; - - local_save_flags(flags); - local_irq_enable(); - - while (time_before(jiffies, target_jiffies)) barrier(); - - local_irq_restore(flags); -} - -static -void acornscsi_resetcard(AS_Host *host) -{ - unsigned int i, timeout; - - /* assert reset line */ - host->card.page_reg = 0x80; - outb(host->card.page_reg, host->card.io_page); - - /* wait 3 cs. SCSI standard says 25ms. */ - acornscsi_csdelay(3); - - host->card.page_reg = 0; - outb(host->card.page_reg, host->card.io_page); - - /* - * Should get a reset from the card - */ - timeout = 1000; - do { - if (inb(host->card.io_intr) & 8) - break; - udelay(1); - } while (--timeout); - - if (timeout == 0) - printk("scsi%d: timeout while resetting card\n", - host->host->host_no); - - sbic_arm_read(host->scsi.io_port, ASR); - sbic_arm_read(host->scsi.io_port, SSR); - - /* setup sbic - WD33C93A */ - sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); - - /* - * Command should cause a reset interrupt - */ - timeout = 1000; - do { - if (inb(host->card.io_intr) & 8) - break; - udelay(1); - } while (--timeout); - - if (timeout == 0) - printk("scsi%d: timeout while resetting card\n", - host->host->host_no); - - sbic_arm_read(host->scsi.io_port, ASR); - if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01) - printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", - host->host->host_no); - - sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - - host->card.page_reg = 0x40; - outb(host->card.page_reg, host->card.io_page); - - /* setup dmac - uPC71071 */ - dmac_write(host->dma.io_port, INIT, 0); -#ifdef USE_DMAC - dmac_write(host->dma.io_port, INIT, INIT_8BIT); - dmac_write(host->dma.io_port, CHANNEL, CHANNEL_0); - dmac_write(host->dma.io_port, DEVCON0, INIT_DEVCON0); - dmac_write(host->dma.io_port, DEVCON1, INIT_DEVCON1); -#endif - - host->SCpnt = NULL; - host->scsi.phase = PHASE_IDLE; - host->scsi.disconnectable = 0; - - memset(host->busyluns, 0, sizeof(host->busyluns)); - - for (i = 0; i < 8; i++) { - host->device[i].sync_state = SYNC_NEGOCIATE; - host->device[i].disconnect_ok = 1; - } - - /* wait 25 cs. SCSI standard says 250ms. */ - acornscsi_csdelay(25); -} - -/*============================================================================================= - * Utility routines (eg. debug) - */ -#ifdef CONFIG_ACORNSCSI_CONSTANTS -static char *acornscsi_interrupttype[] = { - "rst", "suc", "p/a", "3", - "term", "5", "6", "7", - "serv", "9", "a", "b", - "c", "d", "e", "f" -}; - -static signed char acornscsi_map[] = { - 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 -}; - -static char *acornscsi_interruptcode[] = { - /* 0 */ - "reset - normal mode", /* 00 */ - "reset - advanced mode", /* 01 */ - - /* 2 */ - "sel", /* 11 */ - "sel+xfer", /* 16 */ - "data-out", /* 18 */ - "data-in", /* 19 */ - "cmd", /* 1A */ - "stat", /* 1B */ - "??-out", /* 1C */ - "??-in", /* 1D */ - "msg-out", /* 1E */ - "msg-in", /* 1F */ - - /* 12 */ - "/ACK asserted", /* 20 */ - "save-data-ptr", /* 21 */ - "{re}sel", /* 22 */ - - /* 15 */ - "inv cmd", /* 40 */ - "unexpected disconnect", /* 41 */ - "sel timeout", /* 42 */ - "P err", /* 43 */ - "P err+ATN", /* 44 */ - "bad status byte", /* 47 */ - - /* 21 */ - "resel, no id", /* 80 */ - "resel", /* 81 */ - "discon", /* 85 */ -}; - -static -void print_scsi_status(unsigned int ssr) -{ - if (acornscsi_map[ssr] != -1) - printk("%s:%s", - acornscsi_interrupttype[(ssr >> 4)], - acornscsi_interruptcode[acornscsi_map[ssr]]); - else - printk("%X:%X", ssr >> 4, ssr & 0x0f); -} -#endif - -static -void print_sbic_status(int asr, int ssr, int cmdphase) -{ -#ifdef CONFIG_ACORNSCSI_CONSTANTS - printk("sbic: %c%c%c%c%c%c ", - asr & ASR_INT ? 'I' : 'i', - asr & ASR_LCI ? 'L' : 'l', - asr & ASR_BSY ? 'B' : 'b', - asr & ASR_CIP ? 'C' : 'c', - asr & ASR_PE ? 'P' : 'p', - asr & ASR_DBR ? 'D' : 'd'); - printk("scsi: "); - print_scsi_status(ssr); - printk(" ph %02X\n", cmdphase); -#else - printk("sbic: %02X scsi: %X:%X ph: %02X\n", - asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase); -#endif -} - -static void -acornscsi_dumplogline(AS_Host *host, int target, int line) -{ - unsigned long prev; - signed int ptr; - - ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; - if (ptr < 0) - ptr += STATUS_BUFFER_SIZE; - - printk("%c: %3s:", target == 8 ? 'H' : '0' + target, - line == 0 ? "ph" : line == 1 ? "ssr" : "int"); - - prev = host->status[target][ptr].when; - - for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { - unsigned long time_diff; - - if (!host->status[target][ptr].when) - continue; - - switch (line) { - case 0: - printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', - host->status[target][ptr].ph); - break; - - case 1: - printk(" %02X", host->status[target][ptr].ssr); - break; - - case 2: - time_diff = host->status[target][ptr].when - prev; - prev = host->status[target][ptr].when; - if (time_diff == 0) - printk("==^"); - else if (time_diff >= 100) - printk(" "); - else - printk(" %02ld", time_diff); - break; - } - } - - printk("\n"); -} - -static -void acornscsi_dumplog(AS_Host *host, int target) -{ - do { - acornscsi_dumplogline(host, target, 0); - acornscsi_dumplogline(host, target, 1); - acornscsi_dumplogline(host, target, 2); - - if (target == 8) - break; - - target = 8; - } while (1); -} - -static -char acornscsi_target(AS_Host *host) -{ - if (host->SCpnt) - return '0' + host->SCpnt->device->id; - return 'H'; -} - -/* - * Prototype: cmdtype_t acornscsi_cmdtype(int command) - * Purpose : differentiate READ from WRITE from other commands - * Params : command - command to interpret - * Returns : CMD_READ - command reads data, - * CMD_WRITE - command writes data, - * CMD_MISC - everything else - */ -static inline -cmdtype_t acornscsi_cmdtype(int command) -{ - switch (command) { - case WRITE_6: case WRITE_10: case WRITE_12: - return CMD_WRITE; - case READ_6: case READ_10: case READ_12: - return CMD_READ; - default: - return CMD_MISC; - } -} - -/* - * Prototype: int acornscsi_datadirection(int command) - * Purpose : differentiate between commands that have a DATA IN phase - * and a DATA OUT phase - * Params : command - command to interpret - * Returns : DATADIR_OUT - data out phase expected - * DATADIR_IN - data in phase expected - */ -static -datadir_t acornscsi_datadirection(int command) -{ - switch (command) { - case CHANGE_DEFINITION: case COMPARE: case COPY: - case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: - case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: - case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: - case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: - case WRITE_6: case WRITE_10: case WRITE_VERIFY: - case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME: - case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: - case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW: - case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea: - return DATADIR_OUT; - default: - return DATADIR_IN; - } -} - -/* - * Purpose : provide values for synchronous transfers with 33C93. - * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting - * Modified by Russell King for 8MHz WD33C93A - */ -static struct sync_xfer_tbl { - unsigned int period_ns; - unsigned char reg_value; -} sync_xfer_table[] = { - { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 }, - { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 }, - { 874, 0x70 }, { 999, 0x00 }, { 0, 0 } -}; - -/* - * Prototype: int acornscsi_getperiod(unsigned char syncxfer) - * Purpose : period for the synchronous transfer setting - * Params : syncxfer SYNCXFER register value - * Returns : period in ns. - */ -static -int acornscsi_getperiod(unsigned char syncxfer) -{ - int i; - - syncxfer &= 0xf0; - if (syncxfer == 0x10) - syncxfer = 0; - - for (i = 1; sync_xfer_table[i].period_ns; i++) - if (syncxfer == sync_xfer_table[i].reg_value) - return sync_xfer_table[i].period_ns; - return 0; -} - -/* - * Prototype: int round_period(unsigned int period) - * Purpose : return index into above table for a required REQ period - * Params : period - time (ns) for REQ - * Returns : table index - * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting - */ -static inline -int round_period(unsigned int period) -{ - int i; - - for (i = 1; sync_xfer_table[i].period_ns; i++) { - if ((period <= sync_xfer_table[i].period_ns) && - (period > sync_xfer_table[i - 1].period_ns)) - return i; - } - return 7; -} - -/* - * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) - * Purpose : calculate value for 33c93s SYNC register - * Params : period - time (ns) for REQ - * offset - offset in bytes between REQ/ACK - * Returns : value for SYNC register - * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting - */ -static -unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) -{ - return sync_xfer_table[round_period(period)].reg_value | - ((offset < SDTR_SIZE) ? offset : SDTR_SIZE); -} - -/* ==================================================================================== - * Command functions - */ -/* - * Function: acornscsi_kick(AS_Host *host) - * Purpose : kick next command to interface - * Params : host - host to send command to - * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING - * Notes : interrupts are always disabled! - */ -static -intr_ret_t acornscsi_kick(AS_Host *host) -{ - int from_queue = 0; - Scsi_Cmnd *SCpnt; - - /* first check to see if a command is waiting to be executed */ - SCpnt = host->origSCpnt; - host->origSCpnt = NULL; - - /* retrieve next command */ - if (!SCpnt) { - SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); - if (!SCpnt) - return INTR_IDLE; - - from_queue = 1; - } - - if (host->scsi.disconnectable && host->SCpnt) { - queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); - host->scsi.disconnectable = 0; -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", - host->host->host_no, acornscsi_target(host))); -#endif - host->SCpnt = NULL; - } - - /* - * If we have an interrupt pending, then we may have been reselected. - * In this case, we don't want to write to the registers - */ - if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { - sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->device->id); - sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN); - } - - /* - * claim host busy - all of these must happen atomically wrt - * our interrupt routine. Failure means command loss. - */ - host->scsi.phase = PHASE_CONNECTING; - host->SCpnt = SCpnt; - host->scsi.SCp = SCpnt->SCp; - host->dma.xfer_setup = 0; - host->dma.xfer_required = 0; - host->dma.xfer_done = 0; - -#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) - DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", - host->host->host_no, '0' + SCpnt->device->id, - SCpnt->cmnd[0])); -#endif - - if (from_queue) { -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - /* - * tagged queueing - allocate a new tag to this command - */ - if (SCpnt->device->tagged_queue) { - SCpnt->device->current_tag += 1; - if (SCpnt->device->current_tag == 0) - SCpnt->device->current_tag = 1; - SCpnt->tag = SCpnt->device->current_tag; - } else -#endif - set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); - - host->stats.removes += 1; - - switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { - case CMD_WRITE: - host->stats.writes += 1; - break; - case CMD_READ: - host->stats.reads += 1; - break; - case CMD_MISC: - host->stats.miscs += 1; - break; - } - } - - return INTR_PROCESSING; -} - -/* - * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) - * Purpose : complete processing for command - * Params : host - interface that completed - * result - driver byte of result - */ -static -void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) -{ - Scsi_Cmnd *SCpnt = *SCpntp; - - /* clean up */ - sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - - host->stats.fins += 1; - - if (SCpnt) { - *SCpntp = NULL; - - acornscsi_dma_cleanup(host); - - SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; - - /* - * In theory, this should not happen. In practice, it seems to. - * Only trigger an error if the device attempts to report all happy - * but with untransferred buffers... If we don't do something, then - * data loss will occur. Should we check SCpnt->underflow here? - * It doesn't appear to be set to something meaningful by the higher - * levels all the time. - */ - if (result == DID_OK) { - int xfer_warn = 0; - - if (SCpnt->underflow == 0) { - if (host->scsi.SCp.ptr && - acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) - xfer_warn = 1; - } else { - if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || - host->scsi.SCp.scsi_xferred != host->dma.transferred) - xfer_warn = 1; - } - - /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) - * Targets which break data transfers into multiple - * connections shall end each successful connection - * (except possibly the last) with a SAVE DATA - * POINTER - DISCONNECT message sequence. - * - * This makes it difficult to ensure that a transfer has - * completed. If we reach the end of a transfer during - * the command, then we can only have finished the transfer. - * therefore, if we seem to have some data remaining, this - * is not a problem. - */ - if (host->dma.xfer_done) - xfer_warn = 0; - - if (xfer_warn) { - switch (status_byte(SCpnt->result)) { - case CHECK_CONDITION: - case COMMAND_TERMINATED: - case BUSY: - case QUEUE_FULL: - case RESERVATION_CONFLICT: - break; - - default: - printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", - host->host->host_no, SCpnt->result); - print_command(SCpnt->cmnd); - acornscsi_dumpdma(host, "done"); - acornscsi_dumplog(host, SCpnt->device->id); - SCpnt->result &= 0xffff; - SCpnt->result |= DID_ERROR << 16; - } - } - } - - if (!SCpnt->scsi_done) - panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); - - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); - - SCpnt->scsi_done(SCpnt); - } else - printk("scsi%d: null command in acornscsi_done", host->host->host_no); - - host->scsi.phase = PHASE_IDLE; -} - -/* ==================================================================================== - * DMA routines - */ -/* - * Purpose : update SCSI Data Pointer - * Notes : this will only be one SG entry or less - */ -static -void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length) -{ - SCp->ptr += length; - SCp->this_residual -= length; - - if (SCp->this_residual == 0 && next_SCp(SCp) == 0) - host->dma.xfer_done = 1; -} - -/* - * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, - * unsigned int start_addr, unsigned int length) - * Purpose : read data from DMA RAM - * Params : host - host to transfer from - * ptr - DRAM address - * start_addr - host mem address - * length - number of bytes to transfer - * Notes : this will only be one SG entry or less - */ -static -void acornscsi_data_read(AS_Host *host, char *ptr, - unsigned int start_addr, unsigned int length) -{ - extern void __acornscsi_in(int port, char *buf, int len); - unsigned int page, offset, len = length; - - page = (start_addr >> 12); - offset = start_addr & ((1 << 12) - 1); - - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - - while (len > 0) { - unsigned int this_len; - - if (len + offset > (1 << 12)) - this_len = (1 << 12) - offset; - else - this_len = len; - - __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len); - - offset += this_len; - ptr += this_len; - len -= this_len; - - if (offset == (1 << 12)) { - offset = 0; - page ++; - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - } - } - outb(host->card.page_reg, host->card.io_page); -} - -/* - * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, - * unsigned int start_addr, unsigned int length) - * Purpose : write data to DMA RAM - * Params : host - host to transfer from - * ptr - DRAM address - * start_addr - host mem address - * length - number of bytes to transfer - * Notes : this will only be one SG entry or less - */ -static -void acornscsi_data_write(AS_Host *host, char *ptr, - unsigned int start_addr, unsigned int length) -{ - extern void __acornscsi_out(int port, char *buf, int len); - unsigned int page, offset, len = length; - - page = (start_addr >> 12); - offset = start_addr & ((1 << 12) - 1); - - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - - while (len > 0) { - unsigned int this_len; - - if (len + offset > (1 << 12)) - this_len = (1 << 12) - offset; - else - this_len = len; - - __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len); - - offset += this_len; - ptr += this_len; - len -= this_len; - - if (offset == (1 << 12)) { - offset = 0; - page ++; - outb((page & 0x3f) | host->card.page_reg, host->card.io_page); - } - } - outb(host->card.page_reg, host->card.io_page); -} - -/* ========================================================================================= - * On-board DMA routines - */ -#ifdef USE_DMAC -/* - * Prototype: void acornscsi_dmastop(AS_Host *host) - * Purpose : stop all DMA - * Params : host - host on which to stop DMA - * Notes : This is called when leaving DATA IN/OUT phase, - * or when interface is RESET - */ -static inline -void acornscsi_dma_stop(AS_Host *host) -{ - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr(host->dma.io_intr_clear); - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "stop")); -#endif -} - -/* - * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) - * Purpose : setup DMA controller for data transfer - * Params : host - host to setup - * direction - data transfer direction - * Notes : This is called when entering DATA I/O phase, not - * while we're in a DATA I/O phase - */ -static -void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) -{ - unsigned int address, length, mode; - - host->dma.direction = direction; - - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - - if (direction == DMA_OUT) { -#if (DEBUG & DEBUG_NO_WRITE) - if (NO_WRITE & (1 << host->SCpnt->device->id)) { - printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", - host->host->host_no, acornscsi_target(host)); - return; - } -#endif - mode = DMAC_WRITE; - } else - mode = DMAC_READ; - - /* - * Allocate some buffer space, limited to half the buffer size - */ - length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); - if (length) { - host->dma.start_addr = address = host->dma.free_addr; - host->dma.free_addr = (host->dma.free_addr + length) & - (DMAC_BUFFER_SIZE - 1); - - /* - * Transfer data to DMA memory - */ - if (direction == DMA_OUT) - acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, - length); - - length -= 1; - dmac_write(host->dma.io_port, TXCNTLO, length); - dmac_write(host->dma.io_port, TXCNTHI, length >> 8); - dmac_write(host->dma.io_port, TXADRLO, address); - dmac_write(host->dma.io_port, TXADRMD, address >> 8); - dmac_write(host->dma.io_port, TXADRHI, 0); - dmac_write(host->dma.io_port, MODECON, mode); - dmac_write(host->dma.io_port, MASKREG, MASK_OFF); - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "strt")); -#endif - host->dma.xfer_setup = 1; - } -} - -/* - * Function: void acornscsi_dma_cleanup(AS_Host *host) - * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct - * Params : host - host to finish - * Notes : This is called when a command is: - * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT - * : This must not return until all transfers are completed. - */ -static -void acornscsi_dma_cleanup(AS_Host *host) -{ - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr(host->dma.io_intr_clear); - - /* - * Check for a pending transfer - */ - if (host->dma.xfer_required) { - host->dma.xfer_required = 0; - if (host->dma.direction == DMA_IN) - acornscsi_data_read(host, host->dma.xfer_ptr, - host->dma.xfer_start, host->dma.xfer_length); - } - - /* - * Has a transfer been setup? - */ - if (host->dma.xfer_setup) { - unsigned int transferred; - - host->dma.xfer_setup = 0; - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi")); -#endif - - /* - * Calculate number of bytes transferred from DMA. - */ - transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; - host->dma.transferred += transferred; - - if (host->dma.direction == DMA_IN) - acornscsi_data_read(host, host->scsi.SCp.ptr, - host->dma.start_addr, transferred); - - /* - * Update SCSI pointers - */ - acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo")); -#endif - } -} - -/* - * Function: void acornscsi_dmacintr(AS_Host *host) - * Purpose : handle interrupts from DMAC device - * Params : host - host to process - * Notes : If reading, we schedule the read to main memory & - * allow the transfer to continue. - * : If writing, we fill the onboard DMA memory from main - * memory. - * : Called whenever DMAC finished it's current transfer. - */ -static -void acornscsi_dma_intr(AS_Host *host) -{ - unsigned int address, length, transferred; - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "inti")); -#endif - - dmac_write(host->dma.io_port, MASKREG, MASK_ON); - dmac_clearintr(host->dma.io_intr_clear); - - /* - * Calculate amount transferred via DMA - */ - transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; - host->dma.transferred += transferred; - - /* - * Schedule DMA transfer off board - */ - if (host->dma.direction == DMA_IN) { - host->dma.xfer_start = host->dma.start_addr; - host->dma.xfer_length = transferred; - host->dma.xfer_ptr = host->scsi.SCp.ptr; - host->dma.xfer_required = 1; - } - - acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); - - /* - * Allocate some buffer space, limited to half the on-board RAM size - */ - length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); - if (length) { - host->dma.start_addr = address = host->dma.free_addr; - host->dma.free_addr = (host->dma.free_addr + length) & - (DMAC_BUFFER_SIZE - 1); - - /* - * Transfer data to DMA memory - */ - if (host->dma.direction == DMA_OUT) - acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, - length); - - length -= 1; - dmac_write(host->dma.io_port, TXCNTLO, length); - dmac_write(host->dma.io_port, TXCNTHI, length >> 8); - dmac_write(host->dma.io_port, TXADRLO, address); - dmac_write(host->dma.io_port, TXADRMD, address >> 8); - dmac_write(host->dma.io_port, TXADRHI, 0); - dmac_write(host->dma.io_port, MASKREG, MASK_OFF); - -#if (DEBUG & DEBUG_DMA) - DBG(host->SCpnt, acornscsi_dumpdma(host, "into")); -#endif - } else { - host->dma.xfer_setup = 0; -#if 0 - /* - * If the interface still wants more, then this is an error. - * We give it another byte, but we also attempt to raise an - * attention condition. We continue giving one byte until - * the device recognises the attention. - */ - if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) { - acornscsi_abortcmd(host, host->SCpnt->tag); - - dmac_write(host->dma.io_port, TXCNTLO, 0); - dmac_write(host->dma.io_port, TXCNTHI, 0); - dmac_write(host->dma.io_port, TXADRLO, 0); - dmac_write(host->dma.io_port, TXADRMD, 0); - dmac_write(host->dma.io_port, TXADRHI, 0); - dmac_write(host->dma.io_port, MASKREG, MASK_OFF); - } -#endif - } -} - -/* - * Function: void acornscsi_dma_xfer(AS_Host *host) - * Purpose : transfer data between AcornSCSI and memory - * Params : host - host to process - */ -static -void acornscsi_dma_xfer(AS_Host *host) -{ - host->dma.xfer_required = 0; - - if (host->dma.direction == DMA_IN) - acornscsi_data_read(host, host->dma.xfer_ptr, - host->dma.xfer_start, host->dma.xfer_length); -} - -/* - * Function: void acornscsi_dma_adjust(AS_Host *host) - * Purpose : adjust DMA pointers & count for bytes transferred to - * SBIC but not SCSI bus. - * Params : host - host to adjust DMA count for - */ -static -void acornscsi_dma_adjust(AS_Host *host) -{ - if (host->dma.xfer_setup) { - signed long transferred; -#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma(host, "adji")); -#endif - /* - * Calculate correct DMA address - DMA is ahead of SCSI bus while - * writing. - * host->scsi.SCp.scsi_xferred is the number of bytes - * actually transferred to/from the SCSI bus. - * host->dma.transferred is the number of bytes transferred - * over DMA since host->dma.start_addr was last set. - * - * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred - * - host->dma.transferred - */ - transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; - if (transferred < 0) - printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", - host->host->host_no, acornscsi_target(host), transferred); - else if (transferred == 0) - host->dma.xfer_setup = 0; - else { - transferred += host->dma.start_addr; - dmac_write(host->dma.io_port, TXADRLO, transferred); - dmac_write(host->dma.io_port, TXADRMD, transferred >> 8); - dmac_write(host->dma.io_port, TXADRHI, transferred >> 16); -#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) - DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo")); -#endif - } - } -} -#endif - -/* ========================================================================================= - * Data I/O - */ -static int -acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout) -{ - unsigned int asr, timeout = max_timeout; - int my_ptr = *ptr; - - while (my_ptr < len) { - asr = sbic_arm_read(host->scsi.io_port, ASR); - - if (asr & ASR_DBR) { - timeout = max_timeout; - - sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]); - } else if (asr & ASR_INT) - break; - else if (--timeout == 0) - break; - udelay(1); - } - - *ptr = my_ptr; - - return (timeout == 0) ? -1 : 0; -} - -/* - * Function: void acornscsi_sendcommand(AS_Host *host) - * Purpose : send a command to a target - * Params : host - host which is connected to target - */ -static void -acornscsi_sendcommand(AS_Host *host) -{ - Scsi_Cmnd *SCpnt = host->SCpnt; - - sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext(host->scsi.io_port, 0); - sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); - - acornscsi_sbic_issuecmd(host, CMND_XFERINFO); - - if (acornscsi_write_pio(host, SCpnt->cmnd, - (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) - printk("scsi%d: timeout while sending command\n", host->host->host_no); - - host->scsi.phase = PHASE_COMMAND; -} - -static -void acornscsi_sendmessage(AS_Host *host) -{ - unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); - unsigned int msgnr; - struct message *msg; - -#if (DEBUG & DEBUG_MESSAGES) - printk("scsi%d.%c: sending message ", - host->host->host_no, acornscsi_target(host)); -#endif - - switch (message_length) { - case 0: - acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); - - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1"); - - sbic_arm_write(host->scsi.io_port, DATA, NOP); - - host->scsi.last_message = NOP; -#if (DEBUG & DEBUG_MESSAGES) - printk("NOP"); -#endif - break; - - case 1: - acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); - msg = msgqueue_getmsg(&host->scsi.msgs, 0); - - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2"); - - sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]); - - host->scsi.last_message = msg->msg[0]; -#if (DEBUG & DEBUG_MESSAGES) - print_msg(msg->msg); -#endif - break; - - default: - /* - * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14) - * 'When a target sends this (MESSAGE_REJECT) message, it - * shall change to MESSAGE IN phase and send this message - * prior to requesting additional message bytes from the - * initiator. This provides an interlock so that the - * initiator can determine which message byte is rejected. - */ - sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); - sbic_arm_writenext(host->scsi.io_port, 0); - sbic_arm_writenext(host->scsi.io_port, message_length); - acornscsi_sbic_issuecmd(host, CMND_XFERINFO); - - msgnr = 0; - while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) { - unsigned int i; -#if (DEBUG & DEBUG_MESSAGES) - print_msg(msg); -#endif - i = 0; - if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000)) - printk("scsi%d: timeout while sending message\n", host->host->host_no); - - host->scsi.last_message = msg->msg[0]; - if (msg->msg[0] == EXTENDED_MESSAGE) - host->scsi.last_message |= msg->msg[2] << 8; - - if (i != msg->length) - break; - } - break; - } -#if (DEBUG & DEBUG_MESSAGES) - printk("\n"); -#endif -} - -/* - * Function: void acornscsi_readstatusbyte(AS_Host *host) - * Purpose : Read status byte from connected target - * Params : host - host connected to target - */ -static -void acornscsi_readstatusbyte(AS_Host *host) -{ - acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT); - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte"); - host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA); -} - -/* - * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host) - * Purpose : Read one message byte from connected target - * Params : host - host connected to target - */ -static -unsigned char acornscsi_readmessagebyte(AS_Host *host) -{ - unsigned char message; - - acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); - - acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte"); - - message = sbic_arm_read(host->scsi.io_port, DATA); - - /* wait for MSGIN-XFER-PAUSED */ - acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte"); - - sbic_arm_read(host->scsi.io_port, SSR); - - return message; -} - -/* - * Function: void acornscsi_message(AS_Host *host) - * Purpose : Read complete message from connected target & action message - * Params : host - host connected to target - */ -static -void acornscsi_message(AS_Host *host) -{ - unsigned char message[16]; - unsigned int msgidx = 0, msglen = 1; - - do { - message[msgidx] = acornscsi_readmessagebyte(host); - - switch (msgidx) { - case 0: - if (message[0] == EXTENDED_MESSAGE || - (message[0] >= 0x20 && message[0] <= 0x2f)) - msglen = 2; - break; - - case 1: - if (message[0] == EXTENDED_MESSAGE) - msglen += message[msgidx]; - break; - } - msgidx += 1; - if (msgidx < msglen) { - acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); - - /* wait for next msg-in */ - acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack"); - sbic_arm_read(host->scsi.io_port, SSR); - } - } while (msgidx < msglen); - -#if (DEBUG & DEBUG_MESSAGES) - printk("scsi%d.%c: message in: ", - host->host->host_no, acornscsi_target(host)); - print_msg(message); - printk("\n"); -#endif - - if (host->scsi.phase == PHASE_RECONNECTED) { - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) - * 'Whenever a target reconnects to an initiator to continue - * a tagged I/O process, the SIMPLE QUEUE TAG message shall - * be sent immediately following the IDENTIFY message...' - */ - if (message[0] == SIMPLE_QUEUE_TAG) - host->scsi.reconnected.tag = message[1]; - if (acornscsi_reconnect_finish(host)) - host->scsi.phase = PHASE_MSGIN; - } - - switch (message[0]) { - case ABORT: - case ABORT_TAG: - case COMMAND_COMPLETE: - if (host->scsi.phase != PHASE_STATUSIN) { - printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n", - host->host->host_no, acornscsi_target(host)); - acornscsi_dumplog(host, host->SCpnt->device->id); - } - host->scsi.phase = PHASE_DONE; - host->scsi.SCp.Message = message[0]; - break; - - case SAVE_POINTERS: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20) - * 'The SAVE DATA POINTER message is sent from a target to - * direct the initiator to copy the active data pointer to - * the saved data pointer for the current I/O process. - */ - acornscsi_dma_cleanup(host); - host->SCpnt->SCp = host->scsi.SCp; - host->SCpnt->SCp.sent_command = 0; - host->scsi.phase = PHASE_MSGIN; - break; - - case RESTORE_POINTERS: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19) - * 'The RESTORE POINTERS message is sent from a target to - * direct the initiator to copy the most recently saved - * command, data, and status pointers for the I/O process - * to the corresponding active pointers. The command and - * status pointers shall be restored to the beginning of - * the present command and status areas.' - */ - acornscsi_dma_cleanup(host); - host->scsi.SCp = host->SCpnt->SCp; - host->scsi.phase = PHASE_MSGIN; - break; - - case DISCONNECT: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2) - * 'On those occasions when an error or exception condition occurs - * and the target elects to repeat the information transfer, the - * target may repeat the transfer either issuing a RESTORE POINTERS - * message or by disconnecting without issuing a SAVE POINTERS - * message. When reconnection is completed, the most recent - * saved pointer values are restored.' - */ - acornscsi_dma_cleanup(host); - host->scsi.phase = PHASE_DISCONNECT; - break; - - case MESSAGE_REJECT: -#if 0 /* this isn't needed any more */ - /* - * If we were negociating sync transfer, we don't yet know if - * this REJECT is for the sync transfer or for the tagged queue/wide - * transfer. Re-initiate sync transfer negociation now, and if - * we got a REJECT in response to SDTR, then it'll be set to DONE. - */ - if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) - host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE; -#endif - - /* - * If we have any messages waiting to go out, then assert ATN now - */ - if (msgqueue_msglength(&host->scsi.msgs)) - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - - switch (host->scsi.last_message) { -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - case HEAD_OF_QUEUE_TAG: - case ORDERED_QUEUE_TAG: - case SIMPLE_QUEUE_TAG: - /* - * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) - * If a target does not implement tagged queuing and a queue tag - * message is received, it shall respond with a MESSAGE REJECT - * message and accept the I/O process as if it were untagged. - */ - printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", - host->host->host_no, acornscsi_target(host)); - host->SCpnt->device->tagged_queue = 0; - set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns); - break; -#endif - case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): - /* - * Target can't handle synchronous transfers - */ - printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", - host->host->host_no, acornscsi_target(host)); - host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA; - host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS; - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); - break; - - default: - break; - } - break; - - case QUEUE_FULL: - /* TODO: target queue is full */ - break; - - case SIMPLE_QUEUE_TAG: - /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */ - printk("scsi%d.%c: reconnect queue tag %02X\n", - host->host->host_no, acornscsi_target(host), - message[1]); - break; - - case EXTENDED_MESSAGE: - switch (message[2]) { -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - case EXTENDED_SDTR: - if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) { - /* - * We requested synchronous transfers. This isn't quite right... - * We can only say if this succeeded if we proceed on to execute the - * command from this message. If we get a MESSAGE PARITY ERROR, - * and the target retries fail, then we fallback to asynchronous mode - */ - host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED; - printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", - host->host->host_no, acornscsi_target(host), - message[4], message[3] * 4); - host->device[host->SCpnt->device->id].sync_xfer = - calc_sync_xfer(message[3] * 4, message[4]); - } else { - unsigned char period, length; - /* - * Target requested synchronous transfers. The agreement is only - * to be in operation AFTER the target leaves message out phase. - */ - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - period = max_t(unsigned int, message[3], sdtr_period / 4); - length = min_t(unsigned int, message[4], sdtr_size); - msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, - EXTENDED_SDTR, period, length); - host->device[host->SCpnt->device->id].sync_xfer = - calc_sync_xfer(period * 4, length); - } - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); - break; -#else - /* We do not accept synchronous transfers. Respond with a - * MESSAGE_REJECT. - */ -#endif - - case EXTENDED_WDTR: - /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT - * to a wide data transfer request. - */ - default: - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - msgqueue_flush(&host->scsi.msgs); - msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); - break; - } - break; - -#ifdef CONFIG_SCSI_ACORNSCSI_LINK - case LINKED_CMD_COMPLETE: - case LINKED_FLG_CMD_COMPLETE: - /* - * We don't support linked commands yet - */ - if (0) { -#if (DEBUG & DEBUG_LINK) - printk("scsi%d.%c: lun %d tag %d linked command complete\n", - host->host->host_no, acornscsi_target(host), host->SCpnt->tag); -#endif - /* - * A linked command should only terminate with one of these messages - * if there are more linked commands available. - */ - if (!host->SCpnt->next_link) { - printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", - instance->host_no, acornscsi_target(host), host->SCpnt->tag); - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); - } else { - Scsi_Cmnd *SCpnt = host->SCpnt; - - acornscsi_dma_cleanup(host); - - host->SCpnt = host->SCpnt->next_link; - host->SCpnt->tag = SCpnt->tag; - SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status; - SCpnt->done(SCpnt); - - /* initialise host->SCpnt->SCp */ - } - break; - } -#endif - - default: /* reject message */ - printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", - host->host->host_no, acornscsi_target(host), - message[0]); - acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); - msgqueue_flush(&host->scsi.msgs); - msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); - host->scsi.phase = PHASE_MSGIN; - break; - } - acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); -} - -/* - * Function: int acornscsi_buildmessages(AS_Host *host) - * Purpose : build the connection messages for a host - * Params : host - host to add messages to - */ -static -void acornscsi_buildmessages(AS_Host *host) -{ -#if 0 - /* does the device need resetting? */ - if (cmd_reset) { - msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET); - return; - } -#endif - - msgqueue_addmsg(&host->scsi.msgs, 1, - IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok, - host->SCpnt->device->lun)); - -#if 0 - /* does the device need the current command aborted */ - if (cmd_aborted) { - acornscsi_abortcmd(host->SCpnt->tag); - return; - } -#endif - -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - if (host->SCpnt->tag) { - unsigned int tag_type; - - if (host->SCpnt->cmnd[0] == REQUEST_SENSE || - host->SCpnt->cmnd[0] == TEST_UNIT_READY || - host->SCpnt->cmnd[0] == INQUIRY) - tag_type = HEAD_OF_QUEUE_TAG; - else - tag_type = SIMPLE_QUEUE_TAG; - msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); - } -#endif - -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) { - host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST; - msgqueue_addmsg(&host->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - sdtr_period / 4, sdtr_size); - } -#endif -} - -/* - * Function: int acornscsi_starttransfer(AS_Host *host) - * Purpose : transfer data to/from connected target - * Params : host - host to which target is connected - * Returns : 0 if failure - */ -static -int acornscsi_starttransfer(AS_Host *host) -{ - int residual; - - if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) { - printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", - host->host->host_no, acornscsi_target(host)); - return 0; - } - - residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred; - - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); - sbic_arm_writenext(host->scsi.io_port, residual >> 16); - sbic_arm_writenext(host->scsi.io_port, residual >> 8); - sbic_arm_writenext(host->scsi.io_port, residual); - acornscsi_sbic_issuecmd(host, CMND_XFERINFO); - return 1; -} - -/* ========================================================================================= - * Connection & Disconnection - */ -/* - * Function : acornscsi_reconnect(AS_Host *host) - * Purpose : reconnect a previously disconnected command - * Params : host - host specific data - * Remarks : SCSI spec says: - * 'The set of active pointers is restored from the set - * of saved pointers upon reconnection of the I/O process' - */ -static -int acornscsi_reconnect(AS_Host *host) -{ - unsigned int target, lun, ok = 0; - - target = sbic_arm_read(host->scsi.io_port, SOURCEID); - - if (!(target & 8)) - printk(KERN_ERR "scsi%d: invalid source id after reselection " - "- device fault?\n", - host->host->host_no); - - target &= 7; - - if (host->SCpnt && !host->scsi.disconnectable) { - printk(KERN_ERR "scsi%d.%d: reconnected while command in " - "progress to target %d?\n", - host->host->host_no, target, host->SCpnt->device->id); - host->SCpnt = NULL; - } - - lun = sbic_arm_read(host->scsi.io_port, DATA) & 7; - - host->scsi.reconnected.target = target; - host->scsi.reconnected.lun = lun; - host->scsi.reconnected.tag = 0; - - if (host->scsi.disconnectable && host->SCpnt && - host->SCpnt->device->id == target && host->SCpnt->device->lun == lun) - ok = 1; - - if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) - ok = 1; - - ADD_STATUS(target, 0x81, host->scsi.phase, 0); - - if (ok) { - host->scsi.phase = PHASE_RECONNECTED; - } else { - /* this doesn't seem to work */ - printk(KERN_ERR "scsi%d.%c: reselected with no command " - "to reconnect with\n", - host->host->host_no, '0' + target); - acornscsi_dumplog(host, target); - acornscsi_abortcmd(host, 0); - if (host->SCpnt) { - queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); - host->SCpnt = NULL; - } - } - acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); - return !ok; -} - -/* - * Function: int acornscsi_reconect_finish(AS_Host *host) - * Purpose : finish reconnecting a command - * Params : host - host to complete - * Returns : 0 if failed - */ -static -int acornscsi_reconnect_finish(AS_Host *host) -{ - if (host->scsi.disconnectable && host->SCpnt) { - host->scsi.disconnectable = 0; - if (host->SCpnt->device->id == host->scsi.reconnected.target && - host->SCpnt->device->lun == host->scsi.reconnected.lun && - host->SCpnt->tag == host->scsi.reconnected.tag) { -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: reconnected", - host->host->host_no, acornscsi_target(host))); -#endif - } else { - queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: had to move command " - "to disconnected queue\n", - host->host->host_no, acornscsi_target(host))); -#endif - host->SCpnt = NULL; - } - } - if (!host->SCpnt) { - host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, - host->scsi.reconnected.target, - host->scsi.reconnected.lun, - host->scsi.reconnected.tag); -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - DBG(host->SCpnt, printk("scsi%d.%c: had to get command", - host->host->host_no, acornscsi_target(host))); -#endif - } - - if (!host->SCpnt) - acornscsi_abortcmd(host, host->scsi.reconnected.tag); - else { - /* - * Restore data pointer from SAVED pointers. - */ - host->scsi.SCp = host->SCpnt->SCp; -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk(", data pointers: [%p, %X]", - host->scsi.SCp.ptr, host->scsi.SCp.this_residual); -#endif - } -#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) - printk("\n"); -#endif - - host->dma.transferred = host->scsi.SCp.scsi_xferred; - - return host->SCpnt != NULL; -} - -/* - * Function: void acornscsi_disconnect_unexpected(AS_Host *host) - * Purpose : handle an unexpected disconnect - * Params : host - host on which disconnect occurred - */ -static -void acornscsi_disconnect_unexpected(AS_Host *host) -{ - printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", - host->host->host_no, acornscsi_target(host)); -#if (DEBUG & DEBUG_ABORT) - acornscsi_dumplog(host, 8); -#endif - - acornscsi_done(host, &host->SCpnt, DID_ERROR); -} - -/* - * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) - * Purpose : abort a currently executing command - * Params : host - host with connected command to abort - * tag - tag to abort - */ -static -void acornscsi_abortcmd(AS_Host *host, unsigned char tag) -{ - host->scsi.phase = PHASE_ABORTED; - sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN); - - msgqueue_flush(&host->scsi.msgs); -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - if (tag) - msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); - else -#endif - msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); -} - -/* ========================================================================================== - * Interrupt routines. - */ -/* - * Function: int acornscsi_sbicintr(AS_Host *host) - * Purpose : handle interrupts from SCSI device - * Params : host - host to process - * Returns : INTR_PROCESS if expecting another SBIC interrupt - * INTR_IDLE if no interrupt - * INTR_NEXT_COMMAND if we have finished processing the command - */ -static -intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq) -{ - unsigned int asr, ssr; - - asr = sbic_arm_read(host->scsi.io_port, ASR); - if (!(asr & ASR_INT)) - return INTR_IDLE; - - ssr = sbic_arm_read(host->scsi.io_port, SSR); - -#if (DEBUG & DEBUG_PHASES) - print_sbic_status(asr, ssr, host->scsi.phase); -#endif - - ADD_STATUS(8, ssr, host->scsi.phase, in_irq); - - if (host->SCpnt && !host->scsi.disconnectable) - ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); - - switch (ssr) { - case 0x00: /* reset state - not advanced */ - printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", - host->host->host_no); - /* setup sbic - WD33C93A */ - sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); - sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); - return INTR_IDLE; - - case 0x01: /* reset state - advanced */ - sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); - sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); - sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); - sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); - msgqueue_flush(&host->scsi.msgs); - return INTR_IDLE; - - case 0x41: /* unexpected disconnect aborted command */ - acornscsi_disconnect_unexpected(host); - return INTR_NEXT_COMMAND; - } - - switch (host->scsi.phase) { - case PHASE_CONNECTING: /* STATE: command removed from issue queue */ - switch (ssr) { - case 0x11: /* -> PHASE_CONNECTED */ - /* BUS FREE -> SELECTION */ - host->scsi.phase = PHASE_CONNECTED; - msgqueue_flush(&host->scsi.msgs); - host->dma.transferred = host->scsi.SCp.scsi_xferred; - /* 33C93 gives next interrupt indicating bus phase */ - asr = sbic_arm_read(host->scsi.io_port, ASR); - if (!(asr & ASR_INT)) - break; - ssr = sbic_arm_read(host->scsi.io_port, SSR); - ADD_STATUS(8, ssr, host->scsi.phase, 1); - ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1); - goto connected; - - case 0x42: /* select timed out */ - /* -> PHASE_IDLE */ - acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); - return INTR_NEXT_COMMAND; - - case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ - /* BUS FREE -> RESELECTION */ - host->origSCpnt = host->SCpnt; - host->SCpnt = NULL; - msgqueue_flush(&host->scsi.msgs); - acornscsi_reconnect(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - acornscsi_abortcmd(host, host->SCpnt->tag); - } - return INTR_PROCESSING; - - connected: - case PHASE_CONNECTED: /* STATE: device selected ok */ - switch (ssr) { -#ifdef NONSTANDARD - case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - /* SELECTION -> COMMAND */ - acornscsi_sendcommand(host); - break; - - case 0x8b: /* -> PHASE_STATUS */ - /* SELECTION -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; -#endif - - case 0x8e: /* -> PHASE_MSGOUT */ - /* SELECTION ->MESSAGE OUT */ - host->scsi.phase = PHASE_MSGOUT; - acornscsi_buildmessages(host); - acornscsi_sendmessage(host); - break; - - /* these should not happen */ - case 0x85: /* target disconnected */ - acornscsi_done(host, &host->SCpnt, DID_ERROR); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - acornscsi_abortcmd(host, host->SCpnt->tag); - } - return INTR_PROCESSING; - - case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ - /* - * SCSI standard says that MESSAGE OUT phases can be followed by a - * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase - */ - switch (ssr) { - case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - /* MESSAGE OUT -> COMMAND */ - acornscsi_sendcommand(host); - break; - - case 0x8b: /* -> PHASE_STATUS */ - case 0x1b: /* -> PHASE_STATUS */ - /* MESSAGE OUT -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x8e: /* -> PHASE_MSGOUT */ - /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - /* MESSAGE OUT -> MESSAGE IN */ - acornscsi_message(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_COMMAND: /* STATE: connected & command sent */ - switch (ssr) { - case 0x18: /* -> PHASE_DATAOUT */ - /* COMMAND -> DATA OUT */ - if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd(host, host->SCpnt->tag); - acornscsi_dma_setup(host, DMA_OUT); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAOUT; - return INTR_IDLE; - - case 0x19: /* -> PHASE_DATAIN */ - /* COMMAND -> DATA IN */ - if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) - acornscsi_abortcmd(host, host->SCpnt->tag); - acornscsi_dma_setup(host, DMA_IN); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAIN; - return INTR_IDLE; - - case 0x1b: /* -> PHASE_STATUS */ - /* COMMAND -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - /* COMMAND -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - /* COMMAND -> MESSAGE IN */ - acornscsi_message(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */ - if (ssr == 0x85) { /* -> PHASE_IDLE */ - host->scsi.disconnectable = 1; - host->scsi.reconnected.tag = 0; - host->scsi.phase = PHASE_IDLE; - host->stats.disconnects += 1; - } else { - printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_NEXT_COMMAND; - - case PHASE_IDLE: /* STATE: disconnected */ - if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ - acornscsi_reconnect(host); - else { - printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */ - /* - * Command reconnected - if MESGIN, get message - it may be - * the tag. If not, get command out of disconnected queue - */ - /* - * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, - * reconnect I_T_L command - */ - if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) - return INTR_IDLE; - ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); - switch (ssr) { - case 0x88: /* data out phase */ - /* -> PHASE_DATAOUT */ - /* MESSAGE IN -> DATA OUT */ - acornscsi_dma_setup(host, DMA_OUT); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAOUT; - return INTR_IDLE; - - case 0x89: /* data in phase */ - /* -> PHASE_DATAIN */ - /* MESSAGE IN -> DATA IN */ - acornscsi_dma_setup(host, DMA_IN); - if (!acornscsi_starttransfer(host)) - acornscsi_abortcmd(host, host->SCpnt->tag); - host->scsi.phase = PHASE_DATAIN; - return INTR_IDLE; - - case 0x8a: /* command out */ - /* MESSAGE IN -> COMMAND */ - acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ - break; - - case 0x8b: /* status in */ - /* -> PHASE_STATUSIN */ - /* MESSAGE IN -> STATUS */ - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x8e: /* message out */ - /* -> PHASE_MSGOUT */ - /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x8f: /* message in */ - acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DATAIN: /* STATE: transferred data in */ - /* - * This is simple - if we disconnect then the DMA address & count is - * correct. - */ - switch (ssr) { - case 0x19: /* -> PHASE_DATAIN */ - case 0x89: /* -> PHASE_DATAIN */ - acornscsi_abortcmd(host, host->SCpnt->tag); - return INTR_IDLE; - - case 0x1b: /* -> PHASE_STATUSIN */ - case 0x4b: /* -> PHASE_STATUSIN */ - case 0x8b: /* -> PHASE_STATUSIN */ - /* DATA IN -> STATUS */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x4e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* DATA IN -> MESSAGE OUT */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_sendmessage(host); - break; - - case 0x1f: /* message in */ - case 0x4f: /* message in */ - case 0x8f: /* message in */ - /* DATA IN -> MESSAGE IN */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DATAOUT: /* STATE: transferred data out */ - /* - * This is more complicated - if we disconnect, the DMA could be 12 - * bytes ahead of us. We need to correct this. - */ - switch (ssr) { - case 0x18: /* -> PHASE_DATAOUT */ - case 0x88: /* -> PHASE_DATAOUT */ - acornscsi_abortcmd(host, host->SCpnt->tag); - return INTR_IDLE; - - case 0x1b: /* -> PHASE_STATUSIN */ - case 0x4b: /* -> PHASE_STATUSIN */ - case 0x8b: /* -> PHASE_STATUSIN */ - /* DATA OUT -> STATUS */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_dma_adjust(host); - acornscsi_readstatusbyte(host); - host->scsi.phase = PHASE_STATUSIN; - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x4e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* DATA OUT -> MESSAGE OUT */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_dma_adjust(host); - acornscsi_sendmessage(host); - break; - - case 0x1f: /* message in */ - case 0x4f: /* message in */ - case 0x8f: /* message in */ - /* DATA OUT -> MESSAGE IN */ - host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - - acornscsi_sbic_xfcount(host); - acornscsi_dma_stop(host); - acornscsi_dma_adjust(host); - acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_STATUSIN: /* STATE: status in complete */ - switch (ssr) { - case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ - case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ - /* STATUS -> MESSAGE IN */ - acornscsi_message(host); - break; - - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* STATUS -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_MSGIN: /* STATE: message in */ - switch (ssr) { - case 0x1e: /* -> PHASE_MSGOUT */ - case 0x4e: /* -> PHASE_MSGOUT */ - case 0x8e: /* -> PHASE_MSGOUT */ - /* MESSAGE IN -> MESSAGE OUT */ - acornscsi_sendmessage(host); - break; - - case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ - case 0x2f: - case 0x4f: - case 0x8f: - acornscsi_message(host); - break; - - case 0x85: - printk("scsi%d.%c: strange message in disconnection\n", - host->host->host_no, acornscsi_target(host)); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - acornscsi_done(host, &host->SCpnt, DID_ERROR); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_DONE: /* STATE: received status & message */ - switch (ssr) { - case 0x85: /* -> PHASE_IDLE */ - acornscsi_done(host, &host->SCpnt, DID_OK); - return INTR_NEXT_COMMAND; - - case 0x1e: - case 0x8e: - acornscsi_sendmessage(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - case PHASE_ABORTED: - switch (ssr) { - case 0x85: - if (host->SCpnt) - acornscsi_done(host, &host->SCpnt, DID_ABORT); - else { - clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun, - host->busyluns); - host->scsi.phase = PHASE_IDLE; - } - return INTR_NEXT_COMMAND; - - case 0x1e: - case 0x2e: - case 0x4e: - case 0x8e: - acornscsi_sendmessage(host); - break; - - default: - printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; - - default: - printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n", - host->host->host_no, acornscsi_target(host), ssr); - acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); - } - return INTR_PROCESSING; -} - -/* - * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) - * Purpose : handle interrupts from Acorn SCSI card - * Params : irq - interrupt number - * dev_id - device specific data (AS_Host structure) - * regs - processor registers when interrupt occurred - */ -static irqreturn_t -acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - AS_Host *host = (AS_Host *)dev_id; - intr_ret_t ret; - int iostatus; - int in_irq = 0; - - do { - ret = INTR_IDLE; - - iostatus = inb(host->card.io_intr); - - if (iostatus & 2) { - acornscsi_dma_intr(host); - iostatus = inb(host->card.io_intr); - } - - if (iostatus & 8) - ret = acornscsi_sbicintr(host, in_irq); - - /* - * If we have a transfer pending, start it. - * Only start it if the interface has already started transferring - * it's data - */ - if (host->dma.xfer_required) - acornscsi_dma_xfer(host); - - if (ret == INTR_NEXT_COMMAND) - ret = acornscsi_kick(host); - - in_irq = 1; - } while (ret != INTR_IDLE); - - return IRQ_HANDLED; -} - -/*============================================================================================= - * Interfaces between interrupt handler and rest of scsi code - */ - -/* - * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) - * Purpose : queues a SCSI command - * Params : cmd - SCSI command - * done - function called on completion, with pointer to command descriptor - * Returns : 0, or < 0 on error. - */ -int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) -{ - AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; - - if (!done) { - /* there should be some way of rejecting errors like this without panicing... */ - panic("scsi%d: queuecommand called with NULL done function [cmd=%p]", - host->host->host_no, SCpnt); - return -EINVAL; - } - -#if (DEBUG & DEBUG_NO_WRITE) - if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) { - printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", - host->host->host_no, '0' + SCpnt->device->id); - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 0; - } -#endif - - SCpnt->scsi_done = done; - SCpnt->host_scribble = NULL; - SCpnt->result = 0; - SCpnt->tag = 0; - SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]); - SCpnt->SCp.sent_command = 0; - SCpnt->SCp.scsi_xferred = 0; - - init_SCp(SCpnt); - - host->stats.queues += 1; - - { - unsigned long flags; - - if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) { - SCpnt->result = DID_ERROR << 16; - done(SCpnt); - return 0; - } - local_irq_save(flags); - if (host->scsi.phase == PHASE_IDLE) - acornscsi_kick(host); - local_irq_restore(flags); - } - return 0; -} - -/* - * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) - * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 - * Params : SCpntp1 - pointer to command to return - * SCpntp2 - pointer to command to check - * result - result to pass back to mid-level done function - * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. - */ -static inline -void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) -{ - Scsi_Cmnd *SCpnt = *SCpntp1; - - if (SCpnt) { - *SCpntp1 = NULL; - - SCpnt->result = result; - SCpnt->scsi_done(SCpnt); - } - - if (SCpnt == *SCpntp2) - *SCpntp2 = NULL; -} - -enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; - -/* - * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort a command on this host - * Params : SCpnt - command to abort - * Returns : our abort status - */ -static enum res_abort -acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) -{ - enum res_abort res = res_not_running; - - if (queue_remove_cmd(&host->queues.issue, SCpnt)) { - /* - * The command was on the issue queue, and has not been - * issued yet. We can remove the command from the queue, - * and acknowledge the abort. Neither the devices nor the - * interface know about the command. - */ -//#if (DEBUG & DEBUG_ABORT) - printk("on issue queue "); -//#endif - res = res_success; - } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) { - /* - * The command was on the disconnected queue. Simply - * acknowledge the abort condition, and when the target - * reconnects, we will give it an ABORT message. The - * target should then disconnect, and we will clear - * the busylun bit. - */ -//#if (DEBUG & DEBUG_ABORT) - printk("on disconnected queue "); -//#endif - res = res_success; - } else if (host->SCpnt == SCpnt) { - unsigned long flags; - -//#if (DEBUG & DEBUG_ABORT) - printk("executing "); -//#endif - - local_irq_save(flags); - switch (host->scsi.phase) { - /* - * If the interface is idle, and the command is 'disconnectable', - * then it is the same as on the disconnected queue. We simply - * remove all traces of the command. When the target reconnects, - * we will give it an ABORT message since the command could not - * be found. When the target finally disconnects, we will clear - * the busylun bit. - */ - case PHASE_IDLE: - if (host->scsi.disconnectable) { - host->scsi.disconnectable = 0; - host->SCpnt = NULL; - res = res_success; - } - break; - - /* - * If the command has connected and done nothing further, - * simply force a disconnect. We also need to clear the - * busylun bit. - */ - case PHASE_CONNECTED: - sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT); - host->SCpnt = NULL; - res = res_success_clear; - break; - - default: - acornscsi_abortcmd(host, host->SCpnt->tag); - res = res_snooze; - } - local_irq_restore(flags); - } else if (host->origSCpnt == SCpnt) { - /* - * The command will be executed next, but a command - * is currently using the interface. This is similar to - * being on the issue queue, except the busylun bit has - * been set. - */ - host->origSCpnt = NULL; -//#if (DEBUG & DEBUG_ABORT) - printk("waiting for execution "); -//#endif - res = res_success_clear; - } else - printk("unknown "); - - return res; -} - -/* - * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort a command on this host - * Params : SCpnt - command to abort - * Returns : one of SCSI_ABORT_ macros - */ -int acornscsi_abort(Scsi_Cmnd *SCpnt) -{ - AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata; - int result; - - host->stats.aborts += 1; - -#if (DEBUG & DEBUG_ABORT) - { - int asr, ssr; - asr = sbic_arm_read(host->scsi.io_port, ASR); - ssr = sbic_arm_read(host->scsi.io_port, SSR); - - printk(KERN_WARNING "acornscsi_abort: "); - print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog(host, SCpnt->device->id); - } -#endif - - printk("scsi%d: ", host->host->host_no); - - switch (acornscsi_do_abort(host, SCpnt)) { - /* - * We managed to find the command and cleared it out. - * We do not expect the command to be executing on the - * target, but we have set the busylun bit. - */ - case res_success_clear: -//#if (DEBUG & DEBUG_ABORT) - printk("clear "); -//#endif - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); - - /* - * We found the command, and cleared it out. Either - * the command is still known to be executing on the - * target, or the busylun bit is not set. - */ - case res_success: -//#if (DEBUG & DEBUG_ABORT) - printk("success\n"); -//#endif - SCpnt->result = DID_ABORT << 16; - SCpnt->scsi_done(SCpnt); - result = SCSI_ABORT_SUCCESS; - break; - - /* - * We did find the command, but unfortunately we couldn't - * unhook it from ourselves. Wait some more, and if it - * still doesn't complete, reset the interface. - */ - case res_snooze: -//#if (DEBUG & DEBUG_ABORT) - printk("snooze\n"); -//#endif - result = SCSI_ABORT_SNOOZE; - break; - - /* - * The command could not be found (either because it completed, - * or it got dropped. - */ - default: - case res_not_running: - acornscsi_dumplog(host, SCpnt->device->id); -#if (DEBUG & DEBUG_ABORT) - result = SCSI_ABORT_SNOOZE; -#else - result = SCSI_ABORT_NOT_RUNNING; -#endif -//#if (DEBUG & DEBUG_ABORT) - printk("not running\n"); -//#endif - break; - } - - return result; -} - -/* - * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) - * Purpose : reset a command on this host/reset this host - * Params : SCpnt - command causing reset - * result - what type of reset to perform - * Returns : one of SCSI_RESET_ macros - */ -int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) -{ - AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; - Scsi_Cmnd *SCptr; - - host->stats.resets += 1; - -#if (DEBUG & DEBUG_RESET) - { - int asr, ssr; - - asr = sbic_arm_read(host->scsi.io_port, ASR); - ssr = sbic_arm_read(host->scsi.io_port, SSR); - - printk(KERN_WARNING "acornscsi_reset: "); - print_sbic_status(asr, ssr, host->scsi.phase); - acornscsi_dumplog(host, SCpnt->device->id); - } -#endif - - acornscsi_dma_stop(host); - - SCptr = host->SCpnt; - - /* - * do hard reset. This resets all devices on this host, and so we - * must set the reset status on all commands. - */ - acornscsi_resetcard(host); - - /* - * report reset on commands current connected/disconnected - */ - acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET); - - while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL) - acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET); - - if (SCpnt) { - SCpnt->result = DID_RESET << 16; - SCpnt->scsi_done(SCpnt); - } - - return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS; -} - -/*============================================================================================== - * initialisation & miscellaneous support - */ - -/* - * Function: char *acornscsi_info(struct Scsi_Host *host) - * Purpose : return a string describing this interface - * Params : host - host to give information on - * Returns : a constant string - */ -const -char *acornscsi_info(struct Scsi_Host *host) -{ - static char string[100], *p; - - p = string; - - p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d" -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - " SYNC" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - " TAG" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_LINK - " LINK" -#endif -#if (DEBUG & DEBUG_NO_WRITE) - " NOWRITE ("NO_WRITE_STR")" -#endif - , host->hostt->name, host->io_port, host->irq, - VER_MAJOR, VER_MINOR, VER_PATCH); - return string; -} - -int acornscsi_proc_info(char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - int pos, begin = 0, devidx; - struct Scsi_Host *instance; - Scsi_Device *scd; - AS_Host *host; - char *p = buffer; - - instance = scsi_host_hn_get(host_no); - - if (inout == 1 || !instance) - return -EINVAL; - - host = (AS_Host *)instance->hostdata; - - p += sprintf(p, "AcornSCSI driver v%d.%d.%d" -#ifdef CONFIG_SCSI_ACORNSCSI_SYNC - " SYNC" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE - " TAG" -#endif -#ifdef CONFIG_SCSI_ACORNSCSI_LINK - " LINK" -#endif -#if (DEBUG & DEBUG_NO_WRITE) - " NOWRITE ("NO_WRITE_STR")" -#endif - "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH); - - p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", - host->scsi.io_port, host->scsi.irq); -#ifdef USE_DMAC - p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", - host->dma.io_port, host->scsi.irq); -#endif - - p += sprintf(p, "Statistics:\n" - "Queued commands: %-10u Issued commands: %-10u\n" - "Done commands : %-10u Reads : %-10u\n" - "Writes : %-10u Others : %-10u\n" - "Disconnects : %-10u Aborts : %-10u\n" - "Resets : %-10u\n\nLast phases:", - host->stats.queues, host->stats.removes, - host->stats.fins, host->stats.reads, - host->stats.writes, host->stats.miscs, - host->stats.disconnects, host->stats.aborts, - host->stats.resets); - - for (devidx = 0; devidx < 9; devidx ++) { - unsigned int statptr, prev; - - p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); - statptr = host->status_ptr[devidx] - 10; - - if ((signed int)statptr < 0) - statptr += STATUS_BUFFER_SIZE; - - prev = host->status[devidx][statptr].when; - - for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) { - if (host->status[devidx][statptr].when) { - p += sprintf(p, "%c%02X:%02X+%2ld", - host->status[devidx][statptr].irq ? '-' : ' ', - host->status[devidx][statptr].ph, - host->status[devidx][statptr].ssr, - (host->status[devidx][statptr].when - prev) < 100 ? - (host->status[devidx][statptr].when - prev) : 99); - prev = host->status[devidx][statptr].when; - } - } - } - - p += sprintf(p, "\nAttached devices:\n"); - - list_for_each_entry(scd, &instance->my_devices, siblings) { - p += sprintf(p, "Device/Lun TaggedQ Sync\n"); - p += sprintf(p, " %d/%d ", scd->id, scd->lun); - if (scd->tagged_supported) - p += sprintf(p, "%3sabled(%3d) ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); - else - p += sprintf(p, "unsupported "); - - if (host->device[scd->id].sync_xfer & 15) - p += sprintf(p, "offset %d, %d ns\n", - host->device[scd->id].sync_xfer & 15, - acornscsi_getperiod(host->device[scd->id].sync_xfer)); - else - p += sprintf(p, "async\n"); - - pos = p - buffer; - if (pos + begin < offset) { - begin += pos; - p = buffer; - } - pos = p - buffer; - if (pos + begin > offset + length) - break; - } - - pos = p - buffer; - - *start = buffer + (offset - begin); - pos -= offset - begin; - - if (pos > length) - pos = length; - - return pos; -} - -static Scsi_Host_Template acornscsi_template = { - .module = THIS_MODULE, - .proc_info = acornscsi_proc_info, - .name = "AcornSCSI", - .info = acornscsi_info, - .queuecommand = acornscsi_queuecmd, -#warning fixme - .abort = acornscsi_abort, - .reset = acornscsi_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .unchecked_isa_dma = 0, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "acornscsi", -}; - -static int __devinit -acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - AS_Host *ashost; - int ret = -ENOMEM; - - host = scsi_register(&acornscsi_template, sizeof(AS_Host)); - if (!host) - goto out; - - ashost = (AS_Host *)host->hostdata; - - host->io_port = ecard_address(ec, ECARD_MEMC, 0); - host->irq = ec->irq; - - ashost->host = host; - ashost->scsi.io_port = ioaddr(host->io_port + 0x800); - ashost->scsi.irq = host->irq; - ashost->card.io_intr = POD_SPACE(host->io_port) + 0x800; - ashost->card.io_page = POD_SPACE(host->io_port) + 0xc00; - ashost->card.io_ram = ioaddr(host->io_port); - ashost->dma.io_port = host->io_port + 0xc00; - ashost->dma.io_intr_clear = POD_SPACE(host->io_port) + 0x800; - - ec->irqaddr = (char *)ioaddr(ashost->card.io_intr); - ec->irqmask = 0x0a; - - ret = -EBUSY; - if (!request_region(host->io_port + 0x800, 2, "acornscsi(sbic)")) - goto err_1; - if (!request_region(ashost->card.io_intr, 1, "acornscsi(intr)")) - goto err_2; - if (!request_region(ashost->card.io_page, 1, "acornscsi(page)")) - goto err_3; -#ifdef USE_DMAC - if (!request_region(ashost->dma.io_port, 256, "acornscsi(dmac)")) - goto err_4; -#endif - if (!request_region(host->io_port, 2048, "acornscsi(ram)")) - goto err_5; - - ret = request_irq(host->irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", ashost); - if (ret) { - printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n", - host->host_no, ashost->scsi.irq, ret); - goto err_6; - } - - memset(&ashost->stats, 0, sizeof (ashost->stats)); - queue_initialise(&ashost->queues.issue); - queue_initialise(&ashost->queues.disconnected); - msgqueue_initialise(&ashost->scsi.msgs); - - acornscsi_resetcard(ashost); - - ret = scsi_add_host(host, &ec->dev); - if (ret == 0) - goto out; - - free_irq(host->irq, ashost); - err_6: - release_region(host->io_port, 2048); - err_5: -#ifdef USE_DMAC - release_region(ashost->dma.io_port, 256); -#endif - err_4: - release_region(ashost->card.io_page, 1); - err_3: - release_region(ashost->card.io_intr, 1); - err_2: - release_region(host->io_port + 0x800, 2); - err_1: - scsi_unregister(host); - out: - return ret; -} - -static void __devexit acornscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - AS_Host *ashost = (AS_Host *)host->hostdata; - - ecard_set_drvdata(ec, NULL); - scsi_remove_host(host); - - /* - * Put card into RESET state - */ - outb(0x80, ashost->card.io_page); - - free_irq(host->irq, ashost); - - release_region(host->io_port + 0x800, 2); - release_region(ashost->card.io_intr, 1); - release_region(ashost->card.io_page, 1); - release_region(ashost->dma.io_port, 256); - release_region(host->io_port, 2048); - - msgqueue_free(&ashost->scsi.msgs); - queue_free(&ashost->queues.disconnected); - queue_free(&ashost->queues.issue); -} - -static const struct ecard_id acornscsi_cids[] = { - { MANU_ACORN, PROD_ACORN_SCSI }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver acornscsi_driver = { - .probe = acornscsi_probe, - .remove = __devexit_p(acornscsi_remove), - .id_table = acornscsi_cids, - .drv = { - .name = "acornscsi", - }, -}; - -static int __init acornscsi_init(void) -{ - return ecard_register_driver(&acornscsi_driver); -} - -static void __exit acornscsi_exit(void) -{ - ecard_remove_driver(&acornscsi_driver); -} - -module_init(acornscsi_init); -module_exit(acornscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("AcornSCSI driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/scsi/acornscsi.h b/drivers/acorn/scsi/acornscsi.h deleted file mode 100644 index 8e4d88d0aba3..000000000000 --- a/drivers/acorn/scsi/acornscsi.h +++ /dev/null @@ -1,358 +0,0 @@ -/* - * linux/drivers/acorn/scsi/acornscsi.h - * - * Copyright (C) 1997 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Acorn SCSI driver - */ -#ifndef ACORNSCSI_H -#define ACORNSCSI_H - -/* SBIC registers */ -#define OWNID 0 -#define OWNID_FS1 (1<<7) -#define OWNID_FS2 (1<<6) -#define OWNID_EHP (1<<4) -#define OWNID_EAF (1<<3) - -#define CTRL 1 -#define CTRL_DMAMODE (1<<7) -#define CTRL_DMADBAMODE (1<<6) -#define CTRL_DMABURST (1<<5) -#define CTRL_DMAPOLLED 0 -#define CTRL_HHP (1<<4) -#define CTRL_EDI (1<<3) -#define CTRL_IDI (1<<2) -#define CTRL_HA (1<<1) -#define CTRL_HSP (1<<0) - -#define TIMEOUT 2 -#define TOTSECTS 3 -#define TOTHEADS 4 -#define TOTCYLH 5 -#define TOTCYLL 6 -#define LOGADDRH 7 -#define LOGADDRM2 8 -#define LOGADDRM1 9 -#define LOGADDRL 10 -#define SECTORNUM 11 -#define HEADNUM 12 -#define CYLH 13 -#define CYLL 14 -#define TARGETLUN 15 -#define TARGETLUN_TLV (1<<7) -#define TARGETLUN_DOK (1<<6) - -#define CMNDPHASE 16 -#define SYNCHTRANSFER 17 -#define SYNCHTRANSFER_OF0 0x00 -#define SYNCHTRANSFER_OF1 0x01 -#define SYNCHTRANSFER_OF2 0x02 -#define SYNCHTRANSFER_OF3 0x03 -#define SYNCHTRANSFER_OF4 0x04 -#define SYNCHTRANSFER_OF5 0x05 -#define SYNCHTRANSFER_OF6 0x06 -#define SYNCHTRANSFER_OF7 0x07 -#define SYNCHTRANSFER_OF8 0x08 -#define SYNCHTRANSFER_OF9 0x09 -#define SYNCHTRANSFER_OF10 0x0A -#define SYNCHTRANSFER_OF11 0x0B -#define SYNCHTRANSFER_OF12 0x0C -#define SYNCHTRANSFER_8DBA 0x00 -#define SYNCHTRANSFER_2DBA 0x20 -#define SYNCHTRANSFER_3DBA 0x30 -#define SYNCHTRANSFER_4DBA 0x40 -#define SYNCHTRANSFER_5DBA 0x50 -#define SYNCHTRANSFER_6DBA 0x60 -#define SYNCHTRANSFER_7DBA 0x70 - -#define TRANSCNTH 18 -#define TRANSCNTM 19 -#define TRANSCNTL 20 -#define DESTID 21 -#define DESTID_SCC (1<<7) -#define DESTID_DPD (1<<6) - -#define SOURCEID 22 -#define SOURCEID_ER (1<<7) -#define SOURCEID_ES (1<<6) -#define SOURCEID_DSP (1<<5) -#define SOURCEID_SIV (1<<4) - -#define SSR 23 -#define CMND 24 -#define CMND_RESET 0x00 -#define CMND_ABORT 0x01 -#define CMND_ASSERTATN 0x02 -#define CMND_NEGATEACK 0x03 -#define CMND_DISCONNECT 0x04 -#define CMND_RESELECT 0x05 -#define CMND_SELWITHATN 0x06 -#define CMND_SELECT 0x07 -#define CMND_SELECTATNTRANSFER 0x08 -#define CMND_SELECTTRANSFER 0x09 -#define CMND_RESELECTRXDATA 0x0A -#define CMND_RESELECTTXDATA 0x0B -#define CMND_WAITFORSELRECV 0x0C -#define CMND_SENDSTATCMD 0x0D -#define CMND_SENDDISCONNECT 0x0E -#define CMND_SETIDI 0x0F -#define CMND_RECEIVECMD 0x10 -#define CMND_RECEIVEDTA 0x11 -#define CMND_RECEIVEMSG 0x12 -#define CMND_RECEIVEUSP 0x13 -#define CMND_SENDCMD 0x14 -#define CMND_SENDDATA 0x15 -#define CMND_SENDMSG 0x16 -#define CMND_SENDUSP 0x17 -#define CMND_TRANSLATEADDR 0x18 -#define CMND_XFERINFO 0x20 -#define CMND_SBT (1<<7) - -#define DATA 25 -#define ASR 26 -#define ASR_INT (1<<7) -#define ASR_LCI (1<<6) -#define ASR_BSY (1<<5) -#define ASR_CIP (1<<4) -#define ASR_PE (1<<1) -#define ASR_DBR (1<<0) - -/* DMAC registers */ -#define INIT 0x00 -#define INIT_8BIT (1) - -#define CHANNEL 0x80 -#define CHANNEL_0 0x00 -#define CHANNEL_1 0x01 -#define CHANNEL_2 0x02 -#define CHANNEL_3 0x03 - -#define TXCNTLO 0x01 -#define TXCNTHI 0x81 -#define TXADRLO 0x02 -#define TXADRMD 0x82 -#define TXADRHI 0x03 - -#define DEVCON0 0x04 -#define DEVCON0_AKL (1<<7) -#define DEVCON0_RQL (1<<6) -#define DEVCON0_EXW (1<<5) -#define DEVCON0_ROT (1<<4) -#define DEVCON0_CMP (1<<3) -#define DEVCON0_DDMA (1<<2) -#define DEVCON0_AHLD (1<<1) -#define DEVCON0_MTM (1<<0) - -#define DEVCON1 0x84 -#define DEVCON1_WEV (1<<1) -#define DEVCON1_BHLD (1<<0) - -#define MODECON 0x05 -#define MODECON_WOED 0x01 -#define MODECON_VERIFY 0x00 -#define MODECON_READ 0x04 -#define MODECON_WRITE 0x08 -#define MODECON_AUTOINIT 0x10 -#define MODECON_ADDRDIR 0x20 -#define MODECON_DEMAND 0x00 -#define MODECON_SINGLE 0x40 -#define MODECON_BLOCK 0x80 -#define MODECON_CASCADE 0xC0 - -#define STATUS 0x85 -#define STATUS_TC0 (1<<0) -#define STATUS_RQ0 (1<<4) - -#define TEMPLO 0x06 -#define TEMPHI 0x86 -#define REQREG 0x07 -#define MASKREG 0x87 -#define MASKREG_M0 0x01 -#define MASKREG_M1 0x02 -#define MASKREG_M2 0x04 -#define MASKREG_M3 0x08 - -/* miscellaneous internal variables */ - -#define POD_SPACE(x) ((x) + 0xd0000) -#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0) -#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1) - -/* - * SCSI driver phases - */ -typedef enum { - PHASE_IDLE, /* we're not planning on doing anything */ - PHASE_CONNECTING, /* connecting to a target */ - PHASE_CONNECTED, /* connected to a target */ - PHASE_MSGOUT, /* message out to device */ - PHASE_RECONNECTED, /* reconnected */ - PHASE_COMMANDPAUSED, /* command partly sent */ - PHASE_COMMAND, /* command all sent */ - PHASE_DATAOUT, /* data out to device */ - PHASE_DATAIN, /* data in from device */ - PHASE_STATUSIN, /* status in from device */ - PHASE_MSGIN, /* message in from device */ - PHASE_DONE, /* finished */ - PHASE_ABORTED, /* aborted */ - PHASE_DISCONNECT, /* disconnecting */ -} phase_t; - -/* - * After interrupt, what to do now - */ -typedef enum { - INTR_IDLE, /* not expecting another IRQ */ - INTR_NEXT_COMMAND, /* start next command */ - INTR_PROCESSING, /* interrupt routine still processing */ -} intr_ret_t; - -/* - * DMA direction - */ -typedef enum { - DMA_OUT, /* DMA from memory to chip */ - DMA_IN /* DMA from chip to memory */ -} dmadir_t; - -/* - * Synchronous transfer state - */ -typedef enum { /* Synchronous transfer state */ - SYNC_ASYNCHRONOUS, /* don't negociate synchronous transfers*/ - SYNC_NEGOCIATE, /* start negociation */ - SYNC_SENT_REQUEST, /* sent SDTR message */ - SYNC_COMPLETED, /* received SDTR reply */ -} syncxfer_t; - -/* - * Command type - */ -typedef enum { /* command type */ - CMD_READ, /* READ_6, READ_10, READ_12 */ - CMD_WRITE, /* WRITE_6, WRITE_10, WRITE_12 */ - CMD_MISC, /* Others */ -} cmdtype_t; - -/* - * Data phase direction - */ -typedef enum { /* Data direction */ - DATADIR_IN, /* Data in phase expected */ - DATADIR_OUT /* Data out phase expected */ -} datadir_t; - -#include "queue.h" -#include "msgqueue.h" - -#define STATUS_BUFFER_SIZE 32 -/* - * This is used to dump the previous states of the SBIC - */ -struct status_entry { - unsigned long when; - unsigned char ssr; - unsigned char ph; - unsigned char irq; - unsigned char unused; -}; - -#define ADD_STATUS(_q,_ssr,_ph,_irq) \ -({ \ - host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \ - host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \ - host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \ - host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \ - host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \ -}) - -/* - * AcornSCSI host specific data - */ -typedef struct acornscsi_hostdata { - /* miscellaneous */ - struct Scsi_Host *host; /* host */ - Scsi_Cmnd *SCpnt; /* currently processing command */ - Scsi_Cmnd *origSCpnt; /* original connecting command */ - - /* driver information */ - struct { - unsigned int io_port; /* base address of WD33C93 */ - unsigned int irq; /* interrupt */ - phase_t phase; /* current phase */ - - struct { - unsigned char target; /* reconnected target */ - unsigned char lun; /* reconnected lun */ - unsigned char tag; /* reconnected tag */ - } reconnected; - - Scsi_Pointer SCp; /* current commands data pointer */ - - MsgQueue_t msgs; - - unsigned short last_message; /* last message to be sent */ - unsigned char disconnectable:1; /* this command can be disconnected */ - } scsi; - - /* statistics information */ - struct { - unsigned int queues; - unsigned int removes; - unsigned int fins; - unsigned int reads; - unsigned int writes; - unsigned int miscs; - unsigned int disconnects; - unsigned int aborts; - unsigned int resets; - } stats; - - /* queue handling */ - struct { - Queue_t issue; /* issue queue */ - Queue_t disconnected; /* disconnected command queue */ - } queues; - - /* per-device info */ - struct { - unsigned char sync_xfer; /* synchronous transfer (SBIC value) */ - syncxfer_t sync_state; /* sync xfer negociation state */ - unsigned char disconnect_ok:1; /* device can disconnect */ - } device[8]; - unsigned long busyluns[64 / sizeof(unsigned long)];/* array of bits indicating LUNs busy */ - - /* DMA info */ - struct { - unsigned int io_port; /* base address of DMA controller */ - unsigned int io_intr_clear; /* address of DMA interrupt clear */ - unsigned int free_addr; /* next free address */ - unsigned int start_addr; /* start address of current transfer */ - dmadir_t direction; /* dma direction */ - unsigned int transferred; /* number of bytes transferred */ - unsigned int xfer_start; /* scheduled DMA transfer start */ - unsigned int xfer_length; /* scheduled DMA transfer length */ - char *xfer_ptr; /* pointer to area */ - unsigned char xfer_required:1; /* set if we need to transfer something */ - unsigned char xfer_setup:1; /* set if DMA is setup */ - unsigned char xfer_done:1; /* set if DMA reached end of BH list */ - } dma; - - /* card info */ - struct { - unsigned int io_intr; /* base address of interrupt id reg */ - unsigned int io_page; /* base address of page reg */ - unsigned int io_ram; /* base address of RAM access */ - unsigned char page_reg; /* current setting of page reg */ - } card; - - unsigned char status_ptr[9]; - struct status_entry status[9][STATUS_BUFFER_SIZE]; -} AS_Host; - -#endif /* ACORNSCSI_H */ diff --git a/drivers/acorn/scsi/arxescsi.c b/drivers/acorn/scsi/arxescsi.c deleted file mode 100644 index f46a4c822475..000000000000 --- a/drivers/acorn/scsi/arxescsi.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * linux/arch/arm/drivers/scsi/arxescsi.c - * - * Copyright (C) 1997-2000 Russell King, Stefan Hanske - * - * This driver is based on experimentation. Hence, it may have made - * assumptions about the particular card that I have available, and - * may not be reliable! - * - * Changelog: - * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c - * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 - * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. - * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card - * enabled writing - * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing - * (arxescsi_pseudo_dma_write) - * 02-04-2000 RMK 0.1.1 Updated for new error handling code. - * 22-10-2000 SH Updated for new registering scheme. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" - -struct arxescsi_info { - FAS216_Info info; - struct expansion_card *ec; -}; - -#define DMADATA_OFFSET (0x200) - -#define DMASTAT_OFFSET (0x600) -#define DMASTAT_DRQ (1 << 0) - -#define CSTATUS_IRQ (1 << 0) - -#define VERSION "1.10 (23/01/2003 2.5.57)" - -/* - * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : 0 if we should not set CMD_WITHDMA for transfer info command - */ -static fasdmatype_t -arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - /* - * We don't do real DMA - */ - return fasdma_pseudo; -} - -static void arxescsi_pseudo_dma_write(unsigned char *addr, unsigned char *base) -{ - __asm__ __volatile__( - " stmdb sp!, {r0-r12}\n" - " mov r3, %0\n" - " mov r1, %1\n" - " add r2, r1, #512\n" - " mov r4, #256\n" - ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n" - " mov r5, r6, lsl #16\n" - " mov r7, r8, lsl #16\n" - ".loop_2: ldrb r0, [r1, #1536]\n" - " tst r0, #1\n" - " beq .loop_2\n" - " stmia r2, {r5-r8}\n\t" - " mov r9, r10, lsl #16\n" - " mov r11, r12, lsl #16\n" - ".loop_3: ldrb r0, [r1, #1536]\n" - " tst r0, #1\n" - " beq .loop_3\n" - " stmia r2, {r9-r12}\n" - " subs r4, r4, #16\n" - " bne .loop_1\n" - " ldmia sp!, {r0-r12}\n" - : - : "r" (addr), "r" (base)); -} - -/* - * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) - * Purpose : handles pseudo DMA - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * transfer - minimum number of bytes we expect to transfer - */ -static void -arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, int transfer) -{ - struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; - unsigned int length, error = 0; - unsigned char *base = info->info.scsi.io_base; - unsigned char *addr; - - length = SCp->this_residual; - addr = SCp->ptr; - - if (direction == DMA_OUT) { - unsigned int word; - while (length > 256) { - if (readb(base + 0x80) & STAT_INT) { - error = 1; - break; - } - arxescsi_pseudo_dma_write(addr, base); - addr += 256; - length -= 256; - } - - if (!error) - while (length > 0) { - if (readb(base + 0x80) & STAT_INT) - break; - - if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) - continue; - - word = *addr | *(addr + 1) << 8; - - writew(word, base + DMADATA_OFFSET); - if (length > 1) { - addr += 2; - length -= 2; - } else { - addr += 1; - length -= 1; - } - } - } - else { - if (transfer && (transfer & 255)) { - while (length >= 256) { - if (readb(base + 0x80) & STAT_INT) { - error = 1; - break; - } - - if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) - continue; - - readsw(base + DMADATA_OFFSET, addr, 256 >> 1); - addr += 256; - length -= 256; - } - } - - if (!(error)) - while (length > 0) { - unsigned long word; - - if (readb(base + 0x80) & STAT_INT) - break; - - if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) - continue; - - word = readw(base + DMADATA_OFFSET); - *addr++ = word; - if (--length > 0) { - *addr++ = word >> 8; - length --; - } - } - } -} - -/* - * Function: int arxescsi_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - /* - * no DMA to stop - */ -} - -/* - * Function: const char *arxescsi_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -static const char *arxescsi_info(struct Scsi_Host *host) -{ - struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION); - - return string; -} - -/* - * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -static int -arxescsi_proc_info(char *buffer, char **start, off_t offset, int length, - int host_no, int inout) -{ - struct Scsi_Host *host; - struct arxescsi_info *info; - char *p = buffer; - int pos; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - info = (struct arxescsi_info *)host->hostdata; - if (inout == 1) - return -EINVAL; - - p += sprintf(p, "ARXE 16-bit SCSI driver v%s\n", VERSION); - p += fas216_print_host(&info->info, p); - p += fas216_print_stats(&info->info, p); - p += fas216_print_devices(&info->info, p); - - *start = buffer + offset; - pos = p - buffer - offset; - if (pos > length) - pos = length; - - return pos; -} - -static Scsi_Host_Template arxescsi_template = { - .proc_info = arxescsi_proc_info, - .name = "ARXE SCSI card", - .info = arxescsi_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - .can_queue = 0, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "arxescsi", -}; - -static int __devinit -arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct arxescsi_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - if (!request_mem_region(resbase, reslen, "arxescsi")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&arxescsi_template, sizeof(struct arxescsi_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = NO_IRQ; - host->dma_channel = NO_DMA; - - info = (struct arxescsi_info *)host->hostdata; - info->ec = ec; - - info->info.scsi.io_base = base + 0x2000; - info->info.scsi.irq = host->irq; - info->info.scsi.io_shift = 5; - info->info.ifcfg.clockrate = 24; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 0; - info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 0; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; - info->info.dma.setup = arxescsi_dma_setup; - info->info.dma.pseudo = arxescsi_dma_pseudo; - info->info.dma.stop = arxescsi_dma_stop; - - ec->irqaddr = base; - ec->irqmask = CSTATUS_IRQ; - - ret = fas216_init(host); - if (ret) - goto out_unregister; - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - fas216_release(host); - out_unregister: - scsi_unregister(host); - out_unmap: - iounmap(base); - out_region: - release_mem_region(resbase, reslen); - out: - return ret; -} - -static void __devexit arxescsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id arxescsi_cids[] = { - { MANU_ARXE, PROD_ARXE_SCSI }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver arxescsi_driver = { - .probe = arxescsi_probe, - .remove = __devexit_p(arxescsi_remove), - .id_table = arxescsi_cids, - .drv = { - .name = "arxescsi", - }, -}; - -static int __init init_arxe_scsi_driver(void) -{ - return ecard_register_driver(&arxescsi_driver); -} - -static void __exit exit_arxe_scsi_driver(void) -{ - ecard_remove_driver(&arxescsi_driver); -} - -module_init(init_arxe_scsi_driver); -module_exit(exit_arxe_scsi_driver); - -MODULE_AUTHOR("Stefan Hanske"); -MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/acorn/scsi/cumana_1.c b/drivers/acorn/scsi/cumana_1.c deleted file mode 100644 index 222c8fc88b56..000000000000 --- a/drivers/acorn/scsi/cumana_1.c +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Generic Generic NCR5380 driver - * - * Copyright 1995-2002, Russell King - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" - -#include - -#define AUTOSENSE -#define PSEUDO_DMA - -#define CUMANASCSI_PUBLIC_RELEASE 1 - -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance -#define NCR5380_read(reg) cumanascsi_read(_instance, reg) -#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value) -#define NCR5380_intr cumanascsi_intr -#define NCR5380_queue_command cumanascsi_queue_command -#define NCR5380_proc_info cumanascsi_proc_info - -int NCR5380_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -#define BOARD_NORMAL 0 -#define BOARD_NCR53C400 1 - -#include "../../scsi/NCR5380.h" - -void cumanascsi_setup(char *str, int *ints) -{ -} - -const char *cumanascsi_info(struct Scsi_Host *spnt) -{ - return ""; -} - -#ifdef NOT_EFFICIENT -#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) -#define STAT(p) inb((p)+1) -#define IN(p) inb((p)) -#define OUT(v,p) outb((v), (p)) -#else -#define CTRL(p,v) (p[-2308] = (*ctrl = (v))) -#define STAT(p) (p[4]) -#define IN(p) (*(p)) -#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p))) -#define OUT(v,p) (*(p) = (v)) -#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v)) -#endif -#define L(v) (((v)<<16)|((v) & 0x0000ffff)) -#define H(v) (((v)>>16)|((v) & 0xffff0000)) - -static inline int -NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len) -{ - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - int oldctrl = *ctrl; - unsigned long *laddr; -#ifdef NOT_EFFICIENT - int iobase = instance->io_port; - int dma_io = iobase & ~(0x3C0000>>2); -#else - volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); - volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); -#endif - - if(!len) return 0; - - CTRL(iobase, 0x02); - laddr = (unsigned long *)addr; - while(len >= 32) - { - int status; - unsigned long v; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(!(status & 0x40)) - continue; - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); - len -= 32; - if(len == 0) - break; - } - - addr = (unsigned char *)laddr; - CTRL(iobase, 0x12); - while(len > 0) - { - int status; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - OUT(*addr++, dma_io); - if(--len == 0) - break; - } - - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - OUT(*addr++, dma_io); - if(--len == 0) - break; - } - } -end: - CTRL(iobase, oldctrl|0x40); - return len; -} - -static inline int -NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len) -{ - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - int oldctrl = *ctrl; - unsigned long *laddr; -#ifdef NOT_EFFICIENT - int iobase = instance->io_port; - int dma_io = iobase & ~(0x3C0000>>2); -#else - volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); - volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); -#endif - - if(!len) return 0; - - CTRL(iobase, 0x00); - laddr = (unsigned long *)addr; - while(len >= 32) - { - int status; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(!(status & 0x40)) - continue; - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); - len -= 32; - if(len == 0) - break; - } - - addr = (unsigned char *)laddr; - CTRL(iobase, 0x10); - while(len > 0) - { - int status; - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - *addr++ = IN(dma_io); - if(--len == 0) - break; - } - - status = STAT(iobase); - if(status & 0x80) - goto end; - if(status & 0x40) - { - *addr++ = IN(dma_io); - if(--len == 0) - break; - } - } -end: - CTRL(iobase, oldctrl|0x40); - return len; -} - -#undef STAT -#undef CTRL -#undef IN -#undef OUT - -#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) - -static char cumanascsi_read(struct Scsi_Host *instance, int reg) -{ - unsigned int iobase = instance->io_port; - int i; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - - CTRL(iobase, 0); - i = inb(iobase + 64 + reg); - CTRL(iobase, 0x40); - - return i; -} - -static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value) -{ - int iobase = instance->io_port; - int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; - - CTRL(iobase, 0); - outb(value, iobase + 64 + reg); - CTRL(iobase, 0x40); -} - -#undef CTRL - -#include "../../scsi/NCR5380.c" - -static Scsi_Host_Template cumanascsi_template = { - .module = THIS_MODULE, - .name = "Cumana 16-bit SCSI", - .info = cumanascsi_info, - .queuecommand = cumanascsi_queue_command, - .eh_abort_handler = NCR5380_abort, - .eh_device_reset_handler= NCR5380_device_reset, - .eh_bus_reset_handler = NCR5380_bus_reset, - .eh_host_reset_handler = NCR5380_host_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .unchecked_isa_dma = 0, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "CumanaSCSI-1", -}; - -static int __devinit -cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - int ret = -ENOMEM; - - host = scsi_register(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); - if (!host) - goto out; - - host->io_port = ecard_address(ec, ECARD_IOC, ECARD_SLOW) + 0x800; - host->irq = ec->irq; - - NCR5380_init(host, 0); - - host->n_io_port = 255; - if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) { - ret = -EBUSY; - goto out_free; - } - - ((struct NCR5380_hostdata *)host->hostdata)->ctrl = 0; - outb(0x00, host->io_port - 577); - - ret = request_irq(host->irq, cumanascsi_intr, SA_INTERRUPT, - "CumanaSCSI-1", host); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_release; - } - - printk("scsi%d: at port 0x%08lx irq %d", - host->host_no, host->io_port, host->irq); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - - ret = scsi_add_host(host, &ec->dev); - if (ret == 0) - goto out; - - free_irq(host->irq, host); - out_release: - release_region(host->io_port, host->n_io_port); - out_free: - scsi_unregister(host); - out: - return ret; -} - -static void __devexit cumanascsi1_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - - scsi_remove_host(host); - free_irq(host->irq, host); - release_region(host->io_port, host->n_io_port); - scsi_unregister(host); -} - -static const struct ecard_id cumanascsi1_cids[] = { - { MANU_CUMANA, PROD_CUMANA_SCSI_1 }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver cumanascsi1_driver = { - .probe = cumanascsi1_probe, - .remove = __devexit_p(cumanascsi1_remove), - .id_table = cumanascsi1_cids, - .drv = { - .name = "cumanascsi1", - }, -}; - -static int __init cumanascsi_init(void) -{ - return ecard_register_driver(&cumanascsi1_driver); -} - -static void __exit cumanascsi_exit(void) -{ - ecard_remove_driver(&cumanascsi1_driver); -} - -module_init(cumanascsi_init); -module_exit(cumanascsi_exit); - -MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/scsi/cumana_2.c b/drivers/acorn/scsi/cumana_2.c deleted file mode 100644 index e03cc94161ff..000000000000 --- a/drivers/acorn/scsi/cumana_2.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * linux/drivers/acorn/scsi/cumana_2.c - * - * Copyright (C) 1997-2002 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 30-08-1997 RMK 0.0.0 Created, READONLY version. - * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. - * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. - * 02-05-1998 RMK 0.0.2 Updated & added DMA support. - * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. - * 02-04-2000 RMK 0.0.4 Updated for new error handling code. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -#include - -#define CUMANASCSI2_STATUS (0x0000) -#define STATUS_INT (1 << 0) -#define STATUS_DRQ (1 << 1) -#define STATUS_LATCHED (1 << 3) - -#define CUMANASCSI2_ALATCH (0x0014) -#define ALATCH_ENA_INT (3) -#define ALATCH_DIS_INT (2) -#define ALATCH_ENA_TERM (5) -#define ALATCH_DIS_TERM (4) -#define ALATCH_ENA_BIT32 (11) -#define ALATCH_DIS_BIT32 (10) -#define ALATCH_ENA_DMA (13) -#define ALATCH_DIS_DMA (12) -#define ALATCH_DMA_OUT (15) -#define ALATCH_DMA_IN (14) - -#define CUMANASCSI2_PSEUDODMA (0x0200) - -#define CUMANASCSI2_FAS216_OFFSET (0x0300) -#define CUMANASCSI2_FAS216_SHIFT 2 - -/* - * Version - */ -#define VERSION "1.00 (13/11/2002 2.5.47)" - -/* - * Use term=0,1,0,0,0 to turn terminators on/off - */ -static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -#define NR_SG 256 - -struct cumanascsi2_info { - FAS216_Info info; - struct expansion_card *ec; - - void *status; /* card status register */ - void *alatch; /* Control register */ - unsigned int terms; /* Terminator state */ - void *dmaarea; /* Pseudo DMA area */ - struct scatterlist sg[NR_SG]; /* Scatter DMA list */ -}; - -#define CSTATUS_IRQ (1 << 0) -#define CSTATUS_DRQ (1 << 1) - -/* Prototype: void cumanascsi_2_irqenable(ec, irqnr) - * Purpose : Enable interrupts on Cumana SCSI 2 card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr) -{ - writeb(ALATCH_ENA_INT, ec->irq_data); -} - -/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr) - * Purpose : Disable interrupts on Cumana SCSI 2 card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr) -{ - writeb(ALATCH_DIS_INT, ec->irq_data); -} - -static const expansioncard_ops_t cumanascsi_2_ops = { - .irqenable = cumanascsi_2_irqenable, - .irqdisable = cumanascsi_2_irqdisable, -}; - -/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off) - * Purpose : Turn the Cumana SCSI 2 terminators on or off - * Params : host - card to turn on/off - * : on_off - !0 to turn on, 0 to turn off - */ -static void -cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - - if (on_off) { - info->terms = 1; - writeb(ALATCH_ENA_TERM, info->alatch); - } else { - info->terms = 0; - writeb(ALATCH_DIS_TERM, info->alatch); - } -} - -/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs) - * Purpose : handle interrupts from Cumana SCSI 2 card - * Params : irq - interrupt number - * dev_id - user-defined (Scsi_Host structure) - * regs - processor registers at interrupt - */ -static irqreturn_t -cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct cumanascsi2_info *info = dev_id; - - return fas216_intr(&info->info); -} - -/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : type of transfer to be performed - */ -static fasdmatype_t -cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - struct device *dev = scsi_get_device(host); - int dmach = host->dma_channel; - - writeb(ALATCH_DIS_DMA, info->alatch); - - if (dmach != NO_DMA && - (min_type == fasdma_real_all || SCp->this_residual >= 512)) { - int bufs, map_dir, dma_dir, alatch_dir; - - bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); - - if (direction == DMA_OUT) - map_dir = DMA_TO_DEVICE, - dma_dir = DMA_MODE_WRITE, - alatch_dir = ALATCH_DMA_OUT; - else - map_dir = DMA_FROM_DEVICE, - dma_dir = DMA_MODE_READ, - alatch_dir = ALATCH_DMA_IN; - - dma_map_sg(dev, info->sg, bufs + 1, map_dir); - - disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); - writeb(alatch_dir, info->alatch); - set_dma_mode(dmach, dma_dir); - enable_dma(dmach); - writeb(ALATCH_ENA_DMA, info->alatch); - writeb(ALATCH_DIS_BIT32, info->alatch); - return fasdma_real_all; - } - - /* - * If we're not doing DMA, - * we'll do pseudo DMA - */ - return fasdma_pio; -} - -/* - * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer) - * Purpose : handles pseudo DMA - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * transfer - minimum number of bytes we expect to transfer - */ -static void -cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, int transfer) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - unsigned int length; - unsigned char *addr; - - length = SCp->this_residual; - addr = SCp->ptr; - - if (direction == DMA_OUT) -#if 0 - while (length > 1) { - unsigned long word; - unsigned int status = readb(info->status); - - if (status & STATUS_INT) - goto end; - - if (!(status & STATUS_DRQ)) - continue; - - word = *addr | *(addr + 1) << 8; - writew(word, info->dmaarea); - addr += 2; - length -= 2; - } -#else - printk ("PSEUDO_OUT???\n"); -#endif - else { - if (transfer && (transfer & 255)) { - while (length >= 256) { - unsigned int status = readb(info->status); - - if (status & STATUS_INT) - return; - - if (!(status & STATUS_DRQ)) - continue; - - readsw(info->dmaarea, addr, 256 >> 1); - addr += 256; - length -= 256; - } - } - - while (length > 0) { - unsigned long word; - unsigned int status = readb(info->status); - - if (status & STATUS_INT) - return; - - if (!(status & STATUS_DRQ)) - continue; - - word = readw(info->dmaarea); - *addr++ = word; - if (--length > 0) { - *addr++ = word >> 8; - length --; - } - } - } -} - -/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void -cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - if (host->dma_channel != NO_DMA) { - writeb(ALATCH_DIS_DMA, info->alatch); - disable_dma(host->dma_channel); - } -} - -/* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -const char *cumanascsi_2_info(struct Scsi_Host *host) -{ - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s terminators o%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION, info->terms ? "n" : "ff"); - - return string; -} - -/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) - * Purpose : Set a driver specific function - * Params : host - host to setup - * : buffer - buffer containing string describing operation - * : length - length of string - * Returns : -EINVAL, or 0 - */ -static int -cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) -{ - int ret = length; - - if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) { - buffer += 11; - length -= 11; - - if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { - if (buffer[5] == '1') - cumanascsi_2_terminator_ctl(host, 1); - else if (buffer[5] == '0') - cumanascsi_2_terminator_ctl(host, 0); - else - ret = -EINVAL; - } else - ret = -EINVAL; - } else - ret = -EINVAL; - - return ret; -} - -/* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - struct Scsi_Host *host; - struct cumanascsi2_info *info; - char *p = buffer; - int pos; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - if (inout == 1) - return cumanascsi_2_set_proc_info(host, buffer, length); - - info = (struct cumanascsi2_info *)host->hostdata; - - p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION); - p += fas216_print_host(&info->info, p); - p += sprintf(p, "Term : o%s\n", - info->terms ? "n" : "ff"); - - p += fas216_print_stats(&info->info, p); - p += fas216_print_devices(&info->info, p); - - *start = buffer + offset; - pos = p - buffer - offset; - if (pos > length) - pos = length; - - return pos; -} - -static Scsi_Host_Template cumanascsi2_template = { - .module = THIS_MODULE, - .proc_info = cumanascsi_2_proc_info, - .name = "Cumana SCSI II", - .info = cumanascsi_2_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - .can_queue = 1, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "cumanascsi2", -}; - -static int __devinit -cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct cumanascsi2_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - if (!request_mem_region(resbase, reslen, "cumanascsi2")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&cumanascsi2_template, - sizeof(struct cumanascsi2_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = ec->irq; - host->dma_channel = ec->dma; - - ecard_set_drvdata(ec, host); - - info = (struct cumanascsi2_info *)host->hostdata; - info->ec = ec; - info->dmaarea = base + CUMANASCSI2_PSEUDODMA; - info->status = base + CUMANASCSI2_STATUS; - info->alatch = base + CUMANASCSI2_ALATCH; - - ec->irqaddr = info->status; - ec->irqmask = STATUS_INT; - ec->irq_data = base + CUMANASCSI2_ALATCH; - ec->ops = &cumanascsi_2_ops; - - cumanascsi_2_terminator_ctl(host, term[ec->slot_no]); - - info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET; - info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT; - info->info.scsi.irq = host->irq; - info->info.ifcfg.clockrate = 40; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 7; - info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 1; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; - info->info.dma.setup = cumanascsi_2_dma_setup; - info->info.dma.pseudo = cumanascsi_2_dma_pseudo; - info->info.dma.stop = cumanascsi_2_dma_stop; - - ret = fas216_init(host); - if (ret) - goto out_free; - - ret = request_irq(host->irq, cumanascsi_2_intr, - SA_INTERRUPT, "cumanascsi2", info); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_release; - } - - if (host->dma_channel != NO_DMA) { - if (request_dma(host->dma_channel, "cumanascsi2")) { - printk("scsi%d: DMA%d not free, using PIO\n", - host->host_no, host->dma_channel); - host->dma_channel = NO_DMA; - } else { - set_dma_speed(host->dma_channel, 180); - info->info.ifcfg.capabilities |= FASCAP_DMA; - } - } - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, host); - - out_release: - fas216_release(host); - - out_free: - scsi_unregister(host); - - out_unmap: - iounmap(base); - - out_region: - release_mem_region(resbase, reslen); - - out: - return ret; -} - -static void __devexit cumanascsi2_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, info); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_MEMC); - reslen = ecard_resource_len(ec, ECARD_RES_MEMC); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id cumanascsi2_cids[] = { - { MANU_CUMANA, PROD_CUMANA_SCSI_2 }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver cumanascsi2_driver = { - .probe = cumanascsi2_probe, - .remove = __devexit_p(cumanascsi2_remove), - .id_table = cumanascsi2_cids, - .drv = { - .name = "cumanascsi2", - }, -}; - -static int __init cumanascsi2_init(void) -{ - return ecard_register_driver(&cumanascsi2_driver); -} - -static void __exit cumanascsi2_exit(void) -{ - ecard_remove_driver(&cumanascsi2_driver); -} - -module_init(cumanascsi2_init); -module_exit(cumanascsi2_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/scsi/ecoscsi.c b/drivers/acorn/scsi/ecoscsi.c deleted file mode 100644 index e7c6282cb654..000000000000 --- a/drivers/acorn/scsi/ecoscsi.c +++ /dev/null @@ -1,283 +0,0 @@ -#define AUTOSENSE -/* #define PSEUDO_DMA */ - -/* - * EcoSCSI Generic NCR5380 driver - * - * Copyright 1995, Russell King - * - * ALPHA RELEASE 1. - * - * For more information, please consult - * - * NCR 5380 Family - * SCSI Protocol Controller - * Databook - * - * NCR Microelectronics - * 1635 Aeroplaza Drive - * Colorado Springs, CO 80916 - * 1+ (719) 578-3400 - * 1+ (800) 334-5454 - */ - -/* - * Options : - * - * PARITY - enable parity checking. Not supported. - * - * SCSI2 - enable support for SCSI-II tagged queueing. Untested. - * - * USLEEP - enable support for devices that don't disconnect. Untested. - */ - -/* - * $Log: ecoscsi.c,v $ - * Revision 1.2 1998/03/08 05:49:47 davem - * Merge to 2.1.89 - * - * Revision 1.1 1998/02/23 02:45:24 davem - * Merge to 2.1.88 - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" - -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance - -#define NCR5380_read(reg) ecoscsi_read(_instance, reg) -#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value) - -#define NCR5380_intr ecoscsi_intr -#define NCR5380_queue_command ecoscsi_queue_command -#define NCR5380_proc_info ecoscsi_proc_info - -#include "../../scsi/NCR5380.h" - -#define ECOSCSI_PUBLIC_RELEASE 1 - -static char ecoscsi_read(struct Scsi_Host *instance, int reg) -{ - int iobase = instance->io_port; - outb(reg | 8, iobase); - return inb(iobase + 1); -} - -static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value) -{ - int iobase = instance->io_port; - outb(reg | 8, iobase); - outb(value, iobase + 1); -} - -/* - * Function : ecoscsi_setup(char *str, int *ints) - * - * Purpose : LILO command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - */ - -void ecoscsi_setup(char *str, int *ints) { -} - -/* - * Function : int ecoscsi_detect(Scsi_Host_Template * tpnt) - * - * Purpose : initializes ecoscsi NCR5380 driver based on the - * command line / compile time port and irq definitions. - * - * Inputs : tpnt - template for this SCSI adapter. - * - * Returns : 1 if a host adapter was found, 0 if not. - * - */ - -int ecoscsi_detect(Scsi_Host_Template * tpnt) -{ - struct Scsi_Host *host; - - tpnt->proc_name = "ecoscsi"; - - host = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); - if (!host) - return 0; - - host->io_port = 0x80ce8000; - host->n_io_port = 144; - host->irq = IRQ_NONE; - - if ( !(request_region(host->io_port, host->n_io_port, "ecoscsi")) ) - goto unregister_scsi; - - ecoscsi_write (host, MODE_REG, 0x20); /* Is it really SCSI? */ - if (ecoscsi_read (host, MODE_REG) != 0x20) /* Write to a reg. */ - goto release_reg; - - ecoscsi_write( host, MODE_REG, 0x00 ); /* it back. */ - if (ecoscsi_read (host, MODE_REG) != 0x00) - goto release_reg; - - NCR5380_init(host, 0); - - printk("scsi%d: at port 0x%08lx irqs disabled", host->host_no, host->io_port); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, ECOSCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - - return 1; - -release_reg: - release_region(host->io_port, host->n_io_port); -unregister_scsi: - scsi_unregister(host); - return 0; -} - -int ecoscsi_release (struct Scsi_Host *shpnt) -{ - if (shpnt->irq != IRQ_NONE) - free_irq (shpnt->irq, NULL); - if (shpnt->io_port) - release_region (shpnt->io_port, shpnt->n_io_port); - return 0; -} - -const char * ecoscsi_info (struct Scsi_Host *spnt) -{ - return ""; -} - -#if 0 -#define STAT(p) inw(p + 144) - -static inline int NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, - int len) -{ - int iobase = host->io_port; -printk("writing %p len %d\n",addr, len); - if(!len) return -1; - - while(1) - { - int status; - while(((status = STAT(iobase)) & 0x100)==0); - } -} - -static inline int NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, - int len) -{ - int iobase = host->io_port; - int iobase2= host->io_port + 0x100; - unsigned char *start = addr; - int s; -printk("reading %p len %d\n",addr, len); - outb(inb(iobase + 128), iobase + 135); - while(len > 0) - { - int status,b,i, timeout; - timeout = 0x07FFFFFF; - while(((status = STAT(iobase)) & 0x100)==0) - { - timeout--; - if(status & 0x200 || !timeout) - { - printk("status = %p\n",status); - outb(0, iobase + 135); - return 1; - } - } - if(len >= 128) - { - for(i=0; i<64; i++) - { - b = inw(iobase + 136); - *addr++ = b; - *addr++ = b>>8; - } - len -= 128; - } - else - { - b = inw(iobase + 136); - *addr ++ = b; - len -= 1; - if(len) - *addr ++ = b>>8; - len -= 1; - } - } - outb(0, iobase + 135); - printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]); - return 1; -} -#endif -#undef STAT - -int NCR5380_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -#define BOARD_NORMAL 0 -#define BOARD_NCR53C400 1 - -#include "../../scsi/NCR5380.c" - -static Scsi_Host_Template ecoscsi_template = { - .module = THIS_MODULE, - .name = "Serial Port EcoSCSI NCR5380", - .detect = ecoscsi_detect, - .release = ecoscsi_release, - .info = ecoscsi_info, - .queuecommand = ecoscsi_queue_command, - .eh_abort_handler = NCR5380_abort, - .eh_device_reset_handler= NCR5380_device_reset, - .eh_bus_reset_handler = NCR5380_bus_reset, - .eh_host_reset_handler = NCR5380_host_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING -}; - -static int __init ecoscsi_init(void) -{ - scsi_register_host(&ecoscsi_template); - if (ecoscsi_template.present) - return 0; - - scsi_unregister_host(&ecoscsi_template); - return -ENODEV; -} - -static void __exit ecoscsi_exit(void) -{ - scsi_unregister_host(&ecoscsi_template); -} - -module_init(ecoscsi_init); -module_exit(ecoscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Econet-SCSI driver for Acorn machines"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/acorn/scsi/eesox.c b/drivers/acorn/scsi/eesox.c deleted file mode 100644 index 11edbfb58195..000000000000 --- a/drivers/acorn/scsi/eesox.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - * linux/drivers/acorn/scsi/eesox.c - * - * Copyright (C) 1997-2003 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This driver is based on experimentation. Hence, it may have made - * assumptions about the particular card that I have available, and - * may not be reliable! - * - * Changelog: - * 01-10-1997 RMK Created, READONLY version - * 15-02-1998 RMK READ/WRITE version - * added DMA support and hardware definitions - * 14-03-1998 RMK Updated DMA support - * Added terminator control - * 15-04-1998 RMK Only do PIO if FAS216 will allow it. - * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new - * error handling code. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -#include - -#define EESOX_FAS216_OFFSET 0x3000 -#define EESOX_FAS216_SHIFT 5 - -#define EESOX_DMASTAT 0x2800 -#define EESOX_STAT_INTR 0x01 -#define EESOX_STAT_DMA 0x02 - -#define EESOX_CONTROL 0x2800 -#define EESOX_INTR_ENABLE 0x04 -#define EESOX_TERM_ENABLE 0x02 -#define EESOX_RESET 0x01 - -#define EESOX_DMADATA 0x3800 - -#define VERSION "1.10 (17/01/2003 2.5.59)" - -/* - * Use term=0,1,0,0,0 to turn terminators on/off - */ -static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -#define NR_SG 256 - -struct eesoxscsi_info { - FAS216_Info info; - struct expansion_card *ec; - - void *ctl_port; - unsigned int control; - struct scatterlist sg[NR_SG]; /* Scatter DMA list */ -}; - -/* Prototype: void eesoxscsi_irqenable(ec, irqnr) - * Purpose : Enable interrupts on EESOX SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -eesoxscsi_irqenable(struct expansion_card *ec, int irqnr) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; - - info->control |= EESOX_INTR_ENABLE; - - writeb(info->control, info->ctl_port); -} - -/* Prototype: void eesoxscsi_irqdisable(ec, irqnr) - * Purpose : Disable interrupts on EESOX SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; - - info->control &= ~EESOX_INTR_ENABLE; - - writeb(info->control, info->ctl_port); -} - -static const expansioncard_ops_t eesoxscsi_ops = { - .irqenable = eesoxscsi_irqenable, - .irqdisable = eesoxscsi_irqdisable, -}; - -/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off) - * Purpose : Turn the EESOX SCSI terminators on or off - * Params : host - card to turn on/off - * : on_off - !0 to turn on, 0 to turn off - */ -static void -eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - unsigned long flags; - - spin_lock_irqsave(host->host_lock, flags); - if (on_off) - info->control |= EESOX_TERM_ENABLE; - else - info->control &= ~EESOX_TERM_ENABLE; - - writeb(info->control, info->ctl_port); - spin_unlock_irqrestore(host->host_lock, flags); -} - -/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs) - * Purpose : handle interrupts from EESOX SCSI card - * Params : irq - interrupt number - * dev_id - user-defined (Scsi_Host structure) - * regs - processor registers at interrupt - */ -static irqreturn_t -eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct eesoxscsi_info *info = dev_id; - - return fas216_intr(&info->info); -} - -/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : type of transfer to be performed - */ -static fasdmatype_t -eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - struct device *dev = scsi_get_device(host); - int dmach = host->dma_channel; - - if (dmach != NO_DMA && - (min_type == fasdma_real_all || SCp->this_residual >= 512)) { - int bufs, map_dir, dma_dir; - - bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); - - if (direction == DMA_OUT) - map_dir = DMA_TO_DEVICE, - dma_dir = DMA_MODE_WRITE; - else - map_dir = DMA_FROM_DEVICE, - dma_dir = DMA_MODE_READ; - - dma_map_sg(dev, info->sg, bufs + 1, map_dir); - - disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); - set_dma_mode(dmach, dma_dir); - enable_dma(dmach); - return fasdma_real_all; - } - /* - * We don't do DMA, we only do slow PIO - * - * Some day, we will do Pseudo DMA - */ - return fasdma_pseudo; -} - -static void eesoxscsi_buffer_in(void *buf, int length, void *base) -{ - const void *reg_fas = base + EESOX_FAS216_OFFSET; - const void *reg_dmastat = base + EESOX_DMASTAT; - const void *reg_dmadata = base + EESOX_DMADATA; - const register unsigned long mask = 0xffff; - - do { - unsigned int status; - - /* - * Interrupt request? - */ - status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); - if (status & STAT_INT) - break; - - /* - * DMA request active? - */ - status = readb(reg_dmastat); - if (!(status & EESOX_STAT_DMA)) - continue; - - /* - * Get number of bytes in FIFO - */ - status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; - if (status > 16) - status = 16; - if (status > length) - status = length; - - /* - * Align buffer. - */ - if (((u32)buf) & 2 && status >= 2) { - *((u16 *)buf)++ = readl(reg_dmadata); - status -= 2; - length -= 2; - } - - if (status >= 8) { - unsigned long l1, l2; - - l1 = readl(reg_dmadata) & mask; - l1 |= readl(reg_dmadata) << 16; - l2 = readl(reg_dmadata) & mask; - l2 |= readl(reg_dmadata) << 16; - *((u32 *)buf)++ = l1; - *((u32 *)buf)++ = l2; - length -= 8; - continue; - } - - if (status >= 4) { - unsigned long l1; - - l1 = readl(reg_dmadata) & mask; - l1 |= readl(reg_dmadata) << 16; - - *((u32 *)buf)++ = l1; - length -= 4; - continue; - } - - if (status >= 2) { - *((u16 *)buf)++ = readl(reg_dmadata); - length -= 2; - } - } while (length); -} - -static void eesoxscsi_buffer_out(void *buf, int length, void *base) -{ - const void *reg_fas = base + EESOX_FAS216_OFFSET; - const void *reg_dmastat = base + EESOX_DMASTAT; - const void *reg_dmadata = base + EESOX_DMADATA; - - do { - unsigned int status; - - /* - * Interrupt request? - */ - status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); - if (status & STAT_INT) - break; - - /* - * DMA request active? - */ - status = readb(reg_dmastat); - if (!(status & EESOX_STAT_DMA)) - continue; - - /* - * Get number of bytes in FIFO - */ - status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; - if (status > 16) - status = 16; - status = 16 - status; - if (status > length) - status = length; - status &= ~1; - - /* - * Align buffer. - */ - if (((u32)buf) & 2 && status >= 2) { - writel(*((u16 *)buf)++ << 16, reg_dmadata); - status -= 2; - length -= 2; - } - - if (status >= 8) { - unsigned long l1, l2; - - l1 = *((u32 *)buf)++; - l2 = *((u32 *)buf)++; - - writel(l1 << 16, reg_dmadata); - writel(l1, reg_dmadata); - writel(l2 << 16, reg_dmadata); - writel(l2, reg_dmadata); - length -= 8; - continue; - } - - if (status >= 4) { - unsigned long l1; - - l1 = *((u32 *)buf)++; - - writel(l1 << 16, reg_dmadata); - writel(l1, reg_dmadata); - length -= 4; - continue; - } - - if (status >= 2) { - writel(*((u16 *)buf)++ << 16, reg_dmadata); - length -= 2; - } - } while (length); -} - -static void -eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t dir, int transfer_size) -{ - void *base = (void *)host->base; - if (dir == DMA_IN) { - eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, base); - } else { - eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, base); - } -} - -/* Prototype: int eesoxscsi_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void -eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - if (host->dma_channel != NO_DMA) - disable_dma(host->dma_channel); -} - -/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -const char *eesoxscsi_info(struct Scsi_Host *host) -{ - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s terminators o%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff"); - - return string; -} - -/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) - * Purpose : Set a driver specific function - * Params : host - host to setup - * : buffer - buffer containing string describing operation - * : length - length of string - * Returns : -EINVAL, or 0 - */ -static int -eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) -{ - int ret = length; - - if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) { - buffer += 9; - length -= 9; - - if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { - if (buffer[5] == '1') - eesoxscsi_terminator_ctl(host, 1); - else if (buffer[5] == '0') - eesoxscsi_terminator_ctl(host, 0); - else - ret = -EINVAL; - } else - ret = -EINVAL; - } else - ret = -EINVAL; - - return ret; -} - -/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - struct Scsi_Host *host; - struct eesoxscsi_info *info; - char *p = buffer; - int pos; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - if (inout == 1) - return eesoxscsi_set_proc_info(host, buffer, length); - - info = (struct eesoxscsi_info *)host->hostdata; - - p += sprintf(p, "EESOX SCSI driver v%s\n", VERSION); - p += fas216_print_host(&info->info, p); - p += sprintf(p, "Term : o%s\n", - info->control & EESOX_TERM_ENABLE ? "n" : "ff"); - - p += fas216_print_stats(&info->info, p); - p += fas216_print_devices(&info->info, p); - - *start = buffer + offset; - pos = p - buffer - offset; - if (pos > length) - pos = length; - - return pos; -} - -static ssize_t eesoxscsi_show_term(struct device *dev, char *buf) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - - return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0); -} - -static ssize_t eesoxscsi_store_term(struct device *dev, const char *buf, size_t len) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - unsigned long flags; - - if (len > 1) { - spin_lock_irqsave(host->host_lock, flags); - if (buf[0] != '0') { - info->control |= EESOX_TERM_ENABLE; - } else { - info->control &= ~EESOX_TERM_ENABLE; - } - writeb(info->control, info->ctl_port); - spin_unlock_irqrestore(host->host_lock, flags); - } - - return len; -} - -static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, - eesoxscsi_show_term, eesoxscsi_store_term); - -static Scsi_Host_Template eesox_template = { - .module = THIS_MODULE, - .proc_info = eesoxscsi_proc_info, - .name = "EESOX SCSI", - .info = eesoxscsi_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - .can_queue = 1, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "eesox", -}; - -static int __devinit -eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct eesoxscsi_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - if (!request_mem_region(resbase, reslen, "eesoxscsi")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&eesox_template, - sizeof(struct eesoxscsi_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = ec->irq; - host->dma_channel = ec->dma; - - ecard_set_drvdata(ec, host); - - info = (struct eesoxscsi_info *)host->hostdata; - info->ec = ec; - info->ctl_port = base + EESOX_CONTROL; - info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0; - writeb(info->control, info->ctl_port); - - ec->irqaddr = base + EESOX_DMASTAT; - ec->irqmask = EESOX_STAT_INTR; - ec->irq_data = info; - ec->ops = &eesoxscsi_ops; - - info->info.scsi.io_base = base + EESOX_FAS216_OFFSET; - info->info.scsi.io_shift = EESOX_FAS216_SHIFT; - info->info.scsi.irq = host->irq; - info->info.ifcfg.clockrate = 40; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 7; - info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 1; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; - info->info.dma.setup = eesoxscsi_dma_setup; - info->info.dma.pseudo = eesoxscsi_dma_pseudo; - info->info.dma.stop = eesoxscsi_dma_stop; - - device_create_file(&ec->dev, &dev_attr_bus_term); - - ret = fas216_init(host); - if (ret) - goto out_free; - - ret = request_irq(host->irq, eesoxscsi_intr, 0, "eesoxscsi", info); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_remove; - } - - if (host->dma_channel != NO_DMA) { - if (request_dma(host->dma_channel, "eesox")) { - printk("scsi%d: DMA%d not free, DMA disabled\n", - host->host_no, host->dma_channel); - host->dma_channel = NO_DMA; - } else { - set_dma_speed(host->dma_channel, 180); - info->info.ifcfg.capabilities |= FASCAP_DMA; - info->info.ifcfg.cntl3 |= CNTL3_BS8; - } - } - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, host); - - out_remove: - fas216_remove(host); - - out_free: - device_remove_file(&ec->dev, &dev_attr_bus_term); - scsi_unregister(host); - - out_unmap: - iounmap(base); - - out_region: - release_mem_region(resbase, reslen); - - out: - return ret; -} - -static void __devexit eesoxscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, info); - - device_remove_file(&ec->dev, &dev_attr_bus_term); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id eesoxscsi_cids[] = { - { MANU_EESOX, PROD_EESOX_SCSI2 }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver eesoxscsi_driver = { - .probe = eesoxscsi_probe, - .remove = __devexit_p(eesoxscsi_remove), - .id_table = eesoxscsi_cids, - .drv = { - .name = "eesoxscsi", - }, -}; - -static int __init eesox_init(void) -{ - return ecard_register_driver(&eesoxscsi_driver); -} - -static void __exit eesox_exit(void) -{ - ecard_remove_driver(&eesoxscsi_driver); -} - -module_init(eesox_init); -module_exit(eesox_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/acorn/scsi/fas216.c b/drivers/acorn/scsi/fas216.c deleted file mode 100644 index c90d20a99496..000000000000 --- a/drivers/acorn/scsi/fas216.c +++ /dev/null @@ -1,3045 +0,0 @@ -/* - * linux/drivers/acorn/scsi/fas216.c - * - * Copyright (C) 1997-2003 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and - * other sources, including: - * the AMD Am53CF94 data sheet - * the AMD Am53C94 data sheet - * - * This is a generic driver. To use it, have a look at cumana_2.c. You - * should define your own structure that overlays FAS216_Info, eg: - * struct my_host_data { - * FAS216_Info info; - * ... my host specific data ... - * }; - * - * Changelog: - * 30-08-1997 RMK Created - * 14-09-1997 RMK Started disconnect support - * 08-02-1998 RMK Corrected real DMA support - * 15-02-1998 RMK Started sync xfer support - * 06-04-1998 RMK Tightened conditions for printing incomplete - * transfers - * 02-05-1998 RMK Added extra checks in fas216_reset - * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns - * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h - * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT - * 02-04-2000 RMK Converted to use the new error handling, and - * automatically request sense data upon check - * condition status from targets. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -/* NOTE: SCSI2 Synchronous transfers *require* DMA according to - * the data sheet. This restriction is crazy, especially when - * you only want to send 16 bytes! What were the guys who - * designed this chip on at that time? Did they read the SCSI2 - * spec at all? The following sections are taken from the SCSI2 - * standard (s2r10) concerning this: - * - * > IMPLEMENTORS NOTES: - * > (1) Re-negotiation at every selection is not recommended, since a - * > significant performance impact is likely. - * - * > The implied synchronous agreement shall remain in effect until a BUS DEVICE - * > RESET message is received, until a hard reset condition occurs, or until one - * > of the two SCSI devices elects to modify the agreement. The default data - * > transfer mode is asynchronous data transfer mode. The default data transfer - * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard - * > reset condition. - * - * In total, this means that once you have elected to use synchronous - * transfers, you must always use DMA. - * - * I was thinking that this was a good chip until I found this restriction ;( - */ -#define SCSI2_SYNC -#undef SCSI2_TAG - -#undef DEBUG_CONNECT -#undef DEBUG_MESSAGES - -#undef CHECK_STRUCTURE - -#define LOG_CONNECT (1 << 0) -#define LOG_BUSSERVICE (1 << 1) -#define LOG_FUNCTIONDONE (1 << 2) -#define LOG_MESSAGES (1 << 3) -#define LOG_BUFFER (1 << 4) -#define LOG_ERROR (1 << 8) - -static int level_mask = LOG_ERROR; - -MODULE_PARM(level_mask, "i"); - -static int __init fas216_log_setup(char *str) -{ - char *s; - - level_mask = 0; - - while ((s = strsep(&str, ",")) != NULL) { - switch (s[0]) { - case 'a': - if (strcmp(s, "all") == 0) - level_mask |= -1; - break; - case 'b': - if (strncmp(s, "bus", 3) == 0) - level_mask |= LOG_BUSSERVICE; - if (strncmp(s, "buf", 3) == 0) - level_mask |= LOG_BUFFER; - break; - case 'c': - level_mask |= LOG_CONNECT; - break; - case 'e': - level_mask |= LOG_ERROR; - break; - case 'm': - level_mask |= LOG_MESSAGES; - break; - case 'n': - if (strcmp(s, "none") == 0) - level_mask = 0; - break; - case 's': - level_mask |= LOG_FUNCTIONDONE; - break; - } - } - return 1; -} - -__setup("fas216_logging=", fas216_log_setup); - -static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg) -{ - unsigned int off = reg << info->scsi.io_shift; - if (info->scsi.io_base) - return readb(info->scsi.io_base + off); - else - return inb(info->scsi.io_port + off); -} - -static inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val) -{ - unsigned int off = reg << info->scsi.io_shift; - if (info->scsi.io_base) - writeb(val, info->scsi.io_base + off); - else - outb(val, info->scsi.io_port + off); -} - -static void fas216_dumpstate(FAS216_Info *info) -{ - unsigned char is, stat, inst; - - is = fas216_readb(info, REG_IS); - stat = fas216_readb(info, REG_STAT); - inst = fas216_readb(info, REG_INST); - - printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X" - " INST=%02X IS=%02X CFIS=%02X", - fas216_readb(info, REG_CTCL), - fas216_readb(info, REG_CTCM), - fas216_readb(info, REG_CMD), stat, inst, is, - fas216_readb(info, REG_CFIS)); - printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n", - fas216_readb(info, REG_CNTL1), - fas216_readb(info, REG_CNTL2), - fas216_readb(info, REG_CNTL3), - fas216_readb(info, REG_CTCH)); -} - -static void print_SCp(Scsi_Pointer *SCp, const char *prefix, const char *suffix) -{ - printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s", - prefix, SCp->ptr, SCp->this_residual, SCp->buffer, - SCp->buffers_residual, suffix); -} - -static void fas216_dumpinfo(FAS216_Info *info) -{ - static int used = 0; - int i; - - if (used++) - return; - - printk("FAS216_Info=\n"); - printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n", - info->magic_start, info->host, info->SCpnt, - info->origSCpnt); - printk(" scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n", - info->scsi.io_port, info->scsi.io_shift, info->scsi.irq, - info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2], - info->scsi.cfg[3]); - printk(" type=%p phase=%X\n", - info->scsi.type, info->scsi.phase); - print_SCp(&info->scsi.SCp, " SCp={ ", " }\n"); - printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", - info->scsi.async_stp, - info->scsi.disconnectable, info->scsi.aborting); - printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" - " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n", - info->stats.queues, info->stats.removes, info->stats.fins, - info->stats.reads, info->stats.writes, info->stats.miscs, - info->stats.disconnects, info->stats.aborts, info->stats.bus_resets, - info->stats.host_resets); - printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n", - info->ifcfg.clockrate, info->ifcfg.select_timeout, - info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); - for (i = 0; i < 8; i++) { - printk(" busyluns[%d]=%08lx dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", - i, info->busyluns[i], i, - info->device[i].disconnect_ok, info->device[i].stp, - info->device[i].sof, info->device[i].sync_state); - } - printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", - info->dma.transfer_type, info->dma.setup, - info->dma.pseudo, info->dma.stop); - printk(" internal_done=%X magic_end=%lX }\n", - info->internal_done, info->magic_end); -} - -#ifdef CHECK_STRUCTURE -static void __fas216_checkmagic(FAS216_Info *info, const char *func) -{ - int corruption = 0; - if (info->magic_start != MAGIC) { - printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n"); - corruption++; - } - if (info->magic_end != MAGIC) { - printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n"); - corruption++; - } - if (corruption) { - fas216_dumpinfo(info); - panic("scsi memory space corrupted in %s", func); - } -} -#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__) -#else -#define fas216_checkmagic(info) -#endif - -static const char *fas216_bus_phase(int stat) -{ - static const char *phases[] = { - "DATA OUT", "DATA IN", - "COMMAND", "STATUS", - "MISC OUT", "MISC IN", - "MESG OUT", "MESG IN" - }; - - return phases[stat & STAT_BUSMASK]; -} - -static const char *fas216_drv_phase(FAS216_Info *info) -{ - static const char *phases[] = { - [PHASE_IDLE] = "idle", - [PHASE_SELECTION] = "selection", - [PHASE_COMMAND] = "command", - [PHASE_DATAOUT] = "data out", - [PHASE_DATAIN] = "data in", - [PHASE_MSGIN] = "message in", - [PHASE_MSGIN_DISCONNECT]= "disconnect", - [PHASE_MSGOUT_EXPECT] = "expect message out", - [PHASE_MSGOUT] = "message out", - [PHASE_STATUS] = "status", - [PHASE_DONE] = "done", - }; - - if (info->scsi.phase < ARRAY_SIZE(phases) && - phases[info->scsi.phase]) - return phases[info->scsi.phase]; - return "???"; -} - -static char fas216_target(FAS216_Info *info) -{ - if (info->SCpnt) - return '0' + info->SCpnt->device->id; - else - return 'H'; -} - -static void -fas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap) -{ - static char buf[1024]; - - vsnprintf(buf, sizeof(buf), fmt, ap); - printk("scsi%d.%c: %s", info->host->host_no, target, buf); -} - -static void -fas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...) -{ - va_list args; - - if (level != 0 && !(level & level_mask)) - return; - - va_start(args, fmt); - fas216_do_log(info, '0' + SCpnt->device->id, fmt, args); - va_end(args); - - printk(" CDB: "); - print_command(SCpnt->cmnd); -} - -static void -fas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...) -{ - va_list args; - - if (level != 0 && !(level & level_mask)) - return; - - if (target < 0) - target = 'H'; - else - target += '0'; - - va_start(args, fmt); - fas216_do_log(info, target, fmt, args); - va_end(args); - - printk("\n"); -} - -static void fas216_log(FAS216_Info *info, int level, char *fmt, ...) -{ - va_list args; - - if (level != 0 && !(level & level_mask)) - return; - - va_start(args, fmt); - fas216_do_log(info, fas216_target(info), fmt, args); - va_end(args); - - printk("\n"); -} - -#define PH_SIZE 32 - -static struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE]; -static int ph_ptr; - -static void add_debug_list(int stat, int ssr, int isr, int ph) -{ - ph_list[ph_ptr].stat = stat; - ph_list[ph_ptr].ssr = ssr; - ph_list[ph_ptr].isr = isr; - ph_list[ph_ptr].ph = ph; - - ph_ptr = (ph_ptr + 1) & (PH_SIZE-1); -} - -static struct { int command; void *from; } cmd_list[8]; -static int cmd_ptr; - -static void fas216_cmd(FAS216_Info *info, unsigned int command) -{ - cmd_list[cmd_ptr].command = command; - cmd_list[cmd_ptr].from = __builtin_return_address(0); - - cmd_ptr = (cmd_ptr + 1) & 7; - - fas216_writeb(info, REG_CMD, command); -} - -static void print_debug_list(void) -{ - int i; - - i = ph_ptr; - - printk(KERN_ERR "SCSI IRQ trail\n"); - do { - printk(" %02x:%02x:%02x:%1x", - ph_list[i].stat, ph_list[i].ssr, - ph_list[i].isr, ph_list[i].ph); - i = (i + 1) & (PH_SIZE - 1); - if (((i ^ ph_ptr) & 7) == 0) - printk("\n"); - } while (i != ph_ptr); - if ((i ^ ph_ptr) & 7) - printk("\n"); - - i = cmd_ptr; - printk(KERN_ERR "FAS216 commands: "); - do { - printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from); - i = (i + 1) & 7; - } while (i != cmd_ptr); - printk("\n"); -} - -static void fas216_done(FAS216_Info *info, unsigned int result); - -/** - * fas216_get_last_msg - retrive last message from the list - * @info: interface to search - * @pos: current fifo position - * - * Retrieve a last message from the list, using position in fifo. - */ -static inline unsigned short -fas216_get_last_msg(FAS216_Info *info, int pos) -{ - unsigned short packed_msg = NOP; - struct message *msg; - int msgnr = 0; - - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - if (pos >= msg->fifo) - break; - } - - if (msg) { - if (msg->msg[0] == EXTENDED_MESSAGE) - packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; - else - packed_msg = msg->msg[0]; - } - - fas216_log(info, LOG_MESSAGES, - "Message: %04x found at position %02x\n", packed_msg, pos); - - return packed_msg; -} - -/** - * fas216_syncperiod - calculate STP register value - * @info: state structure for interface connected to device - * @ns: period in ns (between subsequent bytes) - * - * Calculate value to be loaded into the STP register for a given period - * in ns. Returns a value suitable for REG_STP. - */ -static int fas216_syncperiod(FAS216_Info *info, int ns) -{ - int value = (info->ifcfg.clockrate * ns) / 1000; - - fas216_checkmagic(info); - - if (value < 4) - value = 4; - else if (value > 35) - value = 35; - - return value & 31; -} - -/** - * fas216_set_sync - setup FAS216 chip for specified transfer period. - * @info: state structure for interface connected to device - * @target: target - * - * Correctly setup FAS216 chip for specified transfer period. - * Notes : we need to switch the chip out of FASTSCSI mode if we have - * a transfer period >= 200ns - otherwise the chip will violate - * the SCSI timings. - */ -static void fas216_set_sync(FAS216_Info *info, int target) -{ - unsigned int cntl3; - - fas216_writeb(info, REG_SOF, info->device[target].sof); - fas216_writeb(info, REG_STP, info->device[target].stp); - - cntl3 = info->scsi.cfg[2]; - if (info->device[target].period >= (200 / 4)) - cntl3 = cntl3 & ~CNTL3_FASTSCSI; - - fas216_writeb(info, REG_CNTL3, cntl3); -} - -/* Synchronous transfer support - * - * Note: The SCSI II r10 spec says (5.6.12): - * - * (2) Due to historical problems with early host adapters that could - * not accept an SDTR message, some targets may not initiate synchronous - * negotiation after a power cycle as required by this standard. Host - * adapters that support synchronous mode may avoid the ensuing failure - * modes when the target is independently power cycled by initiating a - * synchronous negotiation on each REQUEST SENSE and INQUIRY command. - * This approach increases the SCSI bus overhead and is not recommended - * for new implementations. The correct method is to respond to an - * SDTR message with a MESSAGE REJECT message if the either the - * initiator or target devices does not support synchronous transfers - * or does not want to negotiate for synchronous transfers at the time. - * Using the correct method assures compatibility with wide data - * transfers and future enhancements. - * - * We will always initiate a synchronous transfer negotiation request on - * every INQUIRY or REQUEST SENSE message, unless the target itself has - * at some point performed a synchronous transfer negotiation request, or - * we have synchronous transfers disabled for this device. - */ - -/** - * fas216_handlesync - Handle a synchronous transfer message - * @info: state structure for interface - * @msg: message from target - * - * Handle a synchronous transfer message from the target - */ -static void fas216_handlesync(FAS216_Info *info, char *msg) -{ - struct fas216_device *dev = &info->device[info->SCpnt->device->id]; - enum { sync, async, none, reject } res = none; - -#ifdef SCSI2_SYNC - switch (msg[0]) { - case MESSAGE_REJECT: - /* Synchronous transfer request failed. - * Note: SCSI II r10: - * - * SCSI devices that are capable of synchronous - * data transfers shall not respond to an SDTR - * message with a MESSAGE REJECT message. - * - * Hence, if we get this condition, we disable - * negotiation for this device. - */ - if (dev->sync_state == neg_inprogress) { - dev->sync_state = neg_invalid; - res = async; - } - break; - - case EXTENDED_MESSAGE: - switch (dev->sync_state) { - /* We don't accept synchronous transfer requests. - * Respond with a MESSAGE_REJECT to prevent a - * synchronous transfer agreement from being reached. - */ - case neg_invalid: - res = reject; - break; - - /* We were not negotiating a synchronous transfer, - * but the device sent us a negotiation request. - * Honour the request by sending back a SDTR - * message containing our capability, limited by - * the targets capability. - */ - default: - fas216_cmd(info, CMD_SETATN); - if (msg[4] > info->ifcfg.sync_max_depth) - msg[4] = info->ifcfg.sync_max_depth; - if (msg[3] < 1000 / info->ifcfg.clockrate) - msg[3] = 1000 / info->ifcfg.clockrate; - - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - msg[3], msg[4]); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - - /* This is wrong. The agreement is not in effect - * until this message is accepted by the device - */ - dev->sync_state = neg_targcomplete; - res = sync; - break; - - /* We initiated the synchronous transfer negotiation, - * and have successfully received a response from the - * target. The synchronous transfer agreement has been - * reached. Note: if the values returned are out of our - * bounds, we must reject the message. - */ - case neg_inprogress: - res = reject; - if (msg[4] <= info->ifcfg.sync_max_depth && - msg[3] >= 1000 / info->ifcfg.clockrate) { - dev->sync_state = neg_complete; - res = sync; - } - break; - } - } -#else - res = reject; -#endif - - switch (res) { - case sync: - dev->period = msg[3]; - dev->sof = msg[4]; - dev->stp = fas216_syncperiod(info, msg[3] * 4); - fas216_set_sync(info, info->SCpnt->device->id); - break; - - case reject: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - - case async: - dev->period = info->ifcfg.asyncperiod / 4; - dev->sof = 0; - dev->stp = info->scsi.async_stp; - fas216_set_sync(info, info->SCpnt->device->id); - break; - - case none: - break; - } -} - -/** - * fas216_updateptrs - update data pointers after transfer suspended/paused - * @info: interface's local pointer to update - * @bytes_transferred: number of bytes transferred - * - * Update data pointers after transfer suspended/paused - */ -static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) -{ - Scsi_Pointer *SCp = &info->scsi.SCp; - - fas216_checkmagic(info); - - BUG_ON(bytes_transferred < 0); - - info->SCpnt->request_bufflen -= bytes_transferred; - - while (bytes_transferred != 0) { - if (SCp->this_residual > bytes_transferred) - break; - /* - * We have used up this buffer. Move on to the - * next buffer. - */ - bytes_transferred -= SCp->this_residual; - if (!next_SCp(SCp) && bytes_transferred) { - printk(KERN_WARNING "scsi%d.%c: out of buffers\n", - info->host->host_no, '0' + info->SCpnt->device->id); - return; - } - } - - SCp->this_residual -= bytes_transferred; - if (SCp->this_residual) - SCp->ptr += bytes_transferred; - else - SCp->ptr = NULL; -} - -/** - * fas216_pio - transfer data off of/on to card using programmed IO - * @info: interface to transfer data to/from - * @direction: direction to transfer data (DMA_OUT/DMA_IN) - * - * Transfer data off of/on to card using programmed IO. - * Notes: this is incredibly slow. - */ -static void fas216_pio(FAS216_Info *info, fasdmadir_t direction) -{ - Scsi_Pointer *SCp = &info->scsi.SCp; - - fas216_checkmagic(info); - - if (direction == DMA_OUT) - fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp)); - else - put_next_SCp_byte(SCp, fas216_readb(info, REG_FF)); - - if (SCp->this_residual == 0) - next_SCp(SCp); -} - -static void fas216_set_stc(FAS216_Info *info, unsigned int length) -{ - fas216_writeb(info, REG_STCL, length); - fas216_writeb(info, REG_STCM, length >> 8); - fas216_writeb(info, REG_STCH, length >> 16); -} - -static unsigned int fas216_get_ctc(FAS216_Info *info) -{ - return fas216_readb(info, REG_CTCL) + - (fas216_readb(info, REG_CTCM) << 8) + - (fas216_readb(info, REG_CTCH) << 16); -} - -/** - * fas216_cleanuptransfer - clean up after a transfer has completed. - * @info: interface to clean up - * - * Update the data pointers according to the number of bytes transferred - * on the SCSI bus. - */ -static void fas216_cleanuptransfer(FAS216_Info *info) -{ - unsigned long total, residual, fifo; - fasdmatype_t dmatype = info->dma.transfer_type; - - info->dma.transfer_type = fasdma_none; - - /* - * PIO transfers do not need to be cleaned up. - */ - if (dmatype == fasdma_pio || dmatype == fasdma_none) - return; - - if (dmatype == fasdma_real_all) - total = info->SCpnt->request_bufflen; - else - total = info->scsi.SCp.this_residual; - - residual = fas216_get_ctc(info); - - fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - - fas216_log(info, LOG_BUFFER, "cleaning up from previous " - "transfer: length 0x%06x, residual 0x%x, fifo %d", - total, residual, fifo); - - /* - * If we were performing Data-Out, the transfer counter - * counts down each time a byte is transferred by the - * host to the FIFO. This means we must include the - * bytes left in the FIFO from the transfer counter. - */ - if (info->scsi.phase == PHASE_DATAOUT) - residual += fifo; - - fas216_updateptrs(info, total - residual); -} - -/** - * fas216_transfer - Perform a DMA/PIO transfer off of/on to card - * @info: interface from which device disconnected from - * - * Start a DMA/PIO transfer off of/on to card - */ -static void fas216_transfer(FAS216_Info *info) -{ - fasdmadir_t direction; - fasdmatype_t dmatype; - - fas216_log(info, LOG_BUFFER, - "starttransfer: buffer %p length 0x%06x reqlen 0x%06x", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual, - info->SCpnt->request_bufflen); - - if (!info->scsi.SCp.ptr) { - fas216_log(info, LOG_ERROR, "null buffer passed to " - "fas216_starttransfer"); - print_SCp(&info->scsi.SCp, "SCp: ", "\n"); - print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n"); - return; - } - - /* - * If we have a synchronous transfer agreement in effect, we must - * use DMA mode. If we are using asynchronous transfers, we may - * use DMA mode or PIO mode. - */ - if (info->device[info->SCpnt->device->id].sof) - dmatype = fasdma_real_all; - else - dmatype = fasdma_pio; - - if (info->scsi.phase == PHASE_DATAOUT) - direction = DMA_OUT; - else - direction = DMA_IN; - - if (info->dma.setup) - dmatype = info->dma.setup(info->host, &info->scsi.SCp, - direction, dmatype); - info->dma.transfer_type = dmatype; - - if (dmatype == fasdma_real_all) - fas216_set_stc(info, info->SCpnt->request_bufflen); - else - fas216_set_stc(info, info->scsi.SCp.this_residual); - - switch (dmatype) { - case fasdma_pio: - fas216_log(info, LOG_BUFFER, "PIO transfer"); - fas216_writeb(info, REG_SOF, 0); - fas216_writeb(info, REG_STP, info->scsi.async_stp); - fas216_cmd(info, CMD_TRANSFERINFO); - fas216_pio(info, direction); - break; - - case fasdma_pseudo: - fas216_log(info, LOG_BUFFER, "pseudo transfer"); - fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); - info->dma.pseudo(info->host, &info->scsi.SCp, - direction, info->SCpnt->transfersize); - break; - - case fasdma_real_block: - fas216_log(info, LOG_BUFFER, "block dma transfer"); - fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); - break; - - case fasdma_real_all: - fas216_log(info, LOG_BUFFER, "total dma transfer"); - fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); - break; - - default: - fas216_log(info, LOG_BUFFER | LOG_ERROR, - "invalid FAS216 DMA type"); - break; - } -} - -/** - * fas216_stoptransfer - Stop a DMA transfer onto / off of the card - * @info: interface from which device disconnected from - * - * Called when we switch away from DATA IN or DATA OUT phases. - */ -static void fas216_stoptransfer(FAS216_Info *info) -{ - fas216_checkmagic(info); - - if (info->dma.transfer_type == fasdma_real_all || - info->dma.transfer_type == fasdma_real_block) - info->dma.stop(info->host, &info->scsi.SCp); - - fas216_cleanuptransfer(info); - - if (info->scsi.phase == PHASE_DATAIN) { - unsigned int fifo; - - /* - * If we were performing Data-In, then the FIFO counter - * contains the number of bytes not transferred via DMA - * from the on-board FIFO. Read them manually. - */ - fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - while (fifo && info->scsi.SCp.ptr) { - *info->scsi.SCp.ptr = fas216_readb(info, REG_FF); - fas216_updateptrs(info, 1); - fifo--; - } - } else { - /* - * After a Data-Out phase, there may be unsent - * bytes left in the FIFO. Flush them out. - */ - fas216_cmd(info, CMD_FLUSHFIFO); - } -} - -static void fas216_aborttransfer(FAS216_Info *info) -{ - fas216_checkmagic(info); - - if (info->dma.transfer_type == fasdma_real_all || - info->dma.transfer_type == fasdma_real_block) - info->dma.stop(info->host, &info->scsi.SCp); - - info->dma.transfer_type = fasdma_none; - fas216_cmd(info, CMD_FLUSHFIFO); -} - -static void fas216_kick(FAS216_Info *info); - -/** - * fas216_disconnected_intr - handle device disconnection - * @info: interface from which device disconnected from - * - * Handle device disconnection - */ -static void fas216_disconnect_intr(FAS216_Info *info) -{ - unsigned long flags; - - fas216_checkmagic(info); - - fas216_log(info, LOG_CONNECT, "disconnect phase=%02x", - info->scsi.phase); - - msgqueue_flush(&info->scsi.msgs); - - switch (info->scsi.phase) { - case PHASE_SELECTION: /* while selecting - no target */ - case PHASE_SELSTEPS: - fas216_done(info, DID_NO_CONNECT); - break; - - case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ - info->scsi.disconnectable = 1; - info->scsi.phase = PHASE_IDLE; - info->stats.disconnects += 1; - spin_lock_irqsave(&info->host_lock, flags); - if (info->scsi.phase == PHASE_IDLE) - fas216_kick(info); - spin_unlock_irqrestore(&info->host_lock, flags); - break; - - case PHASE_DONE: /* at end of command - complete */ - fas216_done(info, DID_OK); - break; - - case PHASE_MSGOUT: /* message out - possible ABORT message */ - if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { - info->scsi.aborting = 0; - fas216_done(info, DID_ABORT); - break; - } - - default: /* huh? */ - printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n", - info->host->host_no, fas216_target(info), fas216_drv_phase(info)); - print_debug_list(); - fas216_stoptransfer(info); - fas216_done(info, DID_ERROR); - break; - } -} - -/** - * fas216_reselected_intr - start reconnection of a device - * @info: interface which was reselected - * - * Start reconnection of a device - */ -static void -fas216_reselected_intr(FAS216_Info *info) -{ - unsigned int cfis, i; - unsigned char msg[4]; - unsigned char target, lun, tag; - - fas216_checkmagic(info); - - WARN_ON(info->scsi.phase == PHASE_SELECTION || - info->scsi.phase == PHASE_SELSTEPS); - - cfis = fas216_readb(info, REG_CFIS); - - fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x", - info->scsi.phase, cfis); - - cfis &= CFIS_CF; - - if (cfis < 2 || cfis > 4) { - printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", - info->host->host_no); - goto bad_message; - } - - for (i = 0; i < cfis; i++) - msg[i] = fas216_readb(info, REG_FF); - - if (!(msg[0] & (1 << info->host->this_id)) || - !(msg[1] & 0x80)) - goto initiator_error; - - target = msg[0] & ~(1 << info->host->this_id); - target = ffs(target) - 1; - lun = msg[1] & 7; - tag = 0; - - if (cfis >= 3) { - if (msg[2] != SIMPLE_QUEUE_TAG) - goto initiator_error; - - tag = msg[3]; - } - - /* set up for synchronous transfers */ - fas216_writeb(info, REG_SDID, target); - fas216_set_sync(info, target); - msgqueue_flush(&info->scsi.msgs); - - fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x", - target, lun, tag); - - if (info->scsi.disconnectable && info->SCpnt) { - info->scsi.disconnectable = 0; - if (info->SCpnt->device->id == target && - info->SCpnt->device->lun == lun && - info->SCpnt->tag == tag) { - fas216_log(info, LOG_CONNECT, "reconnected previously executing command"); - } else { - queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); - fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue"); - info->SCpnt = NULL; - } - } - if (!info->SCpnt) { - info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected, - target, lun, tag); - fas216_log(info, LOG_CONNECT, "had to get command"); - } - - if (info->SCpnt) { - /* - * Restore data pointer from SAVED data pointer - */ - info->scsi.SCp = info->SCpnt->SCp; - - fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - info->scsi.phase = PHASE_MSGIN; - } else { - /* - * Our command structure not found - abort the - * command on the target. Since we have no - * record of this command, we can't send - * an INITIATOR DETECTED ERROR message. - */ - fas216_cmd(info, CMD_SETATN); - -#if 0 - if (tag) - msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, tag); - else -#endif - msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - info->scsi.aborting = 1; - } - - fas216_cmd(info, CMD_MSGACCEPTED); - return; - - initiator_error: - printk(KERN_ERR "scsi%d.H: error during reselection: bytes", - info->host->host_no); - for (i = 0; i < cfis; i++) - printk(" %02x", msg[i]); - printk("\n"); - bad_message: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - fas216_cmd(info, CMD_MSGACCEPTED); -} - -static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen) -{ - int i; - - switch (message[0]) { - case COMMAND_COMPLETE: - if (msglen != 1) - goto unrecognised; - - printk(KERN_ERR "scsi%d.%c: command complete with no " - "status in MESSAGE_IN?\n", - info->host->host_no, fas216_target(info)); - break; - - case SAVE_POINTERS: - if (msglen != 1) - goto unrecognised; - - /* - * Save current data pointer to SAVED data pointer - * SCSI II standard says that we must not acknowledge - * this until we have really saved pointers. - * NOTE: we DO NOT save the command nor status pointers - * as required by the SCSI II standard. These always - * point to the start of their respective areas. - */ - info->SCpnt->SCp = info->scsi.SCp; - info->SCpnt->SCp.sent_command = 0; - fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, - "save data pointers: [%p, %X]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - break; - - case RESTORE_POINTERS: - if (msglen != 1) - goto unrecognised; - - /* - * Restore current data pointer from SAVED data pointer - */ - info->scsi.SCp = info->SCpnt->SCp; - fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, - "restore data pointers: [%p, 0x%x]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - break; - - case DISCONNECT: - if (msglen != 1) - goto unrecognised; - - info->scsi.phase = PHASE_MSGIN_DISCONNECT; - break; - - case MESSAGE_REJECT: - if (msglen != 1) - goto unrecognised; - - switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) { - case EXTENDED_MESSAGE | EXTENDED_SDTR << 8: - fas216_handlesync(info, message); - break; - - default: - fas216_log(info, 0, "reject, last message 0x%04x", - fas216_get_last_msg(info, info->scsi.msgin_fifo)); - } - break; - - case NOP: - break; - - case EXTENDED_MESSAGE: - if (msglen < 3) - goto unrecognised; - - switch (message[2]) { - case EXTENDED_SDTR: /* Sync transfer negotiation request/reply */ - fas216_handlesync(info, message); - break; - - default: - goto unrecognised; - } - break; - - default: - goto unrecognised; - } - return; - -unrecognised: - fas216_log(info, 0, "unrecognised message, rejecting"); - printk("scsi%d.%c: message was", info->host->host_no, fas216_target(info)); - for (i = 0; i < msglen; i++) - printk("%s%02X", i & 31 ? " " : "\n ", message[i]); - printk("\n"); - - /* - * Something strange seems to be happening here - - * I can't use SETATN since the chip gives me an - * invalid command interrupt when I do. Weird. - */ -fas216_cmd(info, CMD_NOP); -fas216_dumpstate(info); - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); - info->scsi.phase = PHASE_MSGOUT_EXPECT; -fas216_dumpstate(info); -} - -static int fas216_wait_cmd(FAS216_Info *info, int cmd) -{ - int tout; - int stat; - - fas216_cmd(info, cmd); - - for (tout = 1000; tout; tout -= 1) { - stat = fas216_readb(info, REG_STAT); - if (stat & (STAT_INT|STAT_PARITYERROR)) - break; - udelay(1); - } - - return stat; -} - -static int fas216_get_msg_byte(FAS216_Info *info) -{ - unsigned int stat = fas216_wait_cmd(info, CMD_MSGACCEPTED); - - if ((stat & STAT_INT) == 0) - goto timedout; - - if ((stat & STAT_BUSMASK) != STAT_MESGIN) - goto unexpected_phase_change; - - fas216_readb(info, REG_INST); - - stat = fas216_wait_cmd(info, CMD_TRANSFERINFO); - - if ((stat & STAT_INT) == 0) - goto timedout; - - if (stat & STAT_PARITYERROR) - goto parity_error; - - if ((stat & STAT_BUSMASK) != STAT_MESGIN) - goto unexpected_phase_change; - - fas216_readb(info, REG_INST); - - return fas216_readb(info, REG_FF); - -timedout: - fas216_log(info, LOG_ERROR, "timed out waiting for message byte"); - return -1; - -unexpected_phase_change: - fas216_log(info, LOG_ERROR, "unexpected phase change: status = %02x", stat); - return -2; - -parity_error: - fas216_log(info, LOG_ERROR, "parity error during message in phase"); - return -3; -} - -/** - * fas216_message - handle a function done interrupt from FAS216 chip - * @info: interface which caused function done interrupt - * - * Handle a function done interrupt from FAS216 chip - */ -static void fas216_message(FAS216_Info *info) -{ - unsigned char *message = info->scsi.message; - unsigned int msglen = 1; - int msgbyte = 0; - - fas216_checkmagic(info); - - message[0] = fas216_readb(info, REG_FF); - - if (message[0] == EXTENDED_MESSAGE) { - msgbyte = fas216_get_msg_byte(info); - - if (msgbyte >= 0) { - message[1] = msgbyte; - - for (msglen = 2; msglen < message[1] + 2; msglen++) { - msgbyte = fas216_get_msg_byte(info); - - if (msgbyte >= 0) - message[msglen] = msgbyte; - else - break; - } - } - } - - if (msgbyte == -3) - goto parity_error; - -#ifdef DEBUG_MESSAGES - { - int i; - - printk("scsi%d.%c: message in: ", - info->host->host_no, fas216_target(info)); - for (i = 0; i < msglen; i++) - printk("%02X ", message[i]); - printk("\n"); - } -#endif - - fas216_parse_message(info, message, msglen); - fas216_cmd(info, CMD_MSGACCEPTED); - return; - -parity_error: - fas216_cmd(info, CMD_SETATN); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, MSG_PARITY_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - fas216_cmd(info, CMD_MSGACCEPTED); - return; -} - -/** - * fas216_send_command - send command after all message bytes have been sent - * @info: interface which caused bus service - * - * Send a command to a target after all message bytes have been sent - */ -static void fas216_send_command(FAS216_Info *info) -{ - int i; - - fas216_checkmagic(info); - - fas216_cmd(info, CMD_NOP|CMD_WITHDMA); - fas216_cmd(info, CMD_FLUSHFIFO); - - /* load command */ - for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++) - fas216_writeb(info, REG_FF, info->SCpnt->cmnd[i]); - - fas216_cmd(info, CMD_TRANSFERINFO); - - info->scsi.phase = PHASE_COMMAND; -} - -/** - * fas216_send_messageout - handle bus service to send a message - * @info: interface which caused bus service - * - * Handle bus service to send a message. - * Note: We do not allow the device to change the data direction! - */ -static void fas216_send_messageout(FAS216_Info *info, int start) -{ - unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); - - fas216_checkmagic(info); - - fas216_cmd(info, CMD_FLUSHFIFO); - - if (tot_msglen) { - struct message *msg; - int msgnr = 0; - - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - int i; - - for (i = start; i < msg->length; i++) - fas216_writeb(info, REG_FF, msg->msg[i]); - - msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); - start = 0; - } - } else - fas216_writeb(info, REG_FF, NOP); - - fas216_cmd(info, CMD_TRANSFERINFO); - - info->scsi.phase = PHASE_MSGOUT; -} - -/** - * fas216_busservice_intr - handle bus service interrupt from FAS216 chip - * @info: interface which caused bus service interrupt - * @stat: Status register contents - * @is: SCSI Status register contents - * - * Handle a bus service interrupt from FAS216 chip - */ -static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int is) -{ - fas216_checkmagic(info); - - fas216_log(info, LOG_BUSSERVICE, - "bus service: stat=%02x is=%02x phase=%02x", - stat, is, info->scsi.phase); - - switch (info->scsi.phase) { - case PHASE_SELECTION: - if ((is & IS_BITS) != IS_MSGBYTESENT) - goto bad_is; - break; - - case PHASE_SELSTEPS: - switch (is & IS_BITS) { - case IS_SELARB: - case IS_MSGBYTESENT: - goto bad_is; - - case IS_NOTCOMMAND: - case IS_EARLYPHASE: - if ((stat & STAT_BUSMASK) == STAT_MESGIN) - break; - goto bad_is; - - case IS_COMPLETE: - break; - } - - default: - break; - } - - fas216_cmd(info, CMD_NOP); - -#define STATE(st,ph) ((ph) << 3 | (st)) - /* This table describes the legal SCSI state transitions, - * as described by the SCSI II spec. - */ - switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { - case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ - case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ - case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ - case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */ - info->scsi.phase = PHASE_DATAIN; - fas216_transfer(info); - return; - - case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */ - case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */ - fas216_cleanuptransfer(info); - fas216_transfer(info); - return; - - case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ - case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ - case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ - case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */ - fas216_cmd(info, CMD_FLUSHFIFO); - info->scsi.phase = PHASE_DATAOUT; - fas216_transfer(info); - return; - - case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ - case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ - fas216_stoptransfer(info); - case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ - case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ - case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ - case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ - fas216_cmd(info, CMD_INITCMDCOMPLETE); - info->scsi.phase = PHASE_STATUS; - return; - - case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ - case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ - fas216_stoptransfer(info); - case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */ - case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ - case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ - info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - fas216_cmd(info, CMD_FLUSHFIFO); - fas216_cmd(info, CMD_TRANSFERINFO); - info->scsi.phase = PHASE_MSGIN; - return; - - case STATE(STAT_MESGIN, PHASE_MSGIN): - info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; - fas216_cmd(info, CMD_TRANSFERINFO); - return; - - case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ - case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ - fas216_send_command(info); - info->scsi.phase = PHASE_COMMAND; - return; - - - /* - * Selection -> Message Out - */ - case STATE(STAT_MESGOUT, PHASE_SELECTION): - fas216_send_messageout(info, 1); - return; - - /* - * Message Out -> Message Out - */ - case STATE(STAT_MESGOUT, PHASE_SELSTEPS): - case STATE(STAT_MESGOUT, PHASE_MSGOUT): - /* - * If we get another message out phase, this usually - * means some parity error occurred. Resend complete - * set of messages. If we have more than one byte to - * send, we need to assert ATN again. - */ - if (info->device[info->SCpnt->device->id].parity_check) { - /* - * We were testing... good, the device - * supports parity checking. - */ - info->device[info->SCpnt->device->id].parity_check = 0; - info->device[info->SCpnt->device->id].parity_enabled = 1; - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - } - - if (msgqueue_msglength(&info->scsi.msgs) > 1) - fas216_cmd(info, CMD_SETATN); - /*FALLTHROUGH*/ - - /* - * Any -> Message Out - */ - case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT): - fas216_send_messageout(info, 0); - return; - - /* Error recovery rules. - * These either attempt to abort or retry the operation. - * TODO: we need more of these - */ - case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ - /* error - we've sent out all the command bytes - * we have. - * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS - * to include the command bytes sent for this to work - * correctly. - */ - printk(KERN_ERR "scsi%d.%c: " - "target trying to receive more command bytes\n", - info->host->host_no, fas216_target(info)); - fas216_cmd(info, CMD_SETATN); - fas216_set_stc(info, 15); - fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA); - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - return; - } - - if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { - printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat)); - msgqueue_flush(&info->scsi.msgs); - fas216_cmd(info, CMD_SETATN); - msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); - info->scsi.phase = PHASE_MSGOUT_EXPECT; - info->scsi.aborting = 1; - fas216_cmd(info, CMD_TRANSFERINFO); - return; - } - printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", - info->host->host_no, fas216_target(info), - fas216_bus_phase(stat), - fas216_drv_phase(info)); - print_debug_list(); - return; - -bad_is: - fas216_log(info, 0, "bus service at step %d?", is & IS_BITS); - fas216_dumpstate(info); - print_debug_list(); - - fas216_done(info, DID_ERROR); -} - -/** - * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip - * @info: interface which caused function done interrupt - * @stat: Status register contents - * @is: SCSI Status register contents - * - * Handle a function done interrupt from FAS216 chip - */ -static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is) -{ - unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF; - - fas216_checkmagic(info); - - fas216_log(info, LOG_FUNCTIONDONE, - "function done: stat=%02x is=%02x phase=%02x", - stat, is, info->scsi.phase); - - switch (info->scsi.phase) { - case PHASE_STATUS: /* status phase - read status and msg */ - if (fifo_len != 2) { - fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len); - } - /* - * Read status then message byte. - */ - info->scsi.SCp.Status = fas216_readb(info, REG_FF); - info->scsi.SCp.Message = fas216_readb(info, REG_FF); - info->scsi.phase = PHASE_DONE; - fas216_cmd(info, CMD_MSGACCEPTED); - break; - - case PHASE_IDLE: - case PHASE_SELECTION: - case PHASE_SELSTEPS: - break; - - case PHASE_MSGIN: /* message in phase */ - if ((stat & STAT_BUSMASK) == STAT_MESGIN) { - info->scsi.msgin_fifo = fifo_len; - fas216_message(info); - break; - } - - default: - fas216_log(info, 0, "internal phase %s for function done?" - " What do I do with this?", - fas216_target(info), fas216_drv_phase(info)); - } -} - -static void fas216_bus_reset(FAS216_Info *info) -{ - neg_t sync_state; - int i; - - msgqueue_flush(&info->scsi.msgs); - - sync_state = neg_invalid; - -#ifdef SCSI2_SYNC - if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA)) - sync_state = neg_wait; -#endif - - info->scsi.phase = PHASE_IDLE; - info->SCpnt = NULL; /* bug! */ - memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp)); - - for (i = 0; i < 8; i++) { - info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; - info->device[i].sync_state = sync_state; - info->device[i].period = info->ifcfg.asyncperiod / 4; - info->device[i].stp = info->scsi.async_stp; - info->device[i].sof = 0; - info->device[i].wide_xfer = 0; - } - - info->rst_bus_status = 1; - wake_up(&info->eh_wait); -} - -/** - * fas216_intr - handle interrupts to progress a command - * @info: interface to service - * - * Handle interrupts from the interface to progress a command - */ -irqreturn_t fas216_intr(FAS216_Info *info) -{ - unsigned char inst, is, stat; - int handled = IRQ_NONE; - - fas216_checkmagic(info); - - stat = fas216_readb(info, REG_STAT); - is = fas216_readb(info, REG_IS); - inst = fas216_readb(info, REG_INST); - - add_debug_list(stat, is, inst, info->scsi.phase); - - if (stat & STAT_INT) { - if (inst & INST_BUSRESET) { - fas216_log(info, 0, "bus reset detected"); - fas216_bus_reset(info); - scsi_report_bus_reset(info->host, 0); - } else if (inst & INST_ILLEGALCMD) { - fas216_log(info, LOG_ERROR, "illegal command given\n"); - fas216_dumpstate(info); - print_debug_list(); - } else if (inst & INST_DISCONNECT) - fas216_disconnect_intr(info); - else if (inst & INST_RESELECTED) /* reselected */ - fas216_reselected_intr(info); - else if (inst & INST_BUSSERVICE) /* bus service request */ - fas216_busservice_intr(info, stat, is); - else if (inst & INST_FUNCDONE) /* function done */ - fas216_funcdone_intr(info, stat, is); - else - fas216_log(info, 0, "unknown interrupt received:" - " phase %s inst %02X is %02X stat %02X", - fas216_drv_phase(info), inst, is, stat); - handled = IRQ_HANDLED; - } - return handled; -} - -static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - int tot_msglen; - - /* following what the ESP driver says */ - fas216_set_stc(info, 0); - fas216_cmd(info, CMD_NOP | CMD_WITHDMA); - - /* flush FIFO */ - fas216_cmd(info, CMD_FLUSHFIFO); - - /* load bus-id and timeout */ - fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); - fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); - - /* synchronous transfers */ - fas216_set_sync(info, SCpnt->device->id); - - tot_msglen = msgqueue_msglength(&info->scsi.msgs); - -#ifdef DEBUG_MESSAGES - { - struct message *msg; - int msgnr = 0, i; - - printk("scsi%d.%c: message out: ", - info->host->host_no, '0' + SCpnt->device->id); - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - printk("{ "); - for (i = 0; i < msg->length; i++) - printk("%02x ", msg->msg[i]); - printk("} "); - } - printk("\n"); - } -#endif - - if (tot_msglen == 1 || tot_msglen == 3) { - /* - * We have an easy message length to send... - */ - struct message *msg; - int msgnr = 0, i; - - info->scsi.phase = PHASE_SELSTEPS; - - /* load message bytes */ - while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { - for (i = 0; i < msg->length; i++) - fas216_writeb(info, REG_FF, msg->msg[i]); - msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); - } - - /* load command */ - for (i = 0; i < SCpnt->cmd_len; i++) - fas216_writeb(info, REG_FF, SCpnt->cmnd[i]); - - if (tot_msglen == 1) - fas216_cmd(info, CMD_SELECTATN); - else - fas216_cmd(info, CMD_SELECTATN3); - } else { - /* - * We have an unusual number of message bytes to send. - * Load first byte into fifo, and issue SELECT with ATN and - * stop steps. - */ - struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); - - fas216_writeb(info, REG_FF, msg->msg[0]); - msg->fifo = 1; - - fas216_cmd(info, CMD_SELECTATNSTOP); - } -} - -/* - * Decide whether we need to perform a parity test on this device. - * Can also be used to force parity error conditions during initial - * information transfer phase (message out) for test purposes. - */ -static int parity_test(FAS216_Info *info, int target) -{ -#if 0 - if (target == 3) { - info->device[target].parity_check = 0; - return 1; - } -#endif - return info->device[target].parity_check; -} - -static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - int disconnect_ok; - - /* - * claim host busy - */ - info->scsi.phase = PHASE_SELECTION; - info->scsi.SCp = SCpnt->SCp; - info->SCpnt = SCpnt; - info->dma.transfer_type = fasdma_none; - - if (parity_test(info, SCpnt->device->id)) - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE); - else - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - - /* - * Don't allow request sense commands to disconnect. - */ - disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE && - info->device[SCpnt->device->id].disconnect_ok; - - /* - * build outgoing message bytes - */ - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->device->lun)); - - /* - * add tag message if required - */ - if (SCpnt->tag) - msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); - - do { -#ifdef SCSI2_SYNC - if ((info->device[SCpnt->device->id].sync_state == neg_wait || - info->device[SCpnt->device->id].sync_state == neg_complete) && - (SCpnt->cmnd[0] == REQUEST_SENSE || - SCpnt->cmnd[0] == INQUIRY)) { - info->device[SCpnt->device->id].sync_state = neg_inprogress; - msgqueue_addmsg(&info->scsi.msgs, 5, - EXTENDED_MESSAGE, 3, EXTENDED_SDTR, - 1000 / info->ifcfg.clockrate, - info->ifcfg.sync_max_depth); - break; - } -#endif - } while (0); - - __fas216_start_command(info, SCpnt); -} - -static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ -#ifdef SCSI2_TAG - /* - * tagged queuing - allocate a new tag to this command - */ - if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && - SCpnt->cmnd[0] != INQUIRY) { - SCpnt->device->current_tag += 1; - if (SCpnt->device->current_tag == 0) - SCpnt->device->current_tag = 1; - SCpnt->tag = SCpnt->device->current_tag; - } else -#endif - set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); - - info->stats.removes += 1; - switch (SCpnt->cmnd[0]) { - case WRITE_6: - case WRITE_10: - case WRITE_12: - info->stats.writes += 1; - break; - case READ_6: - case READ_10: - case READ_12: - info->stats.reads += 1; - break; - default: - info->stats.miscs += 1; - break; - } -} - -static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - struct message *msg; - - /* - * claim host busy - */ - info->scsi.phase = PHASE_SELECTION; - info->scsi.SCp = SCpnt->SCp; - info->SCpnt = SCpnt; - info->dma.transfer_type = fasdma_none; - - fas216_log(info, LOG_ERROR, "sending bus device reset"); - - msgqueue_flush(&info->scsi.msgs); - msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET); - - /* following what the ESP driver says */ - fas216_set_stc(info, 0); - fas216_cmd(info, CMD_NOP | CMD_WITHDMA); - - /* flush FIFO */ - fas216_cmd(info, CMD_FLUSHFIFO); - - /* load bus-id and timeout */ - fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); - fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); - - /* synchronous transfers */ - fas216_set_sync(info, SCpnt->device->id); - - msg = msgqueue_getmsg(&info->scsi.msgs, 0); - - fas216_writeb(info, REG_FF, BUS_DEVICE_RESET); - msg->fifo = 1; - - fas216_cmd(info, CMD_SELECTATNSTOP); -} - -/** - * fas216_kick - kick a command to the interface - * @info: our host interface to kick - * - * Kick a command to the interface, interface should be idle. - * Notes: Interrupts are always disabled! - */ -static void fas216_kick(FAS216_Info *info) -{ - Scsi_Cmnd *SCpnt = NULL; -#define TYPE_OTHER 0 -#define TYPE_RESET 1 -#define TYPE_QUEUE 2 - int where_from = TYPE_OTHER; - - fas216_checkmagic(info); - - /* - * Obtain the next command to process. - */ - do { - if (info->rstSCpnt) { - SCpnt = info->rstSCpnt; - /* don't remove it */ - where_from = TYPE_RESET; - break; - } - - if (info->reqSCpnt) { - SCpnt = info->reqSCpnt; - info->reqSCpnt = NULL; - break; - } - - if (info->origSCpnt) { - SCpnt = info->origSCpnt; - info->origSCpnt = NULL; - break; - } - - /* retrieve next command */ - if (!SCpnt) { - SCpnt = queue_remove_exclude(&info->queues.issue, - info->busyluns); - where_from = TYPE_QUEUE; - break; - } - } while (0); - - if (!SCpnt) { - /* - * no command pending, so enable reselection. - */ - fas216_cmd(info, CMD_ENABLESEL); - return; - } - - /* - * We're going to start a command, so disable reselection - */ - fas216_cmd(info, CMD_DISABLESEL); - - if (info->scsi.disconnectable && info->SCpnt) { - fas216_log(info, LOG_CONNECT, - "moved command for %d to disconnected queue", - info->SCpnt->device->id); - queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); - info->scsi.disconnectable = 0; - info->SCpnt = NULL; - } - - fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt, - "starting"); - - switch (where_from) { - case TYPE_QUEUE: - fas216_allocate_tag(info, SCpnt); - case TYPE_OTHER: - fas216_start_command(info, SCpnt); - break; - case TYPE_RESET: - fas216_do_bus_device_reset(info, SCpnt); - break; - } - - fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]", - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - - /* - * should now get either DISCONNECT or - * (FUNCTION DONE with BUS SERVICE) interrupt - */ -} - -/* - * Clean up from issuing a BUS DEVICE RESET message to a device. - */ -static void -fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) -{ - fas216_log(info, LOG_ERROR, "fas216 device reset complete"); - - info->rstSCpnt = NULL; - info->rst_dev_status = 1; - wake_up(&info->eh_wait); -} - -/** - * fas216_rq_sns_done - Finish processing automatic request sense command - * @info: interface that completed - * @SCpnt: command that completed - * @result: driver byte of result - * - * Finish processing automatic request sense command - */ -static void -fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) -{ - fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, - "request sense complete, result=0x%04x%02x%02x", - result, SCpnt->SCp.Message, SCpnt->SCp.Status); - - if (result != DID_OK || SCpnt->SCp.Status != GOOD) - /* - * Something went wrong. Make sure that we don't - * have valid data in the sense buffer that could - * confuse the higher levels. - */ - memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); -//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id); -//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); } - /* - * Note that we don't set SCpnt->result, since that should - * reflect the status of the command that we were asked by - * the upper layers to process. This would have been set - * correctly by fas216_std_done. - */ - SCpnt->scsi_done(SCpnt); -} - -/** - * fas216_std_done - finish processing of standard command - * @info: interface that completed - * @SCpnt: command that completed - * @result: driver byte of result - * - * Finish processing of standard command - */ -static void -fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) -{ - info->stats.fins += 1; - - SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | - info->scsi.SCp.Status; - - fas216_log_command(info, LOG_CONNECT, SCpnt, - "command complete, result=0x%08x", SCpnt->result); - - /* - * If the driver detected an error, we're all done. - */ - if (host_byte(SCpnt->result) != DID_OK || - msg_byte(SCpnt->result) != COMMAND_COMPLETE) - goto done; - - /* - * If the command returned CHECK_CONDITION or COMMAND_TERMINATED - * status, request the sense information. - */ - if (status_byte(SCpnt->result) == CHECK_CONDITION || - status_byte(SCpnt->result) == COMMAND_TERMINATED) - goto request_sense; - - /* - * If the command did not complete with GOOD status, - * we are all done here. - */ - if (status_byte(SCpnt->result) != GOOD) - goto done; - - /* - * We have successfully completed a command. Make sure that - * we do not have any buffers left to transfer. The world - * is not perfect, and we seem to occasionally hit this. - * It can be indicative of a buggy driver, target or the upper - * levels of the SCSI code. - */ - if (info->scsi.SCp.ptr) { - switch (SCpnt->cmnd[0]) { - case INQUIRY: - case START_STOP: - case MODE_SENSE: - break; - - default: - printk(KERN_ERR "scsi%d.%c: incomplete data transfer " - "detected: res=%08X ptr=%p len=%X CDB: ", - info->host->host_no, '0' + SCpnt->device->id, - SCpnt->result, info->scsi.SCp.ptr, - info->scsi.SCp.this_residual); - print_command(SCpnt->cmnd); - SCpnt->result &= ~(255 << 16); - SCpnt->result |= DID_BAD_TARGET << 16; - goto request_sense; - } - } - -done: - if (SCpnt->scsi_done) { - SCpnt->scsi_done(SCpnt); - return; - } - - panic("scsi%d.H: null scsi_done function in fas216_done", - info->host->host_no); - - -request_sense: - if (SCpnt->cmnd[0] == REQUEST_SENSE) - goto done; - - fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, - "requesting sense"); - memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd)); - SCpnt->cmnd[0] = REQUEST_SENSE; - SCpnt->cmnd[1] = SCpnt->device->lun << 5; - SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); - SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - SCpnt->SCp.buffer = NULL; - SCpnt->SCp.buffers_residual = 0; - SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; - SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); - SCpnt->SCp.Message = 0; - SCpnt->SCp.Status = 0; - SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); - SCpnt->sc_data_direction = SCSI_DATA_READ; - SCpnt->use_sg = 0; - SCpnt->tag = 0; - SCpnt->host_scribble = (void *)fas216_rq_sns_done; - - /* - * Place this command into the high priority "request - * sense" slot. This will be the very next command - * executed, unless a target connects to us. - */ - if (info->reqSCpnt) - printk(KERN_WARNING "scsi%d.%c: loosing request command\n", - info->host->host_no, '0' + SCpnt->device->id); - info->reqSCpnt = SCpnt; -} - -/** - * fas216_done - complete processing for current command - * @info: interface that completed - * @result: driver byte of result - * - * Complete processing for current command - */ -static void fas216_done(FAS216_Info *info, unsigned int result) -{ - void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int); - Scsi_Cmnd *SCpnt; - unsigned long flags; - - fas216_checkmagic(info); - - if (!info->SCpnt) - goto no_command; - - SCpnt = info->SCpnt; - info->SCpnt = NULL; - info->scsi.phase = PHASE_IDLE; - - if (info->scsi.aborting) { - fas216_log(info, 0, "uncaught abort - returning DID_ABORT"); - result = DID_ABORT; - info->scsi.aborting = 0; - } - - /* - * Sanity check the completion - if we have zero bytes left - * to transfer, we should not have a valid pointer. - */ - if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { - printk("scsi%d.%c: zero bytes left to transfer, but " - "buffer pointer still valid: ptr=%p len=%08x CDB: ", - info->host->host_no, '0' + SCpnt->device->id, - info->scsi.SCp.ptr, info->scsi.SCp.this_residual); - info->scsi.SCp.ptr = NULL; - print_command(SCpnt->cmnd); - } - - /* - * Clear down this command as completed. If we need to request - * the sense information, fas216_kick will re-assert the busy - * status. - */ - info->device[SCpnt->device->id].parity_check = 0; - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); - - fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble; - fn(info, SCpnt, result); - - if (info->scsi.irq != NO_IRQ) { - spin_lock_irqsave(&info->host_lock, flags); - if (info->scsi.phase == PHASE_IDLE) - fas216_kick(info); - spin_unlock_irqrestore(&info->host_lock, flags); - } - return; - -no_command: - panic("scsi%d.H: null command in fas216_done", - info->host->host_no); -} - -/** - * fas216_queue_command - queue a command for adapter to process. - * @SCpnt: Command to queue - * @done: done function to call once command is complete - * - * Queue a command for adapter to process. - * Returns: 0 on success, else error. - * Notes: io_request_lock is held, interrupts are disabled. - */ -int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - int result; - - fas216_checkmagic(info); - - fas216_log_command(info, LOG_CONNECT, SCpnt, - "received command (%p)", SCpnt); - - SCpnt->scsi_done = done; - SCpnt->host_scribble = (void *)fas216_std_done; - SCpnt->result = 0; - - init_SCp(SCpnt); - - info->stats.queues += 1; - SCpnt->tag = 0; - - spin_lock(&info->host_lock); - - /* - * Add command into execute queue and let it complete under - * whatever scheme we're using. - */ - result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt); - - /* - * If we successfully added the command, - * kick the interface to get it moving. - */ - if (result == 0 && info->scsi.phase == PHASE_IDLE) - fas216_kick(info); - spin_unlock(&info->host_lock); - - fas216_log_target(info, LOG_CONNECT, -1, "queue %s", - result ? "failure" : "success"); - - return result; -} - -/** - * fas216_internal_done - trigger restart of a waiting thread in fas216_command - * @SCpnt: Command to wake - * - * Trigger restart of a waiting thread in fas216_command - */ -static void fas216_internal_done(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - - fas216_checkmagic(info); - - info->internal_done = 1; -} - -/** - * fas216_command - queue a command for adapter to process. - * @SCpnt: Command to queue - * - * Queue a command for adapter to process. - * Returns: scsi result code. - * Notes: io_request_lock is held, interrupts are disabled. - */ -int fas216_command(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - - fas216_checkmagic(info); - - /* - * We should only be using this if we don't have an interrupt. - * Provide some "incentive" to use the queueing code. - */ - if (info->scsi.irq != NO_IRQ) - BUG(); - - info->internal_done = 0; - fas216_queue_command(SCpnt, fas216_internal_done); - - /* - * This wastes time, since we can't return until the command is - * complete. We can't sleep either since we may get re-entered! - * However, we must re-enable interrupts, or else we'll be - * waiting forever. - */ - spin_unlock_irq(info->host->host_lock); - - while (!info->internal_done) { - /* - * If we don't have an IRQ, then we must poll the card for - * it's interrupt, and use that to call this driver's - * interrupt routine. That way, we keep the command - * progressing. Maybe we can add some inteligence here - * and go to sleep if we know that the device is going - * to be some time (eg, disconnected). - */ - if (fas216_readb(info, REG_STAT) & STAT_INT) { - spin_lock_irq(info->host->host_lock); - fas216_intr(info); - spin_unlock_irq(info->host->host_lock); - } - } - - spin_lock_irq(info->host->host_lock); - - return SCpnt->result; -} - -/* - * Error handler timeout function. Indicate that we timed out, - * and wake up any error handler process so it can continue. - */ -static void fas216_eh_timer(unsigned long data) -{ - FAS216_Info *info = (FAS216_Info *)data; - - fas216_log(info, LOG_ERROR, "error handling timed out\n"); - - del_timer(&info->eh_timer); - - if (info->rst_bus_status == 0) - info->rst_bus_status = -1; - if (info->rst_dev_status == 0) - info->rst_dev_status = -1; - - wake_up(&info->eh_wait); -} - -enum res_find { - res_failed, /* not found */ - res_success, /* command on issue queue */ - res_hw_abort /* command on disconnected dev */ -}; - -/** - * fas216_do_abort - decide how to abort a command - * @SCpnt: command to abort - * - * Decide how to abort a command. - * Returns: abort status - */ -static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) -{ - enum res_find res = res_failed; - - if (queue_remove_cmd(&info->queues.issue, SCpnt)) { - /* - * The command was on the issue queue, and has not been - * issued yet. We can remove the command from the queue, - * and acknowledge the abort. Neither the device nor the - * interface know about the command. - */ - printk("on issue queue "); - - res = res_success; - } else if (queue_remove_cmd(&info->queues.disconnected, SCpnt)) { - /* - * The command was on the disconnected queue. We must - * reconnect with the device if possible, and send it - * an abort message. - */ - printk("on disconnected queue "); - - res = res_hw_abort; - } else if (info->SCpnt == SCpnt) { - printk("executing "); - - switch (info->scsi.phase) { - /* - * If the interface is idle, and the command is 'disconnectable', - * then it is the same as on the disconnected queue. - */ - case PHASE_IDLE: - if (info->scsi.disconnectable) { - info->scsi.disconnectable = 0; - info->SCpnt = NULL; - res = res_hw_abort; - } - break; - - default: - break; - } - } else if (info->origSCpnt == SCpnt) { - /* - * The command will be executed next, but a command - * is currently using the interface. This is similar to - * being on the issue queue, except the busylun bit has - * been set. - */ - info->origSCpnt = NULL; - clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); - printk("waiting for execution "); - res = res_success; - } else - printk("unknown "); - - return res; -} - -/** - * fas216_eh_abort - abort this command - * @SCpnt: command to abort - * - * Abort this command. - * Returns: FAILED if unable to abort - * Notes: io_request_lock is taken, and irqs are disabled - */ -int fas216_eh_abort(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - int result = FAILED; - - fas216_checkmagic(info); - - info->stats.aborts += 1; - - printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no); - print_command(SCpnt->data_cmnd); - - print_debug_list(); - fas216_dumpstate(info); - - printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt); - - switch (fas216_find_command(info, SCpnt)) { - /* - * We found the command, and cleared it out. Either - * the command is still known to be executing on the - * target, or the busylun bit is not set. - */ - case res_success: - printk("success\n"); - result = SUCCESS; - break; - - /* - * We need to reconnect to the target and send it an - * ABORT or ABORT_TAG message. We can only do this - * if the bus is free. - */ - case res_hw_abort: - - - /* - * We are unable to abort the command for some reason. - */ - default: - case res_failed: - printk("failed\n"); - break; - } - - return result; -} - -/** - * fas216_eh_device_reset - Reset the device associated with this command - * @SCpnt: command specifing device to reset - * - * Reset the device associated with this command. - * Returns: FAILED if unable to reset. - * Notes: We won't be re-entered, so we'll only have one device - * reset on the go at one time. - */ -int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - unsigned long flags; - int i, res = FAILED, target = SCpnt->device->id; - - fas216_log(info, LOG_ERROR, "device reset for target %d", target); - - spin_lock_irqsave(&info->host_lock, flags); - - do { - /* - * If we are currently connected to a device, and - * it is the device we want to reset, there is - * nothing we can do here. Chances are it is stuck, - * and we need a bus reset. - */ - if (info->SCpnt && !info->scsi.disconnectable && - info->SCpnt->device->id == SCpnt->device->id) - break; - - /* - * We're going to be resetting this device. Remove - * all pending commands from the driver. By doing - * so, we guarantee that we won't touch the command - * structures except to process the reset request. - */ - queue_remove_all_target(&info->queues.issue, target); - queue_remove_all_target(&info->queues.disconnected, target); - if (info->origSCpnt && info->origSCpnt->device->id == target) - info->origSCpnt = NULL; - if (info->reqSCpnt && info->reqSCpnt->device->id == target) - info->reqSCpnt = NULL; - for (i = 0; i < 8; i++) - clear_bit(target * 8 + i, info->busyluns); - - /* - * Hijack this SCSI command structure to send - * a bus device reset message to this device. - */ - SCpnt->host_scribble = (void *)fas216_devicereset_done; - - info->rst_dev_status = 0; - info->rstSCpnt = SCpnt; - - if (info->scsi.phase == PHASE_IDLE) - fas216_kick(info); - - mod_timer(&info->eh_timer, 30 * HZ); - spin_unlock_irqrestore(&info->host_lock, flags); - - /* - * Wait up to 30 seconds for the reset to complete. - */ - wait_event(info->eh_wait, info->rst_dev_status); - - del_timer_sync(&info->eh_timer); - spin_lock_irqsave(&info->host_lock, flags); - info->rstSCpnt = NULL; - - if (info->rst_dev_status == 1) - res = SUCCESS; - } while (0); - - SCpnt->host_scribble = NULL; - spin_unlock_irqrestore(&info->host_lock, flags); - - fas216_log(info, LOG_ERROR, "device reset complete: %s\n", - res == SUCCESS ? "success" : "failed"); - - return res; -} - -/** - * fas216_eh_bus_reset - Reset the bus associated with the command - * @SCpnt: command specifing bus to reset - * - * Reset the bus associated with the command. - * Returns: FAILED if unable to reset. - * Notes: Further commands are blocked. - */ -int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - unsigned long flags; - Scsi_Device *SDpnt; - - fas216_checkmagic(info); - fas216_log(info, LOG_ERROR, "resetting bus"); - - info->stats.bus_resets += 1; - - spin_lock_irqsave(&info->host_lock, flags); - - /* - * Stop all activity on this interface. - */ - fas216_aborttransfer(info); - fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); - - /* - * Clear any pending interrupts. - */ - while (fas216_readb(info, REG_STAT) & STAT_INT) - fas216_readb(info, REG_INST); - - info->rst_bus_status = 0; - - /* - * For each attached hard-reset device, clear out - * all command structures. Leave the running - * command in place. - */ - list_for_each_entry(SDpnt, &info->host->my_devices, siblings) { - int i; - - if (SDpnt->soft_reset) - continue; - - queue_remove_all_target(&info->queues.issue, SDpnt->id); - queue_remove_all_target(&info->queues.disconnected, SDpnt->id); - if (info->origSCpnt && info->origSCpnt->device->id == SDpnt->id) - info->origSCpnt = NULL; - if (info->reqSCpnt && info->reqSCpnt->device->id == SDpnt->id) - info->reqSCpnt = NULL; - info->SCpnt = NULL; - - for (i = 0; i < 8; i++) - clear_bit(SDpnt->id * 8 + i, info->busyluns); - } - - info->scsi.phase = PHASE_IDLE; - - /* - * Reset the SCSI bus. Device cleanup happens in - * the interrupt handler. - */ - fas216_cmd(info, CMD_RESETSCSI); - - mod_timer(&info->eh_timer, jiffies + HZ); - spin_unlock_irqrestore(&info->host_lock, flags); - - /* - * Wait one second for the interrupt. - */ - wait_event(info->eh_wait, info->rst_bus_status); - del_timer_sync(&info->eh_timer); - - fas216_log(info, LOG_ERROR, "bus reset complete: %s\n", - info->rst_bus_status == 1 ? "success" : "failed"); - - return info->rst_bus_status == 1 ? SUCCESS : FAILED; -} - -/** - * fas216_init_chip - Initialise FAS216 state after reset - * @info: state structure for interface - * - * Initialise FAS216 state after reset - */ -static void fas216_init_chip(FAS216_Info *info) -{ - unsigned int clock = ((info->ifcfg.clockrate - 1) / 5 + 1) & 7; - fas216_writeb(info, REG_CLKF, clock); - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]); - fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); - fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); - fas216_writeb(info, REG_SOF, 0); - fas216_writeb(info, REG_STP, info->scsi.async_stp); - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); -} - -/** - * fas216_eh_host_reset - Reset the host associated with this command - * @SCpnt: command specifing host to reset - * - * Reset the host associated with this command. - * Returns: FAILED if unable to reset. - * Notes: io_request_lock is taken, and irqs are disabled - */ -int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) -{ - FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; - - fas216_checkmagic(info); - - printk("scsi%d.%c: %s: resetting host\n", - info->host->host_no, '0' + SCpnt->device->id, __FUNCTION__); - - /* - * Reset the SCSI chip. - */ - fas216_cmd(info, CMD_RESETCHIP); - - /* - * Ugly ugly ugly! - * We need to release the host_lock and enable - * IRQs if we sleep, but we must relock and disable - * IRQs after the sleep. - */ - spin_unlock_irq(info->host->host_lock); - scsi_sleep(50 * HZ/100); - spin_lock_irq(info->host->host_lock); - - /* - * Release the SCSI reset. - */ - fas216_cmd(info, CMD_NOP); - - fas216_init_chip(info); - - return SUCCESS; -} - -#define TYPE_UNKNOWN 0 -#define TYPE_NCR53C90 1 -#define TYPE_NCR53C90A 2 -#define TYPE_NCR53C9x 3 -#define TYPE_Am53CF94 4 -#define TYPE_EmFAS216 5 -#define TYPE_QLFAS216 6 - -static char *chip_types[] = { - "unknown", - "NS NCR53C90", - "NS NCR53C90A", - "NS NCR53C9x", - "AMD Am53CF94", - "Emulex FAS216", - "QLogic FAS216" -}; - -static int fas216_detect_type(FAS216_Info *info) -{ - int family, rev; - - /* - * Reset the chip. - */ - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - udelay(50); - fas216_writeb(info, REG_CMD, CMD_NOP); - - /* - * Check to see if control reg 2 is present. - */ - fas216_writeb(info, REG_CNTL3, 0); - fas216_writeb(info, REG_CNTL2, CNTL2_S2FE); - - /* - * If we are unable to read back control reg 2 - * correctly, it is not present, and we have a - * NCR53C90. - */ - if ((fas216_readb(info, REG_CNTL2) & (~0xe0)) != CNTL2_S2FE) - return TYPE_NCR53C90; - - /* - * Now, check control register 3 - */ - fas216_writeb(info, REG_CNTL2, 0); - fas216_writeb(info, REG_CNTL3, 0); - fas216_writeb(info, REG_CNTL3, 5); - - /* - * If we are unable to read the register back - * correctly, we have a NCR53C90A - */ - if (fas216_readb(info, REG_CNTL3) != 5) - return TYPE_NCR53C90A; - - /* - * Now read the ID from the chip. - */ - fas216_writeb(info, REG_CNTL3, 0); - - fas216_writeb(info, REG_CNTL3, CNTL3_ADIDCHK); - fas216_writeb(info, REG_CNTL3, 0); - - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - udelay(50); - fas216_writeb(info, REG_CMD, CMD_WITHDMA | CMD_NOP); - - fas216_writeb(info, REG_CNTL2, CNTL2_ENF); - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - udelay(50); - fas216_writeb(info, REG_CMD, CMD_NOP); - - rev = fas216_readb(info, REG_ID); - family = rev >> 3; - rev &= 7; - - switch (family) { - case 0x01: - if (rev == 4) - return TYPE_Am53CF94; - break; - - case 0x02: - switch (rev) { - case 2: - return TYPE_EmFAS216; - case 3: - return TYPE_QLFAS216; - } - break; - - default: - break; - } - printk("family %x rev %x\n", family, rev); - return TYPE_NCR53C9x; -} - -/** - * fas216_reset_state - Initialise driver internal state - * @info: state to initialise - * - * Initialise driver internal state - */ -static void fas216_reset_state(FAS216_Info *info) -{ - int i; - - fas216_checkmagic(info); - - fas216_bus_reset(info); - - /* - * Clear out all stale info in our state structure - */ - memset(info->busyluns, 0, sizeof(info->busyluns)); - info->scsi.disconnectable = 0; - info->scsi.aborting = 0; - - for (i = 0; i < 8; i++) { - info->device[i].parity_enabled = 0; - info->device[i].parity_check = 1; - } - - /* - * Drain all commands on disconnected queue - */ - while (queue_remove(&info->queues.disconnected) != NULL); - - /* - * Remove executing commands. - */ - info->SCpnt = NULL; - info->reqSCpnt = NULL; - info->rstSCpnt = NULL; - info->origSCpnt = NULL; -} - -/** - * fas216_init - initialise FAS/NCR/AMD SCSI structures. - * @host: a driver-specific filled-out structure - * - * Initialise FAS/NCR/AMD SCSI structures. - * Returns: 0 on success - */ -int fas216_init(struct Scsi_Host *host) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - - info->magic_start = MAGIC; - info->magic_end = MAGIC; - info->host = host; - info->scsi.cfg[0] = host->this_id | CNTL1_PERE; - info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE; - info->scsi.cfg[2] = info->ifcfg.cntl3 | - CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM; - info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); - - info->rst_dev_status = -1; - info->rst_bus_status = -1; - init_waitqueue_head(&info->eh_wait); - init_timer(&info->eh_timer); - info->eh_timer.data = (unsigned long)info; - info->eh_timer.function = fas216_eh_timer; - - spin_lock_init(&info->host_lock); - - memset(&info->stats, 0, sizeof(info->stats)); - - msgqueue_initialise(&info->scsi.msgs); - - if (!queue_initialise(&info->queues.issue)) - return -ENOMEM; - - if (!queue_initialise(&info->queues.disconnected)) { - queue_free(&info->queues.issue); - return -ENOMEM; - } - - return 0; -} - -/** - * fas216_add - initialise FAS/NCR/AMD SCSI ic. - * @host: a driver-specific filled-out structure - * @dev: parent device - * - * Initialise FAS/NCR/AMD SCSI ic. - * Returns: 0 on success - */ -int fas216_add(struct Scsi_Host *host, struct device *dev) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - int type, ret; - - if (info->ifcfg.clockrate <= 10 || info->ifcfg.clockrate > 40) { - printk(KERN_CRIT "fas216: invalid clock rate %u MHz\n", - info->ifcfg.clockrate); - return -EINVAL; - } - - fas216_reset_state(info); - type = fas216_detect_type(info); - info->scsi.type = chip_types[type]; - - udelay(300); - - /* - * Initialise the chip correctly. - */ - fas216_init_chip(info); - - /* - * Reset the SCSI bus. We don't want to see - * the resulting reset interrupt, so mask it - * out. - */ - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_DISR); - fas216_writeb(info, REG_CMD, CMD_RESETSCSI); - - /* - * scsi standard says wait 250ms - */ - spin_unlock_irq(info->host->host_lock); - scsi_sleep(100*HZ/100); - spin_lock_irq(info->host->host_lock); - - fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); - fas216_readb(info, REG_INST); - - fas216_checkmagic(info); - - ret = scsi_add_host(host, dev); - if (ret) - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); - - return ret; -} - -void fas216_remove(struct Scsi_Host *host) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - - fas216_checkmagic(info); - scsi_remove_host(host); - - fas216_writeb(info, REG_CMD, CMD_RESETCHIP); -} - -/** - * fas216_release - release all resources for FAS/NCR/AMD SCSI ic. - * @host: a driver-specific filled-out structure - * - * release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. - */ -void fas216_release(struct Scsi_Host *host) -{ - FAS216_Info *info = (FAS216_Info *)host->hostdata; - - queue_free(&info->queues.disconnected); - queue_free(&info->queues.issue); -} - -int fas216_print_host(FAS216_Info *info, char *buffer) -{ - return sprintf(buffer, - "\n" - "Chip : %s\n" - " Address: 0x%08lx\n" - " IRQ : %d\n" - " DMA : %d\n", - info->scsi.type, info->host->io_port, - info->host->irq, info->host->dma_channel); -} - -int fas216_print_stats(FAS216_Info *info, char *buffer) -{ - char *p = buffer; - - p += sprintf(p, "\n" - "Command Statistics:\n" - " Queued : %u\n" - " Issued : %u\n" - " Completed : %u\n" - " Reads : %u\n" - " Writes : %u\n" - " Others : %u\n" - " Disconnects: %u\n" - " Aborts : %u\n" - " Bus resets : %u\n" - " Host resets: %u\n", - info->stats.queues, info->stats.removes, - info->stats.fins, info->stats.reads, - info->stats.writes, info->stats.miscs, - info->stats.disconnects, info->stats.aborts, - info->stats.bus_resets, info->stats.host_resets); - - return p - buffer; -} - -int fas216_print_devices(FAS216_Info *info, char *buffer) -{ - struct fas216_device *dev; - Scsi_Device *scd; - char *p = buffer; - - p += sprintf(p, "Device/Lun TaggedQ Parity Sync\n"); - - list_for_each_entry(scd, &info->host->my_devices, siblings) { - dev = &info->device[scd->id]; - p += sprintf(p, " %d/%d ", scd->id, scd->lun); - if (scd->tagged_supported) - p += sprintf(p, "%3sabled(%3d) ", - scd->tagged_queue ? "en" : "dis", - scd->current_tag); - else - p += sprintf(p, "unsupported "); - - p += sprintf(p, "%3sabled ", dev->parity_enabled ? "en" : "dis"); - - if (dev->sof) - p += sprintf(p, "offset %d, %d ns\n", - dev->sof, dev->period * 4); - else - p += sprintf(p, "async\n"); - } - - return p - buffer; -} - -EXPORT_SYMBOL(fas216_init); -EXPORT_SYMBOL(fas216_add); -EXPORT_SYMBOL(fas216_queue_command); -EXPORT_SYMBOL(fas216_command); -EXPORT_SYMBOL(fas216_intr); -EXPORT_SYMBOL(fas216_remove); -EXPORT_SYMBOL(fas216_release); -EXPORT_SYMBOL(fas216_eh_abort); -EXPORT_SYMBOL(fas216_eh_device_reset); -EXPORT_SYMBOL(fas216_eh_bus_reset); -EXPORT_SYMBOL(fas216_eh_host_reset); -EXPORT_SYMBOL(fas216_print_host); -EXPORT_SYMBOL(fas216_print_stats); -EXPORT_SYMBOL(fas216_print_devices); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/scsi/fas216.h b/drivers/acorn/scsi/fas216.h deleted file mode 100644 index d6b6322f9d53..000000000000 --- a/drivers/acorn/scsi/fas216.h +++ /dev/null @@ -1,394 +0,0 @@ -/* - * linux/drivers/acorn/scsi/fas216.h - * - * Copyright (C) 1997-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * FAS216 generic driver - */ -#ifndef FAS216_H -#define FAS216_H - -#ifndef NO_IRQ -#define NO_IRQ 255 -#endif - -#include "queue.h" -#include "msgqueue.h" - -/* FAS register definitions */ - -/* transfer count low */ -#define REG_CTCL (0) -#define REG_STCL (0) - -/* transfer count medium */ -#define REG_CTCM (1) -#define REG_STCM (1) - -/* fifo data */ -#define REG_FF (2) - -/* command */ -#define REG_CMD (3) -#define CMD_NOP 0x00 -#define CMD_FLUSHFIFO 0x01 -#define CMD_RESETCHIP 0x02 -#define CMD_RESETSCSI 0x03 - -#define CMD_TRANSFERINFO 0x10 -#define CMD_INITCMDCOMPLETE 0x11 -#define CMD_MSGACCEPTED 0x12 -#define CMD_PADBYTES 0x18 -#define CMD_SETATN 0x1a -#define CMD_RSETATN 0x1b - -#define CMD_SELECTWOATN 0x41 -#define CMD_SELECTATN 0x42 -#define CMD_SELECTATNSTOP 0x43 -#define CMD_ENABLESEL 0x44 -#define CMD_DISABLESEL 0x45 -#define CMD_SELECTATN3 0x46 -#define CMD_RESEL3 0x47 - -#define CMD_WITHDMA 0x80 - -/* status register (read) */ -#define REG_STAT (4) -#define STAT_IO (1 << 0) /* IO phase */ -#define STAT_CD (1 << 1) /* CD phase */ -#define STAT_MSG (1 << 2) /* MSG phase */ -#define STAT_TRANSFERDONE (1 << 3) /* Transfer completed */ -#define STAT_TRANSFERCNTZ (1 << 4) /* Transfer counter is zero */ -#define STAT_PARITYERROR (1 << 5) /* Parity error */ -#define STAT_REALBAD (1 << 6) /* Something bad */ -#define STAT_INT (1 << 7) /* Interrupt */ - -#define STAT_BUSMASK (STAT_MSG|STAT_CD|STAT_IO) -#define STAT_DATAOUT (0) /* Data out */ -#define STAT_DATAIN (STAT_IO) /* Data in */ -#define STAT_COMMAND (STAT_CD) /* Command out */ -#define STAT_STATUS (STAT_CD|STAT_IO) /* Status In */ -#define STAT_MESGOUT (STAT_MSG|STAT_CD) /* Message out */ -#define STAT_MESGIN (STAT_MSG|STAT_CD|STAT_IO) /* Message In */ - -/* bus ID for select / reselect */ -#define REG_SDID (4) -#define BUSID(target) ((target) & 7) - -/* Interrupt status register (read) */ -#define REG_INST (5) -#define INST_SELWOATN (1 << 0) /* Select w/o ATN */ -#define INST_SELATN (1 << 1) /* Select w/ATN */ -#define INST_RESELECTED (1 << 2) /* Reselected */ -#define INST_FUNCDONE (1 << 3) /* Function done */ -#define INST_BUSSERVICE (1 << 4) /* Bus service */ -#define INST_DISCONNECT (1 << 5) /* Disconnect */ -#define INST_ILLEGALCMD (1 << 6) /* Illegal command */ -#define INST_BUSRESET (1 << 7) /* SCSI Bus reset */ - -/* Timeout register (write) */ -#define REG_STIM (5) - -/* Sequence step register (read) */ -#define REG_IS (6) -#define IS_BITS 0x07 -#define IS_SELARB 0x00 /* Select & Arb ok */ -#define IS_MSGBYTESENT 0x01 /* One byte message sent*/ -#define IS_NOTCOMMAND 0x02 /* Not in command state */ -#define IS_EARLYPHASE 0x03 /* Early phase change */ -#define IS_COMPLETE 0x04 /* Command ok */ -#define IS_SOF 0x08 /* Sync off flag */ - -/* Transfer period step (write) */ -#define REG_STP (6) - -/* Synchronous Offset (write) */ -#define REG_SOF (7) - -/* Fifo state register (read) */ -#define REG_CFIS (7) -#define CFIS_CF 0x1f /* Num bytes in FIFO */ -#define CFIS_IS 0xe0 /* Step */ - -/* config register 1 */ -#define REG_CNTL1 (8) -#define CNTL1_CID (7 << 0) /* Chip ID */ -#define CNTL1_STE (1 << 3) /* Self test enable */ -#define CNTL1_PERE (1 << 4) /* Parity enable reporting en. */ -#define CNTL1_PTE (1 << 5) /* Parity test enable */ -#define CNTL1_DISR (1 << 6) /* Disable Irq on SCSI reset */ -#define CNTL1_ETM (1 << 7) /* Extended Timing Mode */ - -/* Clock conversion factor (read) */ -#define REG_CLKF (9) -#define CLKF_F37MHZ 0x00 /* 35.01 - 40 MHz */ -#define CLKF_F10MHZ 0x02 /* 10 MHz */ -#define CLKF_F12MHZ 0x03 /* 10.01 - 15 MHz */ -#define CLKF_F17MHZ 0x04 /* 15.01 - 20 MHz */ -#define CLKF_F22MHZ 0x05 /* 20.01 - 25 MHz */ -#define CLKF_F27MHZ 0x06 /* 25.01 - 30 MHz */ -#define CLKF_F32MHZ 0x07 /* 30.01 - 35 MHz */ - -/* Chip test register (write) */ -#define REG_FTM (10) -#define TEST_FTM 0x01 /* Force target mode */ -#define TEST_FIM 0x02 /* Force initiator mode */ -#define TEST_FHI 0x04 /* Force high impedance mode */ - -/* Configuration register 2 (read/write) */ -#define REG_CNTL2 (11) -#define CNTL2_PGDP (1 << 0) /* Pass Th/Generate Data Parity */ -#define CNTL2_PGRP (1 << 1) /* Pass Th/Generate Reg Parity */ -#define CNTL2_ACDPE (1 << 2) /* Abort on Cmd/Data Parity Err */ -#define CNTL2_S2FE (1 << 3) /* SCSI2 Features Enable */ -#define CNTL2_TSDR (1 << 4) /* Tristate DREQ */ -#define CNTL2_SBO (1 << 5) /* Select Byte Order */ -#define CNTL2_ENF (1 << 6) /* Enable features */ -#define CNTL2_DAE (1 << 7) /* Data Alignment Enable */ - -/* Configuration register 3 (read/write) */ -#define REG_CNTL3 (12) -#define CNTL3_BS8 (1 << 0) /* Burst size 8 */ -#define CNTL3_MDM (1 << 1) /* Modify DMA mode */ -#define CNTL3_LBTM (1 << 2) /* Last Byte Transfer mode */ -#define CNTL3_FASTCLK (1 << 3) /* Fast SCSI clocking */ -#define CNTL3_FASTSCSI (1 << 4) /* Fast SCSI */ -#define CNTL3_G2CB (1 << 5) /* Group2 SCSI support */ -#define CNTL3_QTAG (1 << 6) /* Enable 3 byte msgs */ -#define CNTL3_ADIDCHK (1 << 7) /* Additional ID check */ - -/* High transfer count (read/write) */ -#define REG_CTCH (14) -#define REG_STCH (14) - -/* ID register (read only) */ -#define REG_ID (14) - -/* Data alignment */ -#define REG_DAL (15) - -typedef enum { - PHASE_IDLE, /* we're not planning on doing anything */ - PHASE_SELECTION, /* selecting a device */ - PHASE_SELSTEPS, /* selection with command steps */ - PHASE_COMMAND, /* command sent */ - PHASE_MESSAGESENT, /* selected, and we're sending cmd */ - PHASE_DATAOUT, /* data out to device */ - PHASE_DATAIN, /* data in from device */ - PHASE_MSGIN, /* message in from device */ - PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */ - PHASE_MSGOUT, /* after message out phase */ - PHASE_MSGOUT_EXPECT, /* expecting message out */ - PHASE_STATUS, /* status from device */ - PHASE_DONE /* Command complete */ -} phase_t; - -typedef enum { - DMA_OUT, /* DMA from memory to chip */ - DMA_IN /* DMA from chip to memory */ -} fasdmadir_t; - -typedef enum { - fasdma_none, /* No dma */ - fasdma_pio, /* PIO mode */ - fasdma_pseudo, /* Pseudo DMA */ - fasdma_real_block, /* Real DMA, on block by block basis */ - fasdma_real_all /* Real DMA, on request by request */ -} fasdmatype_t; - -typedef enum { - neg_wait, /* Negociate with device */ - neg_inprogress, /* Negociation sent */ - neg_complete, /* Negociation complete */ - neg_targcomplete, /* Target completed negociation */ - neg_invalid /* Negociation not supported */ -} neg_t; - -#define MAGIC 0x441296bdUL -#define NR_MSGS 8 - -#define FASCAP_DMA (1 << 0) -#define FASCAP_PSEUDODMA (1 << 1) - -typedef struct { - unsigned long magic_start; - spinlock_t host_lock; - struct Scsi_Host *host; /* host */ - Scsi_Cmnd *SCpnt; /* currently processing command */ - Scsi_Cmnd *origSCpnt; /* original connecting command */ - Scsi_Cmnd *reqSCpnt; /* request sense command */ - Scsi_Cmnd *rstSCpnt; /* reset command */ - Scsi_Cmnd *pending_SCpnt[8]; /* per-device pending commands */ - int next_pending; /* next pending device */ - - /* - * Error recovery - */ - wait_queue_head_t eh_wait; - struct timer_list eh_timer; - unsigned int rst_dev_status; - unsigned int rst_bus_status; - - /* driver information */ - struct { - phase_t phase; /* current phase */ - void *io_base; /* iomem base of FAS216 */ - unsigned int io_port; /* base address of FAS216 */ - unsigned int io_shift; /* shift to adjust reg offsets by */ - unsigned char cfg[4]; /* configuration registers */ - const char *type; /* chip type */ - unsigned int irq; /* interrupt */ - - Scsi_Pointer SCp; /* current commands data pointer */ - - MsgQueue_t msgs; /* message queue for connected device */ - - unsigned int async_stp; /* Async transfer STP value */ - unsigned char msgin_fifo; /* bytes in fifo at time of message in */ - unsigned char message[256]; /* last message received from device */ - - unsigned char disconnectable:1; /* this command can be disconnected */ - unsigned char aborting:1; /* aborting command */ - } scsi; - - /* statistics information */ - struct { - unsigned int queues; - unsigned int removes; - unsigned int fins; - unsigned int reads; - unsigned int writes; - unsigned int miscs; - unsigned int disconnects; - unsigned int aborts; - unsigned int bus_resets; - unsigned int host_resets; - } stats; - - /* configuration information */ - struct { - unsigned char clockrate; /* clock rate of FAS device (MHz) */ - unsigned char select_timeout; /* timeout (R5) */ - unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */ - unsigned char wide_max_size; /* Maximum wide transfer size */ - unsigned char cntl3; /* Control Reg 3 */ - unsigned int asyncperiod; /* Async transfer period (ns) */ - unsigned int capabilities; /* driver capabilities */ - unsigned int disconnect_ok:1; /* Disconnects allowed? */ - } ifcfg; - - /* queue handling */ - struct { - Queue_t issue; /* issue queue */ - Queue_t disconnected; /* disconnected command queue */ - } queues; - - /* per-device info */ - struct fas216_device { - unsigned char disconnect_ok:1; /* device can disconnect */ - unsigned char parity_enabled:1; /* parity checking enabled */ - unsigned char parity_check:1; /* need to check parity checking */ - unsigned char period; /* sync xfer period in (*4ns) */ - unsigned char stp; /* synchronous transfer period */ - unsigned char sof; /* synchronous offset register */ - unsigned char wide_xfer; /* currently negociated wide transfer */ - neg_t sync_state; /* synchronous transfer mode */ - neg_t wide_state; /* wide transfer mode */ - } device[8]; - unsigned long busyluns[64/sizeof(unsigned long)];/* array of bits indicating LUNs busy */ - - /* dma */ - struct { - fasdmatype_t transfer_type; /* current type of DMA transfer */ - fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma); - void (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer); - void (*stop) (struct Scsi_Host *host, Scsi_Pointer *SCp); - } dma; - - /* miscellaneous */ - int internal_done; /* flag to indicate request done */ - - unsigned long magic_end; -} FAS216_Info; - -/* Function: int fas216_init (struct Scsi_Host *instance) - * Purpose : initialise FAS/NCR/AMD SCSI structures. - * Params : instance - a driver-specific filled-out structure - * Returns : 0 on success - */ -extern int fas216_init (struct Scsi_Host *instance); - -/* Function: int fas216_add (struct Scsi_Host *instance, struct device *dev) - * Purpose : initialise FAS/NCR/AMD SCSI ic. - * Params : instance - a driver-specific filled-out structure - * Returns : 0 on success - */ -extern int fas216_add (struct Scsi_Host *instance, struct device *dev); - -/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) - * Purpose : queue a command for adapter to process. - * Params : SCpnt - Command to queue - * done - done function to call once command is complete - * Returns : 0 - success, else error - */ -extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); - -/* Function: int fas216_command (Scsi_Cmnd *SCpnt) - * Purpose : queue a command for adapter to process. - * Params : SCpnt - Command to queue - * Returns : scsi result code - */ -extern int fas216_command (Scsi_Cmnd *); - -/* Function: irqreturn_t fas216_intr (FAS216_Info *info) - * Purpose : handle interrupts from the interface to progress a command - * Params : info - interface to service - */ -extern irqreturn_t fas216_intr (FAS216_Info *info); - -extern void fas216_remove (struct Scsi_Host *instance); - -/* Function: void fas216_release (struct Scsi_Host *instance) - * Purpose : release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. - * Params : instance - a driver-specific filled-out structure - * Returns : 0 on success - */ -extern void fas216_release (struct Scsi_Host *instance); - -extern int fas216_print_host(FAS216_Info *info, char *buffer); -extern int fas216_print_stats(FAS216_Info *info, char *buffer); -extern int fas216_print_devices(FAS216_Info *info, char *buffer); - -/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) - * Purpose : abort this command - * Params : SCpnt - command to abort - * Returns : FAILED if unable to abort - */ -extern int fas216_eh_abort(Scsi_Cmnd *SCpnt); - -/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the device associated with this command - * Params : SCpnt - command specifing device to reset - * Returns : FAILED if unable to reset - */ -extern int fas216_eh_device_reset(Scsi_Cmnd *SCpnt); - -/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the complete bus associated with this command - * Params : SCpnt - command specifing bus to reset - * Returns : FAILED if unable to reset - */ -extern int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt); - -/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) - * Purpose : Reset the host associated with this command - * Params : SCpnt - command specifing host to reset - * Returns : FAILED if unable to reset - */ -extern int fas216_eh_host_reset(Scsi_Cmnd *SCpnt); - -#endif /* FAS216_H */ diff --git a/drivers/acorn/scsi/msgqueue.c b/drivers/acorn/scsi/msgqueue.c deleted file mode 100644 index 7c95c7582b29..000000000000 --- a/drivers/acorn/scsi/msgqueue.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * linux/drivers/acorn/scsi/msgqueue.c - * - * Copyright (C) 1997-1998 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * message queue handling - */ -#include -#include -#include -#include - -#include "msgqueue.h" - -/* - * Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) - * Purpose : Allocate a message queue entry - * Params : msgq - message queue to claim entry for - * Returns : message queue entry or NULL. - */ -static struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) -{ - struct msgqueue_entry *mq; - - if ((mq = msgq->free) != NULL) - msgq->free = mq->next; - - return mq; -} - -/* - * Function: void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) - * Purpose : free a message queue entry - * Params : msgq - message queue to free entry from - * mq - message queue entry to free - */ -static void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) -{ - if (mq) { - mq->next = msgq->free; - msgq->free = mq; - } -} - -/* - * Function: void msgqueue_initialise(MsgQueue_t *msgq) - * Purpose : initialise a message queue - * Params : msgq - queue to initialise - */ -void msgqueue_initialise(MsgQueue_t *msgq) -{ - int i; - - msgq->qe = NULL; - msgq->free = &msgq->entries[0]; - - for (i = 0; i < NR_MESSAGES; i++) - msgq->entries[i].next = &msgq->entries[i + 1]; - - msgq->entries[NR_MESSAGES - 1].next = NULL; -} - - -/* - * Function: void msgqueue_free(MsgQueue_t *msgq) - * Purpose : free a queue - * Params : msgq - queue to free - */ -void msgqueue_free(MsgQueue_t *msgq) -{ -} - -/* - * Function: int msgqueue_msglength(MsgQueue_t *msgq) - * Purpose : calculate the total length of all messages on the message queue - * Params : msgq - queue to examine - * Returns : number of bytes of messages in queue - */ -int msgqueue_msglength(MsgQueue_t *msgq) -{ - struct msgqueue_entry *mq = msgq->qe; - int length = 0; - - for (mq = msgq->qe; mq; mq = mq->next) - length += mq->msg.length; - - return length; -} - -/* - * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) - * Purpose : return a message - * Params : msgq - queue to obtain message from - * : msgno - message number - * Returns : pointer to message string, or NULL - */ -struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) -{ - struct msgqueue_entry *mq; - - for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--); - - return mq ? &mq->msg : NULL; -} - -/* - * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) - * Purpose : add a message onto a message queue - * Params : msgq - queue to add message on - * length - length of message - * ... - message bytes - * Returns : != 0 if successful - */ -int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) -{ - struct msgqueue_entry *mq = mqe_alloc(msgq); - va_list ap; - - if (mq) { - struct msgqueue_entry **mqp; - int i; - - va_start(ap, length); - for (i = 0; i < length; i++) - mq->msg.msg[i] = va_arg(ap, unsigned int); - va_end(ap); - - mq->msg.length = length; - mq->msg.fifo = 0; - mq->next = NULL; - - mqp = &msgq->qe; - while (*mqp) - mqp = &(*mqp)->next; - - *mqp = mq; - } - - return mq != NULL; -} - -/* - * Function: void msgqueue_flush(MsgQueue_t *msgq) - * Purpose : flush all messages from message queue - * Params : msgq - queue to flush - */ -void msgqueue_flush(MsgQueue_t *msgq) -{ - struct msgqueue_entry *mq, *mqnext; - - for (mq = msgq->qe; mq; mq = mqnext) { - mqnext = mq->next; - mqe_free(msgq, mq); - } - msgq->qe = NULL; -} - -EXPORT_SYMBOL(msgqueue_initialise); -EXPORT_SYMBOL(msgqueue_free); -EXPORT_SYMBOL(msgqueue_msglength); -EXPORT_SYMBOL(msgqueue_getmsg); -EXPORT_SYMBOL(msgqueue_addmsg); -EXPORT_SYMBOL(msgqueue_flush); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("SCSI message queue handling"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/scsi/msgqueue.h b/drivers/acorn/scsi/msgqueue.h deleted file mode 100644 index 41c7333df3e3..000000000000 --- a/drivers/acorn/scsi/msgqueue.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * linux/drivers/acorn/scsi/msgqueue.h - * - * Copyright (C) 1997 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * message queue handling - */ -#ifndef MSGQUEUE_H -#define MSGQUEUE_H - -struct message { - char msg[8]; - int length; - int fifo; -}; - -struct msgqueue_entry { - struct message msg; - struct msgqueue_entry *next; -}; - -#define NR_MESSAGES 4 - -typedef struct { - struct msgqueue_entry *qe; - struct msgqueue_entry *free; - struct msgqueue_entry entries[NR_MESSAGES]; -} MsgQueue_t; - -/* - * Function: void msgqueue_initialise(MsgQueue_t *msgq) - * Purpose : initialise a message queue - * Params : msgq - queue to initialise - */ -extern void msgqueue_initialise(MsgQueue_t *msgq); - -/* - * Function: void msgqueue_free(MsgQueue_t *msgq) - * Purpose : free a queue - * Params : msgq - queue to free - */ -extern void msgqueue_free(MsgQueue_t *msgq); - -/* - * Function: int msgqueue_msglength(MsgQueue_t *msgq) - * Purpose : calculate the total length of all messages on the message queue - * Params : msgq - queue to examine - * Returns : number of bytes of messages in queue - */ -extern int msgqueue_msglength(MsgQueue_t *msgq); - -/* - * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) - * Purpose : return a message & its length - * Params : msgq - queue to obtain message from - * : msgno - message number - * Returns : pointer to message string, or NULL - */ -extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno); - -/* - * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) - * Purpose : add a message onto a message queue - * Params : msgq - queue to add message on - * length - length of message - * ... - message bytes - * Returns : != 0 if successful - */ -extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...); - -/* - * Function: void msgqueue_flush(MsgQueue_t *msgq) - * Purpose : flush all messages from message queue - * Params : msgq - queue to flush - */ -extern void msgqueue_flush(MsgQueue_t *msgq); - -#endif diff --git a/drivers/acorn/scsi/oak.c b/drivers/acorn/scsi/oak.c deleted file mode 100644 index 6afc155ea856..000000000000 --- a/drivers/acorn/scsi/oak.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Oak Generic NCR5380 driver - * - * Copyright 1995-2002, Russell King - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" - -#define AUTOSENSE -/*#define PSEUDO_DMA*/ - -#define OAKSCSI_PUBLIC_RELEASE 1 - -#define NCR5380_read(reg) oakscsi_read(_instance, reg) -#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value) -#define NCR5380_intr oakscsi_intr -#define NCR5380_queue_command oakscsi_queue_command -#define NCR5380_proc_info oakscsi_proc_info - -int NCR5380_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -#define NCR5380_implementation_fields int port, ctrl -#define NCR5380_local_declare() struct Scsi_Host *_instance -#define NCR5380_setup(instance) _instance = instance - -#define BOARD_NORMAL 0 -#define BOARD_NCR53C400 1 - -#include "../../scsi/NCR5380.h" - -#undef START_DMA_INITIATOR_RECEIVE_REG -#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128) - -const char * oakscsi_info (struct Scsi_Host *spnt) -{ - return ""; -} - -#define STAT(p) inw(p + 144) -extern void inswb(int from, void *to, int len); - -static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, - int len) -{ - int iobase = instance->io_port; -printk("writing %p len %d\n",addr, len); - if(!len) return -1; - - while(1) - { - int status; - while(((status = STAT(iobase)) & 0x100)==0); - } -} - -static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, - int len) -{ - int iobase = instance->io_port; -printk("reading %p len %d\n", addr, len); - while(len > 0) - { - int status, timeout; - unsigned long b; - - timeout = 0x01FFFFFF; - - while(((status = STAT(iobase)) & 0x100)==0) - { - timeout--; - if(status & 0x200 || !timeout) - { - printk("status = %08X\n",status); - return 1; - } - } - if(len >= 128) - { - inswb(iobase + 136, addr, 128); - addr += 128; - len -= 128; - } - else - { - b = (unsigned long) inw(iobase + 136); - *addr ++ = b; - len -= 1; - if(len) - *addr ++ = b>>8; - len -= 1; - } - } - return 0; -} - -#define oakscsi_read(instance,reg) (inb((instance)->io_port + (reg))) -#define oakscsi_write(instance,reg,val) (outb((val), (instance)->io_port + (reg))) - -#undef STAT - -#include "../../scsi/NCR5380.c" - -static Scsi_Host_Template oakscsi_template = { - .module = THIS_MODULE, - .proc_info = oakscsi_proc_info, - .name = "Oak 16-bit SCSI", - .info = oakscsi_info, - .queuecommand = oakscsi_queue_command, - .eh_abort_handler = NCR5380_abort, - .eh_device_reset_handler= NCR5380_device_reset, - .eh_bus_reset_handler = NCR5380_bus_reset, - .eh_host_reset_handler = NCR5380_host_reset, - .can_queue = 16, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = DISABLE_CLUSTERING, - .proc_name = "oakscsi", -}; - -static int __devinit -oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - int ret = -ENOMEM; - - host = scsi_register(&oakscsi_template, sizeof(struct NCR5380_hostdata)); - if (!host) - goto out; - - host->io_port = ecard_address(ec, ECARD_MEMC, 0); - host->irq = IRQ_NONE; - host->n_io_port = 255; - - ret = -EBUSY; - if (!request_region (host->io_port, host->n_io_port, "Oak SCSI")) - goto unreg; - - NCR5380_init(host, 0); - - printk("scsi%d: at port 0x%08lx irqs disabled", - host->host_no, host->io_port); - printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", - host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE); - printk("\nscsi%d:", host->host_no); - NCR5380_print_options(host); - printk("\n"); - - ret = scsi_add_host(host, &ec->dev); - if (ret == 0) - goto out; - - release_region(host->io_port, host->n_io_port); - unreg: - scsi_unregister(host); - out: - return ret; -} - -static void __devexit oakscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - - ecard_set_drvdata(ec, NULL); - scsi_remove_host(host); - - release_region(host->io_port, host->n_io_port); - scsi_unregister(host); -} - -static const struct ecard_id oakscsi_cids[] = { - { MANU_OAK, PROD_OAK_SCSI }, - { 0xffff, 0xffff } -}; - -static struct ecard_driver oakscsi_driver = { - .probe = oakscsi_probe, - .remove = __devexit_p(oakscsi_remove), - .id_table = oakscsi_cids, - .drv = { - .name = "oakscsi", - }, -}; - -static int __init oakscsi_init(void) -{ - return ecard_register_driver(&oakscsi_driver); -} - -static void __exit oakscsi_exit(void) -{ - ecard_remove_driver(&oakscsi_driver); -} - -module_init(oakscsi_init); -module_exit(oakscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Oak SCSI driver"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/acorn/scsi/powertec.c b/drivers/acorn/scsi/powertec.c deleted file mode 100644 index 10c342dae444..000000000000 --- a/drivers/acorn/scsi/powertec.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * linux/drivers/acorn/scsi/powertec.c - * - * Copyright (C) 1997-2003 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" -#include "../../scsi/hosts.h" -#include "fas216.h" -#include "scsi.h" - -#include - -#define POWERTEC_FAS216_OFFSET 0x3000 -#define POWERTEC_FAS216_SHIFT 6 - -#define POWERTEC_INTR_STATUS 0x2000 -#define POWERTEC_INTR_BIT 0x80 - -#define POWERTEC_RESET_CONTROL 0x1018 -#define POWERTEC_RESET_BIT 1 - -#define POWERTEC_TERM_CONTROL 0x2018 -#define POWERTEC_TERM_ENABLE 1 - -#define POWERTEC_INTR_CONTROL 0x101c -#define POWERTEC_INTR_ENABLE 1 -#define POWERTEC_INTR_DISABLE 0 - -#define VERSION "1.10 (19/01/2003 2.5.59)" - -/* - * Use term=0,1,0,0,0 to turn terminators on/off. - * One entry per slot. - */ -static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; - -#define NR_SG 256 - -struct powertec_info { - FAS216_Info info; - struct expansion_card *ec; - void *term_port; - unsigned int term_ctl; - struct scatterlist sg[NR_SG]; -}; - -/* Prototype: void powertecscsi_irqenable(ec, irqnr) - * Purpose : Enable interrupts on Powertec SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -powertecscsi_irqenable(struct expansion_card *ec, int irqnr) -{ - writeb(POWERTEC_INTR_ENABLE, ec->irq_data); -} - -/* Prototype: void powertecscsi_irqdisable(ec, irqnr) - * Purpose : Disable interrupts on Powertec SCSI card - * Params : ec - expansion card structure - * : irqnr - interrupt number - */ -static void -powertecscsi_irqdisable(struct expansion_card *ec, int irqnr) -{ - writeb(POWERTEC_INTR_DISABLE, ec->irq_data); -} - -static const expansioncard_ops_t powertecscsi_ops = { - .irqenable = powertecscsi_irqenable, - .irqdisable = powertecscsi_irqdisable, -}; - -/* Prototype: void powertecscsi_terminator_ctl(host, on_off) - * Purpose : Turn the Powertec SCSI terminators on or off - * Params : host - card to turn on/off - * : on_off - !0 to turn on, 0 to turn off - */ -static void -powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off) -{ - struct powertec_info *info = (struct powertec_info *)host->hostdata; - - info->term_ctl = on_off ? POWERTEC_TERM_ENABLE : 0; - writeb(info->term_ctl, info->term_port); -} - -/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs) - * Purpose : handle interrupts from Powertec SCSI card - * Params : irq - interrupt number - * dev_id - user-defined (Scsi_Host structure) - * regs - processor registers at interrupt - */ -static irqreturn_t -powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs) -{ - struct powertec_info *info = dev_id; - - return fas216_intr(&info->info); -} - -/* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type) - * Purpose : initialises DMA/PIO - * Params : host - host - * SCpnt - command - * direction - DMA on to/off of card - * min_type - minimum DMA support that we must have for this transfer - * Returns : type of transfer to be performed - */ -static fasdmatype_t -powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, - fasdmadir_t direction, fasdmatype_t min_type) -{ - struct powertec_info *info = (struct powertec_info *)host->hostdata; - struct device *dev = scsi_get_device(host); - int dmach = host->dma_channel; - - if (info->info.ifcfg.capabilities & FASCAP_DMA && - min_type == fasdma_real_all) { - int bufs, map_dir, dma_dir; - - bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); - - if (direction == DMA_OUT) - map_dir = DMA_TO_DEVICE, - dma_dir = DMA_MODE_WRITE; - else - map_dir = DMA_FROM_DEVICE, - dma_dir = DMA_MODE_READ; - - dma_map_sg(dev, info->sg, bufs + 1, map_dir); - - disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); - set_dma_mode(dmach, dma_dir); - enable_dma(dmach); - return fasdma_real_all; - } - - /* - * If we're not doing DMA, - * we'll do slow PIO - */ - return fasdma_pio; -} - -/* Prototype: int powertecscsi_dma_stop(host, SCpnt) - * Purpose : stops DMA/PIO - * Params : host - host - * SCpnt - command - */ -static void -powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) -{ - if (host->dma_channel != NO_DMA) - disable_dma(host->dma_channel); -} - -/* Prototype: const char *powertecscsi_info(struct Scsi_Host * host) - * Purpose : returns a descriptive string about this interface, - * Params : host - driver host structure to return info for. - * Returns : pointer to a static buffer containing null terminated string. - */ -const char *powertecscsi_info(struct Scsi_Host *host) -{ - struct powertec_info *info = (struct powertec_info *)host->hostdata; - static char string[150]; - - sprintf(string, "%s (%s) in slot %d v%s terminators o%s", - host->hostt->name, info->info.scsi.type, info->ec->slot_no, - VERSION, info->term_ctl ? "n" : "ff"); - - return string; -} - -/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) - * Purpose : Set a driver specific function - * Params : host - host to setup - * : buffer - buffer containing string describing operation - * : length - length of string - * Returns : -EINVAL, or 0 - */ -static int -powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) -{ - int ret = length; - - if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) { - buffer += 12; - length -= 12; - - if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { - if (buffer[5] == '1') - powertecscsi_terminator_ctl(host, 1); - else if (buffer[5] == '0') - powertecscsi_terminator_ctl(host, 0); - else - ret = -EINVAL; - } else - ret = -EINVAL; - } else - ret = -EINVAL; - - return ret; -} - -/* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset, - * int length, int host_no, int inout) - * Purpose : Return information about the driver to a user process accessing - * the /proc filesystem. - * Params : buffer - a buffer to write information to - * start - a pointer into this buffer set by this routine to the start - * of the required information. - * offset - offset into information that we have read upto. - * length - length of buffer - * host_no - host number to return information for - * inout - 0 for reading, 1 for writing. - * Returns : length of data written to buffer. - */ -int powertecscsi_proc_info(char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - struct Scsi_Host *host; - struct powertec_info *info; - char *p = buffer; - int pos; - - host = scsi_host_hn_get(host_no); - if (!host) - return 0; - - if (inout == 1) - return powertecscsi_set_proc_info(host, buffer, length); - - info = (struct powertec_info *)host->hostdata; - - p += sprintf(p, "PowerTec SCSI driver v%s\n", VERSION); - p += fas216_print_host(&info->info, p); - p += sprintf(p, "Term : o%s\n", - info->term_ctl ? "n" : "ff"); - - p += fas216_print_stats(&info->info, p); - p += fas216_print_devices(&info->info, p); - - *start = buffer + offset; - pos = p - buffer - offset; - if (pos > length) - pos = length; - - return pos; -} - -static ssize_t powertecscsi_show_term(struct device *dev, char *buf) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct powertec_info *info = (struct powertec_info *)host->hostdata; - - return sprintf(buf, "%d\n", info->term_ctl ? 1 : 0); -} - -static ssize_t -powertecscsi_store_term(struct device *dev, const char *buf, size_t len) -{ - struct expansion_card *ec = ECARD_DEV(dev); - struct Scsi_Host *host = ecard_get_drvdata(ec); - - if (len > 1) - powertecscsi_terminator_ctl(host, buf[0] != '0'); - - return len; -} - -static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, - powertecscsi_show_term, powertecscsi_store_term); - -static Scsi_Host_Template powertecscsi_template = { - .module = THIS_MODULE, - .proc_info = powertecscsi_proc_info, - .name = "PowerTec SCSI", - .info = powertecscsi_info, - .command = fas216_command, - .queuecommand = fas216_queue_command, - .eh_host_reset_handler = fas216_eh_host_reset, - .eh_bus_reset_handler = fas216_eh_bus_reset, - .eh_device_reset_handler = fas216_eh_device_reset, - .eh_abort_handler = fas216_eh_abort, - - .can_queue = 8, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 2, - .use_clustering = ENABLE_CLUSTERING, - .proc_name = "powertec", -}; - -static int __devinit -powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id) -{ - struct Scsi_Host *host; - struct powertec_info *info; - unsigned long resbase, reslen; - unsigned char *base; - int ret; - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - if (!request_mem_region(resbase, reslen, "powertecscsi")) { - ret = -EBUSY; - goto out; - } - - base = ioremap(resbase, reslen); - if (!base) { - ret = -ENOMEM; - goto out_region; - } - - host = scsi_register(&powertecscsi_template, - sizeof (struct powertec_info)); - if (!host) { - ret = -ENOMEM; - goto out_unmap; - } - - host->base = (unsigned long)base; - host->irq = ec->irq; - host->dma_channel = ec->dma; - - ec->irqaddr = base + POWERTEC_INTR_STATUS; - ec->irqmask = POWERTEC_INTR_BIT; - ec->irq_data = base + POWERTEC_INTR_CONTROL; - ec->ops = &powertecscsi_ops; - - ecard_set_drvdata(ec, host); - - info = (struct powertec_info *)host->hostdata; - info->term_port = base + POWERTEC_TERM_CONTROL; - powertecscsi_terminator_ctl(host, term[ec->slot_no]); - - device_create_file(&ec->dev, &dev_attr_bus_term); - - info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET; - info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT; - info->info.scsi.irq = host->irq; - info->info.ifcfg.clockrate = 40; /* MHz */ - info->info.ifcfg.select_timeout = 255; - info->info.ifcfg.asyncperiod = 200; /* ns */ - info->info.ifcfg.sync_max_depth = 7; - info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; - info->info.ifcfg.disconnect_ok = 1; - info->info.ifcfg.wide_max_size = 0; - info->info.ifcfg.capabilities = 0; - info->info.dma.setup = powertecscsi_dma_setup; - info->info.dma.pseudo = NULL; - info->info.dma.stop = powertecscsi_dma_stop; - - ret = fas216_init(host); - if (ret) - goto out_free; - - ret = request_irq(host->irq, powertecscsi_intr, - SA_INTERRUPT, "powertec", info); - if (ret) { - printk("scsi%d: IRQ%d not free: %d\n", - host->host_no, host->irq, ret); - goto out_release; - } - - if (host->dma_channel != NO_DMA) { - if (request_dma(host->dma_channel, "powertec")) { - printk("scsi%d: DMA%d not free, using PIO\n", - host->host_no, host->dma_channel); - host->dma_channel = NO_DMA; - } else { - set_dma_speed(host->dma_channel, 180); - info->info.ifcfg.capabilities |= FASCAP_DMA; - } - } - - ret = fas216_add(host, &ec->dev); - if (ret == 0) - goto out; - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, host); - - out_release: - fas216_release(host); - - out_free: - device_remove_file(&ec->dev, &dev_attr_bus_term); - scsi_unregister(host); - - out_unmap: - iounmap(base); - - out_region: - release_mem_region(resbase, reslen); - - out: - return ret; -} - -static void __devexit powertecscsi_remove(struct expansion_card *ec) -{ - struct Scsi_Host *host = ecard_get_drvdata(ec); - struct powertecscsi_info *info = (struct powertecscsi_info *)host->hostdata; - unsigned long resbase, reslen; - - ecard_set_drvdata(ec, NULL); - fas216_remove(host); - - device_remove_file(&ec->dev, &dev_attr_bus_term); - - if (host->dma_channel != NO_DMA) - free_dma(host->dma_channel); - free_irq(host->irq, info); - - iounmap((void *)host->base); - - resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); - reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); - - release_mem_region(resbase, reslen); - - fas216_release(host); - scsi_unregister(host); -} - -static const struct ecard_id powertecscsi_cids[] = { - { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI }, - { 0xffff, 0xffff }, -}; - -static struct ecard_driver powertecscsi_driver = { - .probe = powertecscsi_probe, - .remove = __devexit_p(powertecscsi_remove), - .id_table = powertecscsi_cids, - .drv = { - .name = "powertecscsi", - }, -}; - -static int __init powertecscsi_init(void) -{ - return ecard_register_driver(&powertecscsi_driver); -} - -static void __exit powertecscsi_exit(void) -{ - ecard_remove_driver(&powertecscsi_driver); -} - -module_init(powertecscsi_init); -module_exit(powertecscsi_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("Powertec SCSI driver"); -MODULE_PARM(term, "1-8i"); -MODULE_PARM_DESC(term, "SCSI bus termination"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/scsi/queue.c b/drivers/acorn/scsi/queue.c deleted file mode 100644 index eae5a8d180df..000000000000 --- a/drivers/acorn/scsi/queue.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * linux/drivers/acorn/scsi/queue.c: queue handling primitives - * - * Copyright (C) 1997-2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Changelog: - * 15-Sep-1997 RMK Created. - * 11-Oct-1997 RMK Corrected problem with queue_remove_exclude - * not updating internal linked list properly - * (was causing commands to go missing). - * 30-Aug-2000 RMK Use Linux list handling and spinlocks - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../scsi/scsi.h" - -#define DEBUG - -typedef struct queue_entry { - struct list_head list; - Scsi_Cmnd *SCpnt; -#ifdef DEBUG - unsigned long magic; -#endif -} QE_t; - -#ifdef DEBUG -#define QUEUE_MAGIC_FREE 0xf7e1c9a3 -#define QUEUE_MAGIC_USED 0xf7e1cc33 - -#define SET_MAGIC(q,m) ((q)->magic = (m)) -#define BAD_MAGIC(q,m) ((q)->magic != (m)) -#else -#define SET_MAGIC(q,m) do { } while (0) -#define BAD_MAGIC(q,m) (0) -#endif - -#include "queue.h" - -#define NR_QE 32 - -/* - * Function: void queue_initialise (Queue_t *queue) - * Purpose : initialise a queue - * Params : queue - queue to initialise - */ -int queue_initialise (Queue_t *queue) -{ - unsigned int nqueues = NR_QE; - QE_t *q; - - spin_lock_init(&queue->queue_lock); - INIT_LIST_HEAD(&queue->head); - INIT_LIST_HEAD(&queue->free); - - /* - * If life was easier, then SCpnt would have a - * host-available list head, and we wouldn't - * need to keep free lists or allocate this - * memory. - */ - queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL); - if (q) { - for (; nqueues; q++, nqueues--) { - SET_MAGIC(q, QUEUE_MAGIC_FREE); - q->SCpnt = NULL; - list_add(&q->list, &queue->free); - } - } - - return queue->alloc != NULL; -} - -/* - * Function: void queue_free (Queue_t *queue) - * Purpose : free a queue - * Params : queue - queue to free - */ -void queue_free (Queue_t *queue) -{ - if (!list_empty(&queue->head)) - printk(KERN_WARNING "freeing non-empty queue %p\n", queue); - if (queue->alloc) - kfree(queue->alloc); -} - - -/* - * Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) - * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head. - * Params : queue - destination queue - * SCpnt - command to add - * head - add command to head of queue - * Returns : 0 on error, !0 on success - */ -int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) -{ - unsigned long flags; - struct list_head *l; - QE_t *q; - int ret = 0; - - spin_lock_irqsave(&queue->queue_lock, flags); - if (list_empty(&queue->free)) - goto empty; - - l = queue->free.next; - list_del(l); - - q = list_entry(l, QE_t, list); - if (BAD_MAGIC(q, QUEUE_MAGIC_FREE)) - BUG(); - - SET_MAGIC(q, QUEUE_MAGIC_USED); - q->SCpnt = SCpnt; - - if (head) - list_add(l, &queue->head); - else - list_add_tail(l, &queue->head); - - ret = 1; -empty: - spin_unlock_irqrestore(&queue->queue_lock, flags); - return ret; -} - -static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) -{ - QE_t *q; - - /* - * Move the entry from the "used" list onto the "free" list - */ - list_del(ent); - q = list_entry(ent, QE_t, list); - if (BAD_MAGIC(q, QUEUE_MAGIC_USED)) - BUG(); - - SET_MAGIC(q, QUEUE_MAGIC_FREE); - list_add(ent, &queue->free); - - return q->SCpnt; -} - -/* - * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude) - * Purpose : remove a SCSI command from a queue - * Params : queue - queue to remove command from - * exclude - bit array of target&lun which is busy - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) -{ - unsigned long flags; - struct list_head *l; - Scsi_Cmnd *SCpnt = NULL; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) { - SCpnt = __queue_remove(queue, l); - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return SCpnt; -} - -/* - * Function: Scsi_Cmnd *queue_remove (queue) - * Purpose : removes first SCSI command from a queue - * Params : queue - queue to remove command from - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -Scsi_Cmnd *queue_remove(Queue_t *queue) -{ - unsigned long flags; - Scsi_Cmnd *SCpnt = NULL; - - spin_lock_irqsave(&queue->queue_lock, flags); - if (!list_empty(&queue->head)) - SCpnt = __queue_remove(queue, queue->head.next); - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return SCpnt; -} - -/* - * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) - * Purpose : remove a SCSI command from the queue for a specified target/lun/tag - * Params : queue - queue to remove command from - * target - target that we want - * lun - lun on device - * tag - tag on device - * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements - */ -Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag) -{ - unsigned long flags; - struct list_head *l; - Scsi_Cmnd *SCpnt = NULL; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun && - q->SCpnt->tag == tag) { - SCpnt = __queue_remove(queue, l); - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return SCpnt; -} - -/* - * Function: queue_remove_all_target(queue, target) - * Purpose : remove all SCSI commands from the queue for a specified target - * Params : queue - queue to remove command from - * target - target device id - * Returns : nothing - */ -void queue_remove_all_target(Queue_t *queue, int target) -{ - unsigned long flags; - struct list_head *l; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt->device->id == target) - __queue_remove(queue, l); - } - spin_unlock_irqrestore(&queue->queue_lock, flags); -} - -/* - * Function: int queue_probetgtlun (queue, target, lun) - * Purpose : check to see if we have a command in the queue for the specified - * target/lun. - * Params : queue - queue to look in - * target - target we want to probe - * lun - lun on target - * Returns : 0 if not found, != 0 if found - */ -int queue_probetgtlun (Queue_t *queue, int target, int lun) -{ - unsigned long flags; - struct list_head *l; - int found = 0; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) { - found = 1; - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return found; -} - -/* - * Function: int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) - * Purpose : remove a specific command from the queues - * Params : queue - queue to look in - * SCpnt - command to find - * Returns : 0 if not found - */ -int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) -{ - unsigned long flags; - struct list_head *l; - int found = 0; - - spin_lock_irqsave(&queue->queue_lock, flags); - list_for_each(l, &queue->head) { - QE_t *q = list_entry(l, QE_t, list); - if (q->SCpnt == SCpnt) { - __queue_remove(queue, l); - found = 1; - break; - } - } - spin_unlock_irqrestore(&queue->queue_lock, flags); - - return found; -} - -EXPORT_SYMBOL(queue_initialise); -EXPORT_SYMBOL(queue_free); -EXPORT_SYMBOL(__queue_add); -EXPORT_SYMBOL(queue_remove); -EXPORT_SYMBOL(queue_remove_exclude); -EXPORT_SYMBOL(queue_remove_tgtluntag); -EXPORT_SYMBOL(queue_remove_cmd); -EXPORT_SYMBOL(queue_remove_all_target); -EXPORT_SYMBOL(queue_probetgtlun); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("SCSI command queueing"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/scsi/queue.h b/drivers/acorn/scsi/queue.h deleted file mode 100644 index 0c9dec4c1716..000000000000 --- a/drivers/acorn/scsi/queue.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * linux/drivers/acorn/scsi/queue.h: queue handling - * - * Copyright (C) 1997 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef QUEUE_H -#define QUEUE_H - -typedef struct { - struct list_head head; - struct list_head free; - spinlock_t queue_lock; - void *alloc; /* start of allocated mem */ -} Queue_t; - -/* - * Function: void queue_initialise (Queue_t *queue) - * Purpose : initialise a queue - * Params : queue - queue to initialise - */ -extern int queue_initialise (Queue_t *queue); - -/* - * Function: void queue_free (Queue_t *queue) - * Purpose : free a queue - * Params : queue - queue to free - */ -extern void queue_free (Queue_t *queue); - -/* - * Function: Scsi_Cmnd *queue_remove (queue) - * Purpose : removes first SCSI command from a queue - * Params : queue - queue to remove command from - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -extern Scsi_Cmnd *queue_remove (Queue_t *queue); - -/* - * Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude) - * Purpose : remove a SCSI command from a queue - * Params : queue - queue to remove command from - * exclude - array of busy LUNs - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available - */ -extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned long *exclude); - -#define queue_add_cmd_ordered(queue,SCpnt) \ - __queue_add(queue,SCpnt,(SCpnt)->cmnd[0] == REQUEST_SENSE) -#define queue_add_cmd_tail(queue,SCpnt) \ - __queue_add(queue,SCpnt,0) -/* - * Function: int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) - * Purpose : Add a new command onto a queue - * Params : queue - destination queue - * SCpnt - command to add - * head - add command to head of queue - * Returns : 0 on error, !0 on success - */ -extern int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head); - -/* - * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) - * Purpose : remove a SCSI command from the queue for a specified target/lun/tag - * Params : queue - queue to remove command from - * target - target that we want - * lun - lun on device - * tag - tag on device - * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements - */ -extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag); - -/* - * Function: queue_remove_all_target(queue, target) - * Purpose : remove all SCSI commands from the queue for a specified target - * Params : queue - queue to remove command from - * target - target device id - * Returns : nothing - */ -extern void queue_remove_all_target(Queue_t *queue, int target); - -/* - * Function: int queue_probetgtlun (queue, target, lun) - * Purpose : check to see if we have a command in the queue for the specified - * target/lun. - * Params : queue - queue to look in - * target - target we want to probe - * lun - lun on target - * Returns : 0 if not found, != 0 if found - */ -extern int queue_probetgtlun (Queue_t *queue, int target, int lun); - -/* - * Function: int queue_remove_cmd (Queue_t *queue, Scsi_Cmnd *SCpnt) - * Purpose : remove a specific command from the queues - * Params : queue - queue to look in - * SCpnt - command to find - * Returns : 0 if not found - */ -int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt); - -#endif /* QUEUE_H */ diff --git a/drivers/acorn/scsi/scsi.h b/drivers/acorn/scsi/scsi.h deleted file mode 100644 index 3b82817d88f4..000000000000 --- a/drivers/acorn/scsi/scsi.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * linux/drivers/acorn/scsi/scsi.h - * - * Copyright (C) 2002 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Commonly used scsi driver functions. - */ - -#define BELT_AND_BRACES - -/* - * The scatter-gather list handling. This contains all - * the yucky stuff that needs to be fixed properly. - */ -static inline int copy_SCp_to_sg(struct scatterlist *sg, Scsi_Pointer *SCp, int max) -{ - int bufs = SCp->buffers_residual; - - BUG_ON(bufs + 1 > max); - - sg->page = virt_to_page(SCp->ptr); - sg->offset = ((unsigned int)SCp->ptr) & ~PAGE_MASK; - sg->length = SCp->this_residual; - - if (bufs) - memcpy(sg + 1, SCp->buffer + 1, - sizeof(struct scatterlist) * bufs); - return bufs + 1; -} - -static inline int next_SCp(Scsi_Pointer *SCp) -{ - int ret = SCp->buffers_residual; - if (ret) { - SCp->buffer++; - SCp->buffers_residual--; - SCp->ptr = (char *) - (page_address(SCp->buffer->page) + - SCp->buffer->offset); - SCp->this_residual = SCp->buffer->length; - } else { - SCp->ptr = NULL; - SCp->this_residual = 0; - } - return ret; -} - -static inline unsigned char get_next_SCp_byte(Scsi_Pointer *SCp) -{ - char c = *SCp->ptr; - - SCp->ptr += 1; - SCp->this_residual -= 1; - - return c; -} - -static inline void put_next_SCp_byte(Scsi_Pointer *SCp, unsigned char c) -{ - *SCp->ptr = c; - SCp->ptr += 1; - SCp->this_residual -= 1; -} - -static inline void init_SCp(Scsi_Cmnd *SCpnt) -{ - memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer)); - - if (SCpnt->use_sg) { - unsigned long len = 0; - int buf; - - SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; - SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; - SCpnt->SCp.ptr = (char *) - (page_address(SCpnt->SCp.buffer->page) + - SCpnt->SCp.buffer->offset); - SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; - -#ifdef BELT_AND_BRACES - /* - * Calculate correct buffer length. Some commands - * come in with the wrong request_bufflen. - */ - for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++) - len += SCpnt->SCp.buffer[buf].length; - - if (SCpnt->request_bufflen != len) - printk(KERN_WARNING "scsi%d.%c: bad request buffer " - "length %d, should be %ld\n", SCpnt->device->host->host_no, - '0' + SCpnt->device->id, SCpnt->request_bufflen, len); - SCpnt->request_bufflen = len; -#endif - } else { - SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer; - SCpnt->SCp.this_residual = SCpnt->request_bufflen; - } - - /* - * If the upper SCSI layers pass a buffer, but zero length, - * we aren't interested in the buffer pointer. - */ - if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) { -#if 0 //def BELT_AND_BRACES - printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for " - "command ", SCpnt->host->host_no, '0' + SCpnt->target); - print_command(SCpnt->cmnd); -#endif - SCpnt->SCp.ptr = NULL; - } -} diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 5de65f013fb8..5e8f1e9bb4d9 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -221,14 +221,7 @@ config MII or internal device. It is safe to say Y or M here even if your ethernet card lack MII. -config ARM_AM79C961A - bool "ARM EBSA110 AM79C961A support" - depends on NET_ETHERNET && ARM && ARCH_EBSA110 - help - If you wish to compile a kernel for the EBSA-110, then you should - always answer Y to this. - -source "drivers/acorn/net/Kconfig" +source "drivers/net/arm/Kconfig" config MACE tristate "MACE (Power Mac ethernet) support" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a60b936743c4..1c47eb79f00b 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,7 +77,6 @@ obj-$(CONFIG_HP100) += hp100.o obj-$(CONFIG_SMC9194) += smc9194.o obj-$(CONFIG_FEC) += fec.o obj-$(CONFIG_68360_ENET) += 68360enet.o -obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o obj-$(CONFIG_ARM_ETHERH) += 8390.o obj-$(CONFIG_WD80x3) += wd.o 8390.o obj-$(CONFIG_EL2) += 3c503.o 8390.o @@ -179,7 +178,7 @@ obj-$(CONFIG_AMD8111_ETH) += amd8111e.o # non-drivers/net drivers who want mii lib obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o -obj-$(CONFIG_ARCH_ACORN) += ../acorn/net/ +obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_NET_FC) += fc/ obj-$(CONFIG_DEV_APPLETALK) += appletalk/ obj-$(CONFIG_TR) += tokenring/ diff --git a/drivers/net/am79c961a.c b/drivers/net/am79c961a.c deleted file mode 100644 index 68527d691d85..000000000000 --- a/drivers/net/am79c961a.c +++ /dev/null @@ -1,685 +0,0 @@ -/* - * linux/drivers/net/am79c961.c - * - * by Russell King 1995-2001. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Derived from various things including skeleton.c - * - * This is a special driver for the am79c961A Lance chip used in the - * Intel (formally Digital Equipment Corp) EBSA110 platform. Please - * note that this can not be built as a module (it doesn't make sense). - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define TX_BUFFERS 15 -#define RX_BUFFERS 25 - -#include "am79c961a.h" - -static irqreturn_t -am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs); - -static unsigned int net_debug = NET_DEBUG; - -static const char version[] = - "am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n"; - -/* --------------------------------------------------------------------------- */ - -#ifdef __arm__ -static void write_rreg(u_long base, u_int reg, u_int val) -{ - __asm__("str%?h %1, [%2] @ NET_RAP - str%?h %0, [%2, #-4] @ NET_RDP - " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); -} - -static inline unsigned short read_rreg(u_long base_addr, u_int reg) -{ - unsigned short v; - __asm__("str%?h %1, [%2] @ NET_RAP - ldr%?h %0, [%2, #-4] @ NET_RDP - " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); - return v; -} - -static inline void write_ireg(u_long base, u_int reg, u_int val) -{ - __asm__("str%?h %1, [%2] @ NET_RAP - str%?h %0, [%2, #8] @ NET_IDP - " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); -} - -#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1)) -#define am_readword(dev,off) __raw_readw(ISAMEM_BASE + ((off) << 1)) - -static inline void -am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) -{ - offset = ISAMEM_BASE + (offset << 1); - length = (length + 1) & ~1; - if ((int)buf & 2) { - __asm__ __volatile__("str%?h %2, [%0], #4" - : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); - buf += 2; - length -= 2; - } - while (length > 8) { - unsigned int tmp, tmp2; - __asm__ __volatile__(" - ldm%?ia %1!, {%2, %3} - str%?h %2, [%0], #4 - mov%? %2, %2, lsr #16 - str%?h %2, [%0], #4 - str%?h %3, [%0], #4 - mov%? %3, %3, lsr #16 - str%?h %3, [%0], #4 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) - : "0" (offset), "1" (buf)); - length -= 8; - } - while (length > 0) { - __asm__ __volatile__("str%?h %2, [%0], #4" - : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); - buf += 2; - length -= 2; - } -} - -static inline void -am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) -{ - offset = ISAMEM_BASE + (offset << 1); - length = (length + 1) & ~1; - if ((int)buf & 2) { - unsigned int tmp; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - str%?b %2, [%1], #1 - mov%? %2, %2, lsr #8 - str%?b %2, [%1], #1 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); - length -= 2; - } - while (length > 8) { - unsigned int tmp, tmp2, tmp3; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - ldr%?h %3, [%0], #4 - orr%? %2, %2, %3, lsl #16 - ldr%?h %3, [%0], #4 - ldr%?h %4, [%0], #4 - orr%? %3, %3, %4, lsl #16 - stm%?ia %1!, {%2, %3} - " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) - : "0" (offset), "1" (buf)); - length -= 8; - } - while (length > 0) { - unsigned int tmp; - __asm__ __volatile__(" - ldr%?h %2, [%0], #4 - str%?b %2, [%1], #1 - mov%? %2, %2, lsr #8 - str%?b %2, [%1], #1 - " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); - length -= 2; - } -} -#else -#error Not compatible -#endif - -static int -am79c961_ramtest(struct net_device *dev, unsigned int val) -{ - unsigned char *buffer = kmalloc (65536, GFP_KERNEL); - int i, error = 0, errorcount = 0; - - if (!buffer) - return 0; - memset (buffer, val, 65536); - am_writebuffer(dev, 0, buffer, 65536); - memset (buffer, val ^ 255, 65536); - am_readbuffer(dev, 0, buffer, 65536); - for (i = 0; i < 65536; i++) { - if (buffer[i] != val && !error) { - printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i); - error = 1; - errorcount ++; - } else if (error && buffer[i] == val) { - printk ("%05X\n", i); - error = 0; - } - } - if (error) - printk ("10000\n"); - kfree (buffer); - return errorcount; -} - -static void -am79c961_init_for_open(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - unsigned char *p; - u_int hdr_addr, first_free_addr; - int i; - - /* - * Stop the chip. - */ - spin_lock_irqsave(priv->chip_lock, flags); - write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP); - spin_unlock_irqrestore(priv->chip_lock, flags); - - write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */ - write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */ - write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */ - write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */ - - for (i = LADRL; i <= LADRH; i++) - write_rreg (dev->base_addr, i, 0); - - for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2) - write_rreg (dev->base_addr, i, p[0] | (p[1] << 8)); - - i = MODE_PORT_10BT; - if (dev->flags & IFF_PROMISC) - i |= MODE_PROMISC; - - write_rreg (dev->base_addr, MODE, i); - write_rreg (dev->base_addr, POLLINT, 0); - write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS); - write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS); - - first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16; - hdr_addr = 0; - - priv->rxhead = 0; - priv->rxtail = 0; - priv->rxhdr = hdr_addr; - - for (i = 0; i < RX_BUFFERS; i++) { - priv->rxbuffer[i] = first_free_addr; - am_writeword (dev, hdr_addr, first_free_addr); - am_writeword (dev, hdr_addr + 2, RMD_OWN); - am_writeword (dev, hdr_addr + 4, (-1600)); - am_writeword (dev, hdr_addr + 6, 0); - first_free_addr += 1600; - hdr_addr += 8; - } - priv->txhead = 0; - priv->txtail = 0; - priv->txhdr = hdr_addr; - for (i = 0; i < TX_BUFFERS; i++) { - priv->txbuffer[i] = first_free_addr; - am_writeword (dev, hdr_addr, first_free_addr); - am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP); - am_writeword (dev, hdr_addr + 4, 0xf000); - am_writeword (dev, hdr_addr + 6, 0); - first_free_addr += 1600; - hdr_addr += 8; - } - - write_rreg (dev->base_addr, BASERXL, priv->rxhdr); - write_rreg (dev->base_addr, BASERXH, 0); - write_rreg (dev->base_addr, BASETXL, priv->txhdr); - write_rreg (dev->base_addr, BASERXH, 0); - write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO); - write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); -} - -/* - * Open/initialize the board. - */ -static int -am79c961_open(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - int ret; - - memset (&priv->stats, 0, sizeof (priv->stats)); - - ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev); - if (ret) - return ret; - - am79c961_init_for_open(dev); - - netif_start_queue(dev); - - return 0; -} - -/* - * The inverse routine to am79c961_open(). - */ -static int -am79c961_close(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - - netif_stop_queue(dev); - - spin_lock_irqsave(priv->chip_lock, flags); - write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); - spin_unlock_irqrestore(priv->chip_lock, flags); - - free_irq (dev->irq, dev); - - return 0; -} - -/* - * Get the current statistics. - */ -static struct net_device_stats *am79c961_getstats (struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - return &priv->stats; -} - -static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash) -{ - if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) { - int idx, bit; - u32 crc; - - crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr); - - idx = crc >> 30; - bit = (crc >> 26) & 15; - - hash[idx] |= 1 << bit; - } -} - -/* - * Set or clear promiscuous/multicast mode filter for this adapter. - */ -static void am79c961_setmulticastlist (struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned long flags; - unsigned short multi_hash[4], mode; - int i, stopped; - - mode = MODE_PORT_10BT; - - if (dev->flags & IFF_PROMISC) { - mode |= MODE_PROMISC; - } else if (dev->flags & IFF_ALLMULTI) { - memset(multi_hash, 0xff, sizeof(multi_hash)); - } else { - struct dev_mc_list *dmi; - - memset(multi_hash, 0x00, sizeof(multi_hash)); - - for (dmi = dev->mc_list; dmi; dmi = dmi->next) - am79c961_mc_hash(dmi, multi_hash); - } - - spin_lock_irqsave(priv->chip_lock, flags); - - stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; - - if (!stopped) { - /* - * Put the chip into suspend mode - */ - write_rreg(dev->base_addr, CTRL1, CTRL1_SPND); - - /* - * Spin waiting for chip to report suspend mode - */ - while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) { - spin_unlock_irqrestore(priv->chip_lock, flags); - nop(); - spin_lock_irqsave(priv->chip_lock, flags); - } - } - - /* - * Update the multicast hash table - */ - for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++) - write_rreg(dev->base_addr, i + LADRL, multi_hash[i]); - - /* - * Write the mode register - */ - write_rreg(dev->base_addr, MODE, mode); - - if (!stopped) { - /* - * Put the chip back into running mode - */ - write_rreg(dev->base_addr, CTRL1, 0); - } - - spin_unlock_irqrestore(priv->chip_lock, flags); -} - -static void am79c961_timeout(struct net_device *dev) -{ - printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n", - dev->name); - - /* - * ought to do some setup of the tx side here - */ - - netif_wake_queue(dev); -} - -/* - * Transmit a packet - */ -static int -am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - unsigned int length = skb->len; - unsigned int hdraddr, bufaddr; - unsigned int head; - unsigned long flags; - - /* FIXME: I thought the 79c961 could do padding - RMK ??? */ - if (length < ETH_ZLEN) { - skb = skb_padto(skb, ETH_ZLEN); - if (skb == NULL) - return 0; - length = ETH_ZLEN; - } - - head = priv->txhead; - hdraddr = priv->txhdr + (head << 3); - bufaddr = priv->txbuffer[head]; - head += 1; - if (head >= TX_BUFFERS) - head = 0; - - am_writebuffer (dev, bufaddr, skb->data, length); - am_writeword (dev, hdraddr + 4, -length); - am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); - priv->txhead = head; - - spin_lock_irqsave(priv->chip_lock, flags); - write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA); - dev->trans_start = jiffies; - spin_unlock_irqrestore(priv->chip_lock, flags); - - /* - * If the next packet is owned by the ethernet device, - * then the tx ring is full and we can't add another - * packet. - */ - if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN) - netif_stop_queue(dev); - - dev_kfree_skb(skb); - - return 0; -} - -/* - * If we have a good packet(s), get it/them out of the buffers. - */ -static void -am79c961_rx(struct net_device *dev, struct dev_priv *priv) -{ - do { - struct sk_buff *skb; - u_int hdraddr; - u_int pktaddr; - u_int status; - int len; - - hdraddr = priv->rxhdr + (priv->rxtail << 3); - pktaddr = priv->rxbuffer[priv->rxtail]; - - status = am_readword (dev, hdraddr + 2); - if (status & RMD_OWN) /* do we own it? */ - break; - - priv->rxtail ++; - if (priv->rxtail >= RX_BUFFERS) - priv->rxtail = 0; - - if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) { - am_writeword (dev, hdraddr + 2, RMD_OWN); - priv->stats.rx_errors ++; - if (status & RMD_ERR) { - if (status & RMD_FRAM) - priv->stats.rx_frame_errors ++; - if (status & RMD_CRC) - priv->stats.rx_crc_errors ++; - } else if (status & RMD_STP) - priv->stats.rx_length_errors ++; - continue; - } - - len = am_readword(dev, hdraddr + 6); - skb = dev_alloc_skb(len + 2); - - if (skb) { - skb->dev = dev; - skb_reserve(skb, 2); - - am_readbuffer(dev, pktaddr, skb_put(skb, len), len); - am_writeword(dev, hdraddr + 2, RMD_OWN); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - dev->last_rx = jiffies; - priv->stats.rx_bytes += len; - priv->stats.rx_packets ++; - } else { - am_writeword (dev, hdraddr + 2, RMD_OWN); - printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name); - priv->stats.rx_dropped ++; - break; - } - } while (1); -} - -/* - * Update stats for the transmitted packet - */ -static void -am79c961_tx(struct net_device *dev, struct dev_priv *priv) -{ - do { - u_int hdraddr; - u_int status; - - hdraddr = priv->txhdr + (priv->txtail << 3); - status = am_readword (dev, hdraddr + 2); - if (status & TMD_OWN) - break; - - priv->txtail ++; - if (priv->txtail >= TX_BUFFERS) - priv->txtail = 0; - - if (status & TMD_ERR) { - u_int status2; - - priv->stats.tx_errors ++; - - status2 = am_readword (dev, hdraddr + 6); - - /* - * Clear the error byte - */ - am_writeword (dev, hdraddr + 6, 0); - - if (status2 & TST_RTRY) - priv->stats.collisions += 16; - if (status2 & TST_LCOL) - priv->stats.tx_window_errors ++; - if (status2 & TST_LCAR) - priv->stats.tx_carrier_errors ++; - if (status2 & TST_UFLO) - priv->stats.tx_fifo_errors ++; - continue; - } - priv->stats.tx_packets ++; - } while (priv->txtail != priv->txhead); - - netif_wake_queue(dev); -} - -static irqreturn_t -am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct dev_priv *priv = (struct dev_priv *)dev->priv; - u_int status; - int handled = 0; - - status = read_rreg(dev->base_addr, CSR0); - write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA)); - - if (status & CSR0_RINT) { - handled = 1; - am79c961_rx(dev, priv); - } - if (status & CSR0_TINT) { - handled = 1; - am79c961_tx(dev, priv); - } - if (status & CSR0_MISS) { - handled = 1; - priv->stats.rx_dropped ++; - } - return IRQ_RETVAL(handled); -} - -/* - * Initialise the chip. Note that we always expect - * to be entered with interrupts enabled. - */ -static int -am79c961_hw_init(struct net_device *dev) -{ - struct dev_priv *priv = (struct dev_priv *)dev->priv; - - spin_lock_irq(priv->chip_lock); - write_rreg (dev->base_addr, CSR0, CSR0_STOP); - write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); - spin_unlock_irq(priv->chip_lock); - - am79c961_ramtest(dev, 0x66); - am79c961_ramtest(dev, 0x99); - - return 0; -} - -static void __init am79c961_banner(void) -{ - static unsigned version_printed; - - if (net_debug && version_printed++ == 0) - printk(KERN_INFO "%s", version); -} - -static int __init am79c961_init(void) -{ - struct net_device *dev; - struct dev_priv *priv; - int i, ret; - - dev = init_etherdev(NULL, sizeof(struct dev_priv)); - ret = -ENOMEM; - if (!dev) - goto out; - - priv = dev->priv; - - /* - * Fixed address and IRQ lines here. - * The PNP initialisation should have been - * done by the ether bootp loader. - */ - dev->base_addr = 0x220; - dev->irq = IRQ_EBSA110_ETHERNET; - - /* - * Reset the device. - */ - inb(dev->base_addr + NET_RESET); - udelay(5); - - /* - * Check the manufacturer part of the - * ether address. - */ - ret = -ENODEV; - if (inb(dev->base_addr) != 0x08 || - inb(dev->base_addr + 2) != 0x00 || - inb(dev->base_addr + 4) != 0x2b) - goto nodev; - - if (!request_region(dev->base_addr, 0x18, dev->name)) - goto nodev; - - am79c961_banner(); - printk(KERN_INFO "%s: ether address ", dev->name); - - /* Retrive and print the ethernet address. */ - for (i = 0; i < 6; i++) { - dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff; - printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); - } - - if (am79c961_hw_init(dev)) - goto release; - - dev->open = am79c961_open; - dev->stop = am79c961_close; - dev->hard_start_xmit = am79c961_sendpacket; - dev->get_stats = am79c961_getstats; - dev->set_multicast_list = am79c961_setmulticastlist; - dev->tx_timeout = am79c961_timeout; - - return 0; - -release: - release_region(dev->base_addr, 0x18); -nodev: - unregister_netdev(dev); - kfree(dev); -out: - return ret; -} - -__initcall(am79c961_init); diff --git a/drivers/net/am79c961a.h b/drivers/net/am79c961a.h deleted file mode 100644 index 453b728ab744..000000000000 --- a/drivers/net/am79c961a.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * linux/drivers/net/am79c961.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _LINUX_am79c961a_H -#define _LINUX_am79c961a_H - -/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ -#define DEBUG_TX 2 -#define DEBUG_RX 4 -#define DEBUG_INT 8 -#define DEBUG_IC 16 -#ifndef NET_DEBUG -#define NET_DEBUG 0 -#endif - -#define NET_UID 0 -#define NET_RDP 0x10 -#define NET_RAP 0x12 -#define NET_RESET 0x14 -#define NET_IDP 0x16 - -/* - * RAP registers - */ -#define CSR0 0 -#define CSR0_INIT 0x0001 -#define CSR0_STRT 0x0002 -#define CSR0_STOP 0x0004 -#define CSR0_TDMD 0x0008 -#define CSR0_TXON 0x0010 -#define CSR0_RXON 0x0020 -#define CSR0_IENA 0x0040 -#define CSR0_INTR 0x0080 -#define CSR0_IDON 0x0100 -#define CSR0_TINT 0x0200 -#define CSR0_RINT 0x0400 -#define CSR0_MERR 0x0800 -#define CSR0_MISS 0x1000 -#define CSR0_CERR 0x2000 -#define CSR0_BABL 0x4000 -#define CSR0_ERR 0x8000 - -#define CSR3 3 -#define CSR3_EMBA 0x0008 -#define CSR3_DXMT2PD 0x0010 -#define CSR3_LAPPEN 0x0020 -#define CSR3_DXSUFLO 0x0040 -#define CSR3_IDONM 0x0100 -#define CSR3_TINTM 0x0200 -#define CSR3_RINTM 0x0400 -#define CSR3_MERRM 0x0800 -#define CSR3_MISSM 0x1000 -#define CSR3_BABLM 0x4000 -#define CSR3_MASKALL 0x5F00 - -#define CTRL1 5 -#define CTRL1_SPND 0x0001 - -#define LADRL 8 -#define LADRM1 9 -#define LADRM2 10 -#define LADRH 11 -#define PADRL 12 -#define PADRM 13 -#define PADRH 14 - -#define MODE 15 -#define MODE_DISRX 0x0001 -#define MODE_DISTX 0x0002 -#define MODE_LOOP 0x0004 -#define MODE_DTCRC 0x0008 -#define MODE_COLL 0x0010 -#define MODE_DRETRY 0x0020 -#define MODE_INTLOOP 0x0040 -#define MODE_PORT_AUI 0x0000 -#define MODE_PORT_10BT 0x0080 -#define MODE_DRXPA 0x2000 -#define MODE_DRXBA 0x4000 -#define MODE_PROMISC 0x8000 - -#define BASERXL 24 -#define BASERXH 25 -#define BASETXL 30 -#define BASETXH 31 - -#define POLLINT 47 - -#define SIZERXR 76 -#define SIZETXR 78 - -#define RMD_ENP 0x0100 -#define RMD_STP 0x0200 -#define RMD_CRC 0x0800 -#define RMD_FRAM 0x2000 -#define RMD_ERR 0x4000 -#define RMD_OWN 0x8000 - -#define TMD_ENP 0x0100 -#define TMD_STP 0x0200 -#define TMD_MORE 0x1000 -#define TMD_ERR 0x4000 -#define TMD_OWN 0x8000 - -#define TST_RTRY 0x0400 -#define TST_LCAR 0x0800 -#define TST_LCOL 0x1000 -#define TST_UFLO 0x4000 -#define TST_BUFF 0x8000 - -struct dev_priv { - struct net_device_stats stats; - unsigned long rxbuffer[RX_BUFFERS]; - unsigned long txbuffer[TX_BUFFERS]; - unsigned char txhead; - unsigned char txtail; - unsigned char rxhead; - unsigned char rxtail; - unsigned long rxhdr; - unsigned long txhdr; - spinlock_t chip_lock; -}; - -extern int am79c961_probe (struct net_device *dev); - -#endif diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig new file mode 100644 index 000000000000..de3cfdcf61a3 --- /dev/null +++ b/drivers/net/arm/Kconfig @@ -0,0 +1,45 @@ +# +# Acorn Network device configuration +# These are for Acorn's Expansion card network interfaces +# +config ARM_AM79C961A + bool "ARM EBSA110 AM79C961A support" + depends on NET_ETHERNET && ARM && ARCH_EBSA110 + help + If you wish to compile a kernel for the EBSA-110, then you should + always answer Y to this. + +config ARM_ETHER1 + tristate "Acorn Ether1 support" + depends on NET_ETHERNET && ARM && ARCH_ACORN + help + If you have an Acorn system with one of these (AKA25) network cards, + you should say Y to this option if you wish to use it with Linux. + +config ARM_ETHER3 + tristate "Acorn/ANT Ether3 support" + depends on NET_ETHERNET && ARM && ARCH_ACORN + help + If you have an Acorn system with one of these network cards, you + should say Y to this option if you wish to use it with Linux. + +config ARM_ETHERH + tristate "I-cubed EtherH/ANT EtherM support" + depends on NET_ETHERNET && ARM && ARCH_ACORN + help + If you have an Acorn system with one of these network cards, you + should say Y to this option if you wish to use it with Linux. + +config ARM_ETHER00 + tristate "Altera Ether00 support" + depends on NET_ETHERNET && ARM && ARCH_CAMELOT + help + This is the driver for Altera's ether00 ethernet mac IP core. Say + Y here if you want to build support for this into the kernel. It + is also available as a module (say M here) that can be inserted/ + removed from the kernel at the same time as the PLD is configured. + If this driver is running on an epxa10 development board then it + will generate a suitable hw address based on the board serial + number (MTD support is required for this). Otherwise you will + need to set a suitable hw address using ifconfig. + diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile new file mode 100644 index 000000000000..b0d706834d89 --- /dev/null +++ b/drivers/net/arm/Makefile @@ -0,0 +1,10 @@ +# File: drivers/net/arm/Makefile +# +# Makefile for the ARM network device drivers +# + +obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o +obj-$(CONFIG_ARM_ETHER00) += ether00.o +obj-$(CONFIG_ARM_ETHERH) += etherh.o +obj-$(CONFIG_ARM_ETHER3) += ether3.o +obj-$(CONFIG_ARM_ETHER1) += ether1.o diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c new file mode 100644 index 000000000000..68527d691d85 --- /dev/null +++ b/drivers/net/arm/am79c961a.c @@ -0,0 +1,685 @@ +/* + * linux/drivers/net/am79c961.c + * + * by Russell King 1995-2001. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Derived from various things including skeleton.c + * + * This is a special driver for the am79c961A Lance chip used in the + * Intel (formally Digital Equipment Corp) EBSA110 platform. Please + * note that this can not be built as a module (it doesn't make sense). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TX_BUFFERS 15 +#define RX_BUFFERS 25 + +#include "am79c961a.h" + +static irqreturn_t +am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs); + +static unsigned int net_debug = NET_DEBUG; + +static const char version[] = + "am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n"; + +/* --------------------------------------------------------------------------- */ + +#ifdef __arm__ +static void write_rreg(u_long base, u_int reg, u_int val) +{ + __asm__("str%?h %1, [%2] @ NET_RAP + str%?h %0, [%2, #-4] @ NET_RDP + " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); +} + +static inline unsigned short read_rreg(u_long base_addr, u_int reg) +{ + unsigned short v; + __asm__("str%?h %1, [%2] @ NET_RAP + ldr%?h %0, [%2, #-4] @ NET_RDP + " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464)); + return v; +} + +static inline void write_ireg(u_long base, u_int reg, u_int val) +{ + __asm__("str%?h %1, [%2] @ NET_RAP + str%?h %0, [%2, #8] @ NET_IDP + " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464)); +} + +#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1)) +#define am_readword(dev,off) __raw_readw(ISAMEM_BASE + ((off) << 1)) + +static inline void +am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) +{ + offset = ISAMEM_BASE + (offset << 1); + length = (length + 1) & ~1; + if ((int)buf & 2) { + __asm__ __volatile__("str%?h %2, [%0], #4" + : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); + buf += 2; + length -= 2; + } + while (length > 8) { + unsigned int tmp, tmp2; + __asm__ __volatile__(" + ldm%?ia %1!, {%2, %3} + str%?h %2, [%0], #4 + mov%? %2, %2, lsr #16 + str%?h %2, [%0], #4 + str%?h %3, [%0], #4 + mov%? %3, %3, lsr #16 + str%?h %3, [%0], #4 + " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2) + : "0" (offset), "1" (buf)); + length -= 8; + } + while (length > 0) { + __asm__ __volatile__("str%?h %2, [%0], #4" + : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8))); + buf += 2; + length -= 2; + } +} + +static inline void +am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length) +{ + offset = ISAMEM_BASE + (offset << 1); + length = (length + 1) & ~1; + if ((int)buf & 2) { + unsigned int tmp; + __asm__ __volatile__(" + ldr%?h %2, [%0], #4 + str%?b %2, [%1], #1 + mov%? %2, %2, lsr #8 + str%?b %2, [%1], #1 + " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf)); + length -= 2; + } + while (length > 8) { + unsigned int tmp, tmp2, tmp3; + __asm__ __volatile__(" + ldr%?h %2, [%0], #4 + ldr%?h %3, [%0], #4 + orr%? %2, %2, %3, lsl #16 + ldr%?h %3, [%0], #4 + ldr%?h %4, [%0], #4 + orr%? %3, %3, %4, lsl #16 + stm%?ia %1!, {%2, %3} + " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3) + : "0" (offset), "1" (buf)); + length -= 8; + } + while (length > 0) { + unsigned int tmp; + __asm__ __volatile__(" + ldr%?h %2, [%0], #4 + str%?b %2, [%1], #1 + mov%? %2, %2, lsr #8 + str%?b %2, [%1], #1 + " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf)); + length -= 2; + } +} +#else +#error Not compatible +#endif + +static int +am79c961_ramtest(struct net_device *dev, unsigned int val) +{ + unsigned char *buffer = kmalloc (65536, GFP_KERNEL); + int i, error = 0, errorcount = 0; + + if (!buffer) + return 0; + memset (buffer, val, 65536); + am_writebuffer(dev, 0, buffer, 65536); + memset (buffer, val ^ 255, 65536); + am_readbuffer(dev, 0, buffer, 65536); + for (i = 0; i < 65536; i++) { + if (buffer[i] != val && !error) { + printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i); + error = 1; + errorcount ++; + } else if (error && buffer[i] == val) { + printk ("%05X\n", i); + error = 0; + } + } + if (error) + printk ("10000\n"); + kfree (buffer); + return errorcount; +} + +static void +am79c961_init_for_open(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + unsigned char *p; + u_int hdr_addr, first_free_addr; + int i; + + /* + * Stop the chip. + */ + spin_lock_irqsave(priv->chip_lock, flags); + write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP); + spin_unlock_irqrestore(priv->chip_lock, flags); + + write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */ + write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */ + write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */ + write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */ + + for (i = LADRL; i <= LADRH; i++) + write_rreg (dev->base_addr, i, 0); + + for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2) + write_rreg (dev->base_addr, i, p[0] | (p[1] << 8)); + + i = MODE_PORT_10BT; + if (dev->flags & IFF_PROMISC) + i |= MODE_PROMISC; + + write_rreg (dev->base_addr, MODE, i); + write_rreg (dev->base_addr, POLLINT, 0); + write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS); + write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS); + + first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16; + hdr_addr = 0; + + priv->rxhead = 0; + priv->rxtail = 0; + priv->rxhdr = hdr_addr; + + for (i = 0; i < RX_BUFFERS; i++) { + priv->rxbuffer[i] = first_free_addr; + am_writeword (dev, hdr_addr, first_free_addr); + am_writeword (dev, hdr_addr + 2, RMD_OWN); + am_writeword (dev, hdr_addr + 4, (-1600)); + am_writeword (dev, hdr_addr + 6, 0); + first_free_addr += 1600; + hdr_addr += 8; + } + priv->txhead = 0; + priv->txtail = 0; + priv->txhdr = hdr_addr; + for (i = 0; i < TX_BUFFERS; i++) { + priv->txbuffer[i] = first_free_addr; + am_writeword (dev, hdr_addr, first_free_addr); + am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP); + am_writeword (dev, hdr_addr + 4, 0xf000); + am_writeword (dev, hdr_addr + 6, 0); + first_free_addr += 1600; + hdr_addr += 8; + } + + write_rreg (dev->base_addr, BASERXL, priv->rxhdr); + write_rreg (dev->base_addr, BASERXH, 0); + write_rreg (dev->base_addr, BASETXL, priv->txhdr); + write_rreg (dev->base_addr, BASERXH, 0); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO); + write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT); +} + +/* + * Open/initialize the board. + */ +static int +am79c961_open(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int ret; + + memset (&priv->stats, 0, sizeof (priv->stats)); + + ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev); + if (ret) + return ret; + + am79c961_init_for_open(dev); + + netif_start_queue(dev); + + return 0; +} + +/* + * The inverse routine to am79c961_open(). + */ +static int +am79c961_close(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + + netif_stop_queue(dev); + + spin_lock_irqsave(priv->chip_lock, flags); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); + spin_unlock_irqrestore(priv->chip_lock, flags); + + free_irq (dev->irq, dev); + + return 0; +} + +/* + * Get the current statistics. + */ +static struct net_device_stats *am79c961_getstats (struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + return &priv->stats; +} + +static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash) +{ + if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) { + int idx, bit; + u32 crc; + + crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr); + + idx = crc >> 30; + bit = (crc >> 26) & 15; + + hash[idx] |= 1 << bit; + } +} + +/* + * Set or clear promiscuous/multicast mode filter for this adapter. + */ +static void am79c961_setmulticastlist (struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + unsigned short multi_hash[4], mode; + int i, stopped; + + mode = MODE_PORT_10BT; + + if (dev->flags & IFF_PROMISC) { + mode |= MODE_PROMISC; + } else if (dev->flags & IFF_ALLMULTI) { + memset(multi_hash, 0xff, sizeof(multi_hash)); + } else { + struct dev_mc_list *dmi; + + memset(multi_hash, 0x00, sizeof(multi_hash)); + + for (dmi = dev->mc_list; dmi; dmi = dmi->next) + am79c961_mc_hash(dmi, multi_hash); + } + + spin_lock_irqsave(priv->chip_lock, flags); + + stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; + + if (!stopped) { + /* + * Put the chip into suspend mode + */ + write_rreg(dev->base_addr, CTRL1, CTRL1_SPND); + + /* + * Spin waiting for chip to report suspend mode + */ + while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) { + spin_unlock_irqrestore(priv->chip_lock, flags); + nop(); + spin_lock_irqsave(priv->chip_lock, flags); + } + } + + /* + * Update the multicast hash table + */ + for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++) + write_rreg(dev->base_addr, i + LADRL, multi_hash[i]); + + /* + * Write the mode register + */ + write_rreg(dev->base_addr, MODE, mode); + + if (!stopped) { + /* + * Put the chip back into running mode + */ + write_rreg(dev->base_addr, CTRL1, 0); + } + + spin_unlock_irqrestore(priv->chip_lock, flags); +} + +static void am79c961_timeout(struct net_device *dev) +{ + printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n", + dev->name); + + /* + * ought to do some setup of the tx side here + */ + + netif_wake_queue(dev); +} + +/* + * Transmit a packet + */ +static int +am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned int length = skb->len; + unsigned int hdraddr, bufaddr; + unsigned int head; + unsigned long flags; + + /* FIXME: I thought the 79c961 could do padding - RMK ??? */ + if (length < ETH_ZLEN) { + skb = skb_padto(skb, ETH_ZLEN); + if (skb == NULL) + return 0; + length = ETH_ZLEN; + } + + head = priv->txhead; + hdraddr = priv->txhdr + (head << 3); + bufaddr = priv->txbuffer[head]; + head += 1; + if (head >= TX_BUFFERS) + head = 0; + + am_writebuffer (dev, bufaddr, skb->data, length); + am_writeword (dev, hdraddr + 4, -length); + am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); + priv->txhead = head; + + spin_lock_irqsave(priv->chip_lock, flags); + write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA); + dev->trans_start = jiffies; + spin_unlock_irqrestore(priv->chip_lock, flags); + + /* + * If the next packet is owned by the ethernet device, + * then the tx ring is full and we can't add another + * packet. + */ + if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN) + netif_stop_queue(dev); + + dev_kfree_skb(skb); + + return 0; +} + +/* + * If we have a good packet(s), get it/them out of the buffers. + */ +static void +am79c961_rx(struct net_device *dev, struct dev_priv *priv) +{ + do { + struct sk_buff *skb; + u_int hdraddr; + u_int pktaddr; + u_int status; + int len; + + hdraddr = priv->rxhdr + (priv->rxtail << 3); + pktaddr = priv->rxbuffer[priv->rxtail]; + + status = am_readword (dev, hdraddr + 2); + if (status & RMD_OWN) /* do we own it? */ + break; + + priv->rxtail ++; + if (priv->rxtail >= RX_BUFFERS) + priv->rxtail = 0; + + if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) { + am_writeword (dev, hdraddr + 2, RMD_OWN); + priv->stats.rx_errors ++; + if (status & RMD_ERR) { + if (status & RMD_FRAM) + priv->stats.rx_frame_errors ++; + if (status & RMD_CRC) + priv->stats.rx_crc_errors ++; + } else if (status & RMD_STP) + priv->stats.rx_length_errors ++; + continue; + } + + len = am_readword(dev, hdraddr + 6); + skb = dev_alloc_skb(len + 2); + + if (skb) { + skb->dev = dev; + skb_reserve(skb, 2); + + am_readbuffer(dev, pktaddr, skb_put(skb, len), len); + am_writeword(dev, hdraddr + 2, RMD_OWN); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + priv->stats.rx_bytes += len; + priv->stats.rx_packets ++; + } else { + am_writeword (dev, hdraddr + 2, RMD_OWN); + printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name); + priv->stats.rx_dropped ++; + break; + } + } while (1); +} + +/* + * Update stats for the transmitted packet + */ +static void +am79c961_tx(struct net_device *dev, struct dev_priv *priv) +{ + do { + u_int hdraddr; + u_int status; + + hdraddr = priv->txhdr + (priv->txtail << 3); + status = am_readword (dev, hdraddr + 2); + if (status & TMD_OWN) + break; + + priv->txtail ++; + if (priv->txtail >= TX_BUFFERS) + priv->txtail = 0; + + if (status & TMD_ERR) { + u_int status2; + + priv->stats.tx_errors ++; + + status2 = am_readword (dev, hdraddr + 6); + + /* + * Clear the error byte + */ + am_writeword (dev, hdraddr + 6, 0); + + if (status2 & TST_RTRY) + priv->stats.collisions += 16; + if (status2 & TST_LCOL) + priv->stats.tx_window_errors ++; + if (status2 & TST_LCAR) + priv->stats.tx_carrier_errors ++; + if (status2 & TST_UFLO) + priv->stats.tx_fifo_errors ++; + continue; + } + priv->stats.tx_packets ++; + } while (priv->txtail != priv->txhead); + + netif_wake_queue(dev); +} + +static irqreturn_t +am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct dev_priv *priv = (struct dev_priv *)dev->priv; + u_int status; + int handled = 0; + + status = read_rreg(dev->base_addr, CSR0); + write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA)); + + if (status & CSR0_RINT) { + handled = 1; + am79c961_rx(dev, priv); + } + if (status & CSR0_TINT) { + handled = 1; + am79c961_tx(dev, priv); + } + if (status & CSR0_MISS) { + handled = 1; + priv->stats.rx_dropped ++; + } + return IRQ_RETVAL(handled); +} + +/* + * Initialise the chip. Note that we always expect + * to be entered with interrupts enabled. + */ +static int +am79c961_hw_init(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + + spin_lock_irq(priv->chip_lock); + write_rreg (dev->base_addr, CSR0, CSR0_STOP); + write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); + spin_unlock_irq(priv->chip_lock); + + am79c961_ramtest(dev, 0x66); + am79c961_ramtest(dev, 0x99); + + return 0; +} + +static void __init am79c961_banner(void) +{ + static unsigned version_printed; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static int __init am79c961_init(void) +{ + struct net_device *dev; + struct dev_priv *priv; + int i, ret; + + dev = init_etherdev(NULL, sizeof(struct dev_priv)); + ret = -ENOMEM; + if (!dev) + goto out; + + priv = dev->priv; + + /* + * Fixed address and IRQ lines here. + * The PNP initialisation should have been + * done by the ether bootp loader. + */ + dev->base_addr = 0x220; + dev->irq = IRQ_EBSA110_ETHERNET; + + /* + * Reset the device. + */ + inb(dev->base_addr + NET_RESET); + udelay(5); + + /* + * Check the manufacturer part of the + * ether address. + */ + ret = -ENODEV; + if (inb(dev->base_addr) != 0x08 || + inb(dev->base_addr + 2) != 0x00 || + inb(dev->base_addr + 4) != 0x2b) + goto nodev; + + if (!request_region(dev->base_addr, 0x18, dev->name)) + goto nodev; + + am79c961_banner(); + printk(KERN_INFO "%s: ether address ", dev->name); + + /* Retrive and print the ethernet address. */ + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff; + printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]); + } + + if (am79c961_hw_init(dev)) + goto release; + + dev->open = am79c961_open; + dev->stop = am79c961_close; + dev->hard_start_xmit = am79c961_sendpacket; + dev->get_stats = am79c961_getstats; + dev->set_multicast_list = am79c961_setmulticastlist; + dev->tx_timeout = am79c961_timeout; + + return 0; + +release: + release_region(dev->base_addr, 0x18); +nodev: + unregister_netdev(dev); + kfree(dev); +out: + return ret; +} + +__initcall(am79c961_init); diff --git a/drivers/net/arm/am79c961a.h b/drivers/net/arm/am79c961a.h new file mode 100644 index 000000000000..453b728ab744 --- /dev/null +++ b/drivers/net/arm/am79c961a.h @@ -0,0 +1,130 @@ +/* + * linux/drivers/net/am79c961.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_am79c961a_H +#define _LINUX_am79c961a_H + +/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ +#define DEBUG_TX 2 +#define DEBUG_RX 4 +#define DEBUG_INT 8 +#define DEBUG_IC 16 +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +#define NET_UID 0 +#define NET_RDP 0x10 +#define NET_RAP 0x12 +#define NET_RESET 0x14 +#define NET_IDP 0x16 + +/* + * RAP registers + */ +#define CSR0 0 +#define CSR0_INIT 0x0001 +#define CSR0_STRT 0x0002 +#define CSR0_STOP 0x0004 +#define CSR0_TDMD 0x0008 +#define CSR0_TXON 0x0010 +#define CSR0_RXON 0x0020 +#define CSR0_IENA 0x0040 +#define CSR0_INTR 0x0080 +#define CSR0_IDON 0x0100 +#define CSR0_TINT 0x0200 +#define CSR0_RINT 0x0400 +#define CSR0_MERR 0x0800 +#define CSR0_MISS 0x1000 +#define CSR0_CERR 0x2000 +#define CSR0_BABL 0x4000 +#define CSR0_ERR 0x8000 + +#define CSR3 3 +#define CSR3_EMBA 0x0008 +#define CSR3_DXMT2PD 0x0010 +#define CSR3_LAPPEN 0x0020 +#define CSR3_DXSUFLO 0x0040 +#define CSR3_IDONM 0x0100 +#define CSR3_TINTM 0x0200 +#define CSR3_RINTM 0x0400 +#define CSR3_MERRM 0x0800 +#define CSR3_MISSM 0x1000 +#define CSR3_BABLM 0x4000 +#define CSR3_MASKALL 0x5F00 + +#define CTRL1 5 +#define CTRL1_SPND 0x0001 + +#define LADRL 8 +#define LADRM1 9 +#define LADRM2 10 +#define LADRH 11 +#define PADRL 12 +#define PADRM 13 +#define PADRH 14 + +#define MODE 15 +#define MODE_DISRX 0x0001 +#define MODE_DISTX 0x0002 +#define MODE_LOOP 0x0004 +#define MODE_DTCRC 0x0008 +#define MODE_COLL 0x0010 +#define MODE_DRETRY 0x0020 +#define MODE_INTLOOP 0x0040 +#define MODE_PORT_AUI 0x0000 +#define MODE_PORT_10BT 0x0080 +#define MODE_DRXPA 0x2000 +#define MODE_DRXBA 0x4000 +#define MODE_PROMISC 0x8000 + +#define BASERXL 24 +#define BASERXH 25 +#define BASETXL 30 +#define BASETXH 31 + +#define POLLINT 47 + +#define SIZERXR 76 +#define SIZETXR 78 + +#define RMD_ENP 0x0100 +#define RMD_STP 0x0200 +#define RMD_CRC 0x0800 +#define RMD_FRAM 0x2000 +#define RMD_ERR 0x4000 +#define RMD_OWN 0x8000 + +#define TMD_ENP 0x0100 +#define TMD_STP 0x0200 +#define TMD_MORE 0x1000 +#define TMD_ERR 0x4000 +#define TMD_OWN 0x8000 + +#define TST_RTRY 0x0400 +#define TST_LCAR 0x0800 +#define TST_LCOL 0x1000 +#define TST_UFLO 0x4000 +#define TST_BUFF 0x8000 + +struct dev_priv { + struct net_device_stats stats; + unsigned long rxbuffer[RX_BUFFERS]; + unsigned long txbuffer[TX_BUFFERS]; + unsigned char txhead; + unsigned char txtail; + unsigned char rxhead; + unsigned char rxtail; + unsigned long rxhdr; + unsigned long txhdr; + spinlock_t chip_lock; +}; + +extern int am79c961_probe (struct net_device *dev); + +#endif diff --git a/drivers/net/arm/ether00.c b/drivers/net/arm/ether00.c new file mode 100644 index 000000000000..d5a05d1d1cd3 --- /dev/null +++ b/drivers/net/arm/ether00.c @@ -0,0 +1,1025 @@ +/* + * drivers/net/ether00.c + * + * Copyright (C) 2001 Altera Corporation + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* includes */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +MODULE_AUTHOR("Clive Davies"); +MODULE_DESCRIPTION("Altera Ether00 IP core driver"); +MODULE_LICENSE("GPL"); + +#define PKT_BUF_SZ 1540 /* Size of each rx buffer */ +#define ETH_NR 4 /* Number of MACs this driver supports */ + +#define DEBUG(x) + +#define __dma_va(x) (unsigned int)((unsigned int)priv->dma_data+(((unsigned int)(x))&(EXC_SPSRAM_BLOCK0_SIZE-1))) +#define __dma_pa(x) (unsigned int)(EXC_SPSRAM_BLOCK0_BASE+(((unsigned int)(x))-(unsigned int)priv->dma_data)) + +#define ETHER00_BASE 0 +#define ETHER00_TYPE +#define ETHER00_NAME "ether00" +#define MAC_REG_SIZE 0x400 /* size of MAC register area */ + + + +/* typedefs */ + +/* The definition of the driver control structure */ + +#define RX_NUM_BUFF 10 +#define RX_NUM_FDESC 10 +#define TX_NUM_FDESC 10 + +struct tx_fda_ent{ + FDA_DESC fd; + BUF_DESC bd; + BUF_DESC pad; +}; +struct rx_fda_ent{ + FDA_DESC fd; + BUF_DESC bd; + BUF_DESC pad; +}; +struct rx_blist_ent{ + FDA_DESC fd; + BUF_DESC bd; + BUF_DESC pad; +}; +struct net_priv +{ + struct net_device_stats stats; + struct sk_buff* skb; + void* dma_data; + struct rx_blist_ent* rx_blist_vp; + struct rx_fda_ent* rx_fda_ptr; + struct tx_fda_ent* tx_fdalist_vp; + struct tq_struct tq_memupdate; + unsigned char memupdate_scheduled; + unsigned char rx_disabled; + unsigned char queue_stopped; + spinlock_t rx_lock; +}; + +static const char vendor_id[2]={0x07,0xed}; + +#ifdef ETHER00_DEBUG + +/* Dump (most) registers for debugging puposes */ + +static void dump_regs(struct net_device *dev){ + struct net_priv* priv=dev->priv; + unsigned int* i; + + printk("\n RX free descriptor area:\n"); + + for(i=(unsigned int*)priv->rx_fda_ptr; + i<((unsigned int*)(priv->rx_fda_ptr+RX_NUM_FDESC));){ + printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3)); + i+=4; + } + + printk("\n RX buffer list:\n"); + + for(i=(unsigned int*)priv->rx_blist_vp; + i<((unsigned int*)(priv->rx_blist_vp+RX_NUM_BUFF));){ + printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3)); + i+=4; + } + + printk("\n TX frame descriptor list:\n"); + + for(i=(unsigned int*)priv->tx_fdalist_vp; + i<((unsigned int*)(priv->tx_fdalist_vp+TX_NUM_FDESC));){ + printk("%#8x %#8x %#8x %#8x\n",*i,*(i+1),*(i+2),*(i+3)); + i+=4; + } + + printk("\ndma ctl=%#x\n",readw(ETHER_DMA_CTL(dev->base_addr))); + printk("txfrmptr=%#x\n",readw(ETHER_TXFRMPTR(dev->base_addr))); + printk("txthrsh=%#x\n",readw(ETHER_TXTHRSH(dev->base_addr))); + printk("txpollctr=%#x\n",readw(ETHER_TXPOLLCTR(dev->base_addr))); + printk("blfrmptr=%#x\n",readw(ETHER_BLFRMPTR(dev->base_addr))); + printk("rxfragsize=%#x\n",readw(ETHER_RXFRAGSIZE(dev->base_addr))); + printk("tx_int_en=%#x\n",readw(ETHER_INT_EN(dev->base_addr))); + printk("fda_bas=%#x\n",readw(ETHER_FDA_BAS(dev->base_addr))); + printk("fda_lim=%#x\n",readw(ETHER_FDA_LIM(dev->base_addr))); + printk("int_src=%#x\n",readw(ETHER_INT_SRC(dev->base_addr))); + printk("pausecnt=%#x\n",readw(ETHER_PAUSECNT(dev->base_addr))); + printk("rempaucnt=%#x\n",readw(ETHER_REMPAUCNT(dev->base_addr))); + printk("txconfrmstat=%#x\n",readw(ETHER_TXCONFRMSTAT(dev->base_addr))); + printk("mac_ctl=%#x\n",readw(ETHER_MAC_CTL(dev->base_addr))); + printk("arc_ctl=%#x\n",readw(ETHER_ARC_CTL(dev->base_addr))); + printk("tx_ctl=%#x\n",readw(ETHER_TX_CTL(dev->base_addr))); +} +#endif /* ETHER00_DEBUG */ + + +static int ether00_write_phy(struct net_device *dev, short address, short value) +{ + volatile int count = 1024; + writew(value,ETHER_MD_DATA(dev->base_addr)); + writew( ETHER_MD_CA_BUSY_MSK | + ETHER_MD_CA_WR_MSK | + (address & ETHER_MD_CA_ADDR_MSK), + ETHER_MD_CA(dev->base_addr)); + + /* Wait for the command to complete */ + while((readw(ETHER_MD_CA(dev->base_addr)) & ETHER_MD_CA_BUSY_MSK)&&count){ + count--; + } + if (!count){ + printk("Write to phy failed, addr=%#x, data=%#x\n",address, value); + return -EIO; + } + return 0; +} + +static int ether00_read_phy(struct net_device *dev, short address) +{ + volatile int count = 1024; + writew( ETHER_MD_CA_BUSY_MSK | + (address & ETHER_MD_CA_ADDR_MSK), + ETHER_MD_CA(dev->base_addr)); + + /* Wait for the command to complete */ + while((readw(ETHER_MD_CA(dev->base_addr)) & ETHER_MD_CA_BUSY_MSK)&&count){ + count--; + } + if (!count){ + printk(KERN_WARNING "Read from phy timed out\n"); + return -EIO; + } + return readw(ETHER_MD_DATA(dev->base_addr)); +} + +static void ether00_phy_int(int irq_num, void* dev_id, struct pt_regs* regs) +{ + struct net_device* dev=dev_id; + int irq_status; + + irq_status=ether00_read_phy(dev, PHY_IRQ_CONTROL); + + if(irq_status & PHY_IRQ_CONTROL_ANEG_COMP_INT_MSK){ + /* + * Autonegotiation complete on epxa10db. The mac doesn't + * twig if we're in full duplex so we need to check the + * phy status register and configure the mac accordingly + */ + if(ether00_read_phy(dev, PHY_STATUS)&(PHY_STATUS_10T_F_MSK|PHY_STATUS_100_X_F_MSK)){ + int tmp; + tmp=readl(ETHER_MAC_CTL(dev->base_addr)); + writel(tmp|ETHER_MAC_CTL_FULLDUP_MSK,ETHER_MAC_CTL(dev->base_addr)); + } + } + + if(irq_status&PHY_IRQ_CONTROL_LS_CHG_INT_MSK){ + + if(ether00_read_phy(dev, PHY_STATUS)& PHY_STATUS_LINK_MSK){ + /* Link is up */ + netif_carrier_on(dev); + //printk("Carrier on\n"); + }else{ + netif_carrier_off(dev); + //printk("Carrier off\n"); + + } + } + +} + +static void setup_blist_entry(struct sk_buff* skb,struct rx_blist_ent* blist_ent_ptr){ + /* Make the buffer consistent with the cache as the mac is going to write + * directly into it*/ + blist_ent_ptr->fd.FDSystem=(unsigned int)skb; + blist_ent_ptr->bd.BuffData=(char*)__pa(skb->data); + consistent_sync(skb->data,PKT_BUF_SZ,PCI_DMA_FROMDEVICE); + /* align IP on 16 Byte (DMA_CTL set to skip 2 bytes) */ + skb_reserve(skb,2); + blist_ent_ptr->bd.BuffLength=PKT_BUF_SZ-2; + blist_ent_ptr->fd.FDLength=1; + blist_ent_ptr->fd.FDCtl=FDCTL_COWNSFD_MSK; + blist_ent_ptr->bd.BDCtl=BDCTL_COWNSBD_MSK; +} + + +static int ether00_mem_init(struct net_device* dev) +{ + struct net_priv* priv=dev->priv; + struct tx_fda_ent *tx_fd_ptr,*tx_end_ptr; + struct rx_blist_ent* blist_ent_ptr; + int i; + + /* + * Grab a block of on chip SRAM to contain the control stuctures for + * the ethernet MAC. This uncached becuase it needs to be accesses by both + * bus masters (cpu + mac). However, it shouldn't matter too much in terms + * of speed as its on chip memory + */ + priv->dma_data=ioremap_nocache(EXC_SPSRAM_BLOCK0_BASE,EXC_SPSRAM_BLOCK0_SIZE ); + if (!priv->dma_data) + return -ENOMEM; + + priv->rx_fda_ptr=(struct rx_fda_ent*)priv->dma_data; + /* + * Now share it out amongst the Frame descriptors and the buffer list + */ + priv->rx_blist_vp=(struct rx_blist_ent*)((unsigned int)priv->dma_data+RX_NUM_FDESC*sizeof(struct rx_fda_ent)); + + /* + *Initalise the FDA list + */ + /* set ownership to the controller */ + memset(priv->rx_fda_ptr,0x80,RX_NUM_FDESC*sizeof(struct rx_fda_ent)); + + /* + *Initialise the buffer list + */ + blist_ent_ptr=priv->rx_blist_vp; + i=0; + while(blist_ent_ptr<(priv->rx_blist_vp+RX_NUM_BUFF)){ + struct sk_buff *skb; + blist_ent_ptr->fd.FDLength=1; + skb=dev_alloc_skb(PKT_BUF_SZ); + if(skb){ + setup_blist_entry(skb,blist_ent_ptr); + blist_ent_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(blist_ent_ptr+1); + blist_ent_ptr->bd.BDStat=i++; + blist_ent_ptr++; + } + else + { + printk("Failed to initalise buffer list\n"); + } + + } + blist_ent_ptr--; + blist_ent_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(priv->rx_blist_vp); + + priv->tx_fdalist_vp=(struct tx_fda_ent*)(priv->rx_blist_vp+RX_NUM_BUFF); + + /* Initialise the buffers to be a circular list. The mac will then go poll + * the list until it finds a frame ready to transmit */ + tx_end_ptr=priv->tx_fdalist_vp+TX_NUM_FDESC; + for(tx_fd_ptr=priv->tx_fdalist_vp;tx_fd_ptrfd.FDNext=(FDA_DESC*)__dma_pa((tx_fd_ptr+1)); + tx_fd_ptr->fd.FDCtl=1; + tx_fd_ptr->fd.FDStat=0; + tx_fd_ptr->fd.FDLength=1; + + } + /* Change the last FDNext pointer to make a circular list */ + tx_fd_ptr--; + tx_fd_ptr->fd.FDNext=(FDA_DESC*)__dma_pa(priv->tx_fdalist_vp); + + /* Point the device at the chain of Rx and Tx Buffers */ + writel((unsigned int)__dma_pa(priv->rx_fda_ptr),ETHER_FDA_BAS(dev->base_addr)); + writel((RX_NUM_FDESC-1)*sizeof(struct rx_fda_ent),ETHER_FDA_LIM(dev->base_addr)); + writel((unsigned int)__dma_pa(priv->rx_blist_vp),ETHER_BLFRMPTR(dev->base_addr)); + + writel((unsigned int)__dma_pa(priv->tx_fdalist_vp),ETHER_TXFRMPTR(dev->base_addr)); + + return 0; +} + + +void ether00_mem_update(void* dev_id) +{ + struct net_device* dev=dev_id; + struct net_priv* priv=dev->priv; + struct sk_buff* skb; + struct tx_fda_ent *fda_ptr=priv->tx_fdalist_vp; + struct rx_blist_ent* blist_ent_ptr; + unsigned long flags; + + priv->tq_memupdate.sync=0; + //priv->tq_memupdate.list= + priv->memupdate_scheduled=0; + + /* Transmit interrupt */ + while(fda_ptr<(priv->tx_fdalist_vp+TX_NUM_FDESC)){ + if(!(FDCTL_COWNSFD_MSK&fda_ptr->fd.FDCtl) && (ETHER_TX_STAT_COMP_MSK&fda_ptr->fd.FDStat)){ + priv->stats.tx_packets++; + priv->stats.tx_bytes+=fda_ptr->bd.BuffLength; + skb=(struct sk_buff*)fda_ptr->fd.FDSystem; + //printk("%d:txcln:fda=%#x skb=%#x\n",jiffies,fda_ptr,skb); + dev_kfree_skb(skb); + fda_ptr->fd.FDSystem=0; + fda_ptr->fd.FDStat=0; + fda_ptr->fd.FDCtl=0; + } + fda_ptr++; + } + /* Fill in any missing buffers from the received queue */ + spin_lock_irqsave(&priv->rx_lock,flags); + blist_ent_ptr=priv->rx_blist_vp; + while(blist_ent_ptr<(priv->rx_blist_vp+RX_NUM_BUFF)){ + /* fd.FDSystem of 0 indicates we failed to allocate the buffer in the ISR */ + if(!blist_ent_ptr->fd.FDSystem){ + struct sk_buff *skb; + skb=dev_alloc_skb(PKT_BUF_SZ); + blist_ent_ptr->fd.FDSystem=(unsigned int)skb; + if(skb){ + setup_blist_entry(skb,blist_ent_ptr); + } + else + { + break; + } + } + blist_ent_ptr++; + } + spin_unlock_irqrestore(&priv->rx_lock,flags); + if(priv->queue_stopped){ + //printk("%d:cln:start q\n",jiffies); + netif_start_queue(dev); + } + if(priv->rx_disabled){ + //printk("%d:enable_irq\n",jiffies); + priv->rx_disabled=0; + writel(ETHER_RX_CTL_RXEN_MSK,ETHER_RX_CTL(dev->base_addr)); + + } +} + + +static void ether00_int( int irq_num, void* dev_id, struct pt_regs* regs) +{ + struct net_device* dev=dev_id; + struct net_priv* priv=dev->priv; + + unsigned int interruptValue; + + interruptValue=readl(ETHER_INT_SRC(dev->base_addr)); + + //printk("INT_SRC=%x\n",interruptValue); + + if(!(readl(ETHER_INT_SRC(dev->base_addr)) & ETHER_INT_SRC_IRQ_MSK)) + { + return; /* Interrupt wasn't caused by us!! */ + } + + if(readl(ETHER_INT_SRC(dev->base_addr))& + (ETHER_INT_SRC_INTMACRX_MSK | + ETHER_INT_SRC_FDAEX_MSK | + ETHER_INT_SRC_BLEX_MSK)) { + struct rx_blist_ent* blist_ent_ptr; + struct rx_fda_ent* fda_ent_ptr; + struct sk_buff* skb; + + fda_ent_ptr=priv->rx_fda_ptr; + spin_lock(&priv->rx_lock); + while(fda_ent_ptr<(priv->rx_fda_ptr+RX_NUM_FDESC)){ + int result; + + if(!(fda_ent_ptr->fd.FDCtl&FDCTL_COWNSFD_MSK)) + { + /* This frame is ready for processing */ + /*find the corresponding buffer in the bufferlist */ + blist_ent_ptr=priv->rx_blist_vp+fda_ent_ptr->bd.BDStat; + skb=(struct sk_buff*)blist_ent_ptr->fd.FDSystem; + + /* Pass this skb up the stack */ + skb->dev=dev; + skb_put(skb,fda_ent_ptr->fd.FDLength); + skb->protocol=eth_type_trans(skb,dev); + skb->ip_summed=CHECKSUM_UNNECESSARY; + result=netif_rx(skb); + /* Update statistics */ + priv->stats.rx_packets++; + priv->stats.rx_bytes+=fda_ent_ptr->fd.FDLength; + + /* Free the FDA entry */ + fda_ent_ptr->bd.BDStat=0xff; + fda_ent_ptr->fd.FDCtl=FDCTL_COWNSFD_MSK; + + /* Allocate a new skb and point the bd entry to it */ + blist_ent_ptr->fd.FDSystem=0; + skb=dev_alloc_skb(PKT_BUF_SZ); + //printk("allocskb=%#x\n",skb); + if(skb){ + setup_blist_entry(skb,blist_ent_ptr); + + } + else if(!priv->memupdate_scheduled){ + int tmp; + /* There are no buffers at the moment, so schedule */ + /* the background task to sort this out */ + schedule_task(&priv->tq_memupdate); + priv->memupdate_scheduled=1; + printk(KERN_DEBUG "%s:No buffers",dev->name); + /* If this interrupt was due to a lack of buffers then + * we'd better stop the receiver too */ + if(interruptValueÐER_INT_SRC_BLEX_MSK){ + priv->rx_disabled=1; + tmp=readl(ETHER_INT_SRC(dev->base_addr)); + writel(tmp&~ETHER_RX_CTL_RXEN_MSK,ETHER_RX_CTL(dev->base_addr)); + printk(KERN_DEBUG "%s:Halting rx",dev->name); + } + + } + + } + fda_ent_ptr++; + } + spin_unlock(&priv->rx_lock); + + /* Clear the interrupts */ + writel(ETHER_INT_SRC_INTMACRX_MSK | ETHER_INT_SRC_FDAEX_MSK + | ETHER_INT_SRC_BLEX_MSK,ETHER_INT_SRC(dev->base_addr)); + + } + + if(readl(ETHER_INT_SRC(dev->base_addr))ÐER_INT_SRC_INTMACTX_MSK){ + + if(!priv->memupdate_scheduled){ + schedule_task(&priv->tq_memupdate); + priv->memupdate_scheduled=1; + } + /* Clear the interrupt */ + writel(ETHER_INT_SRC_INTMACTX_MSK,ETHER_INT_SRC(dev->base_addr)); + } + + if (readl(ETHER_INT_SRC(dev->base_addr)) & (ETHER_INT_SRC_SWINT_MSK| + ETHER_INT_SRC_INTEARNOT_MSK| + ETHER_INT_SRC_INTLINK_MSK| + ETHER_INT_SRC_INTEXBD_MSK| + ETHER_INT_SRC_INTTXCTLCMP_MSK)) + { + /* + * Not using any of these so they shouldn't happen + * + * In the cased of INTEXBD - if you allocate more + * than 28 decsriptors you may need to think about this + */ + printk("Not using this interrupt\n"); + } + + if (readl(ETHER_INT_SRC(dev->base_addr)) & + (ETHER_INT_SRC_INTSBUS_MSK | + ETHER_INT_SRC_INTNRABT_MSK + |ETHER_INT_SRC_DMPARERR_MSK)) + { + /* + * Hardware errors, we can either ignore them and hope they go away + *or reset the device, I'll try the first for now to see if they happen + */ + printk("Hardware error\n"); + } +} + +static void ether00_setup_ethernet_address(struct net_device* dev) +{ + int tmp; + + dev->addr_len=6; + writew(0,ETHER_ARC_ADR(dev->base_addr)); + writel((dev->dev_addr[0]<<24) | + (dev->dev_addr[1]<<16) | + (dev->dev_addr[2]<<8) | + dev->dev_addr[3], + ETHER_ARC_DATA(dev->base_addr)); + + writew(4,ETHER_ARC_ADR(dev->base_addr)); + tmp=readl(ETHER_ARC_DATA(dev->base_addr)); + tmp&=0xffff; + tmp|=(dev->dev_addr[4]<<24) | (dev->dev_addr[5]<<16); + writel(tmp, ETHER_ARC_DATA(dev->base_addr)); + /* Enable this entry in the ARC */ + + writel(1,ETHER_ARC_ENA(dev->base_addr)); + + return; +} + + +static void ether00_reset(struct net_device *dev) +{ + /* reset the controller */ + writew(ETHER_MAC_CTL_RESET_MSK,ETHER_MAC_CTL(dev->base_addr)); + + /* + * Make sure we're not going to send anything + */ + + writew(ETHER_TX_CTL_TXHALT_MSK,ETHER_TX_CTL(dev->base_addr)); + + /* + * Make sure we're not going to receive anything + */ + writew(ETHER_RX_CTL_RXHALT_MSK,ETHER_RX_CTL(dev->base_addr)); + + /* + * Disable Interrupts for now, and set the burst size to 8 bytes + */ + + writel(ETHER_DMA_CTL_INTMASK_MSK | + ((8 << ETHER_DMA_CTL_DMBURST_OFST) & ETHER_DMA_CTL_DMBURST_MSK) + |(2<base_addr)); + + + /* + * Set TxThrsh - start transmitting a packet after 1514 + * bytes or when a packet is complete, whichever comes first + */ + writew(1514,ETHER_TXTHRSH(dev->base_addr)); + + /* + * Set TxPollCtr. Each cycle is + * 61.44 microseconds with a 33 MHz bus + */ + writew(1,ETHER_TXPOLLCTR(dev->base_addr)); + + /* + * Set Rx_Ctl - Turn off reception and let RxData turn it + * on later + */ + writew(ETHER_RX_CTL_RXHALT_MSK,ETHER_RX_CTL(dev->base_addr)); + +} + + +static void ether00_set_multicast(struct net_device* dev) +{ + int count=dev->mc_count; + + /* Set promiscuous mode if it's asked for. */ + + if (dev->flags&IFF_PROMISC){ + + writew( ETHER_ARC_CTL_COMPEN_MSK | + ETHER_ARC_CTL_BROADACC_MSK | + ETHER_ARC_CTL_GROUPACC_MSK | + ETHER_ARC_CTL_STATIONACC_MSK, + ETHER_ARC_CTL(dev->base_addr)); + return; + } + + /* + * Get all multicast packets if required, or if there are too + * many addresses to fit in hardware + */ + if (dev->flags & IFF_ALLMULTI){ + writew( ETHER_ARC_CTL_COMPEN_MSK | + ETHER_ARC_CTL_GROUPACC_MSK | + ETHER_ARC_CTL_BROADACC_MSK, + ETHER_ARC_CTL(dev->base_addr)); + return; + } + if (dev->mc_count > (ETHER_ARC_SIZE - 1)){ + + printk(KERN_WARNING "Too many multicast addresses for hardware to filter - receiving all multicast packets\n"); + writew( ETHER_ARC_CTL_COMPEN_MSK | + ETHER_ARC_CTL_GROUPACC_MSK | + ETHER_ARC_CTL_BROADACC_MSK, + ETHER_ARC_CTL(dev->base_addr)); + return; + } + + if(dev->mc_count){ + struct dev_mc_list *mc_list_ent=dev->mc_list; + unsigned int temp,i; + DEBUG(printk("mc_count=%d mc_list=%#x\n",dev-> mc_count, dev->mc_list)); + DEBUG(printk("mc addr=%02#x%02x%02x%02x%02x%02x\n", + mc_list_ent->dmi_addr[5], + mc_list_ent->dmi_addr[4], + mc_list_ent->dmi_addr[3], + mc_list_ent->dmi_addr[2], + mc_list_ent->dmi_addr[1], + mc_list_ent->dmi_addr[0]);) + + /* + * The first 6 bytes are the MAC address, so + * don't change them! + */ + writew(4,ETHER_ARC_ADR(dev->base_addr)); + temp=readl(ETHER_ARC_DATA(dev->base_addr)); + temp&=0xffff0000; + + /* Disable the current multicast stuff */ + writel(1,ETHER_ARC_ENA(dev->base_addr)); + + for(;;){ + temp|=mc_list_ent->dmi_addr[1] | + mc_list_ent->dmi_addr[0]<<8; + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + i=readl(ETHER_ARC_ADR(dev->base_addr)); + writew(i+4,ETHER_ARC_ADR(dev->base_addr)); + + temp=mc_list_ent->dmi_addr[5]| + mc_list_ent->dmi_addr[4]<<8 | + mc_list_ent->dmi_addr[3]<<16 | + mc_list_ent->dmi_addr[2]<<24; + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + count--; + if(!mc_list_ent->next || !count){ + break; + } + DEBUG(printk("mc_list_next=%#x\n",mc_list_ent->next);) + mc_list_ent=mc_list_ent->next; + + + i=readl(ETHER_ARC_ADR(dev->base_addr)); + writel(i+4,ETHER_ARC_ADR(dev->base_addr)); + + temp=mc_list_ent->dmi_addr[3]| + mc_list_ent->dmi_addr[2]<<8 | + mc_list_ent->dmi_addr[1]<<16 | + mc_list_ent->dmi_addr[0]<<24; + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + i=readl(ETHER_ARC_ADR(dev->base_addr)); + writel(i+4,ETHER_ARC_ADR(dev->base_addr)); + + temp=mc_list_ent->dmi_addr[4]<<16 | + mc_list_ent->dmi_addr[5]<<24; + + writel(temp,ETHER_ARC_DATA(dev->base_addr)); + + count--; + if(!mc_list_ent->next || !count){ + break; + } + mc_list_ent=mc_list_ent->next; + } + + + if(count) + printk(KERN_WARNING "Multicast list size error\n"); + + + writew( ETHER_ARC_CTL_BROADACC_MSK| + ETHER_ARC_CTL_COMPEN_MSK, + ETHER_ARC_CTL(dev->base_addr)); + + } + + /* enable the active ARC enties */ + writew((1<<(count+2))-1,ETHER_ARC_ENA(dev->base_addr)); +} + + +static int ether00_open(struct net_device* dev) +{ + int result,tmp; + struct net_priv* priv; + + if (!is_valid_ether_addr(dev->dev_addr)) + return -EINVAL; + + /* Allocate private memory */ + dev->priv=kmalloc(sizeof(struct net_priv),GFP_KERNEL); + if(!dev->priv) + return -ENOMEM; + memset(dev->priv,0,sizeof(struct net_priv)); + priv=(struct net_priv*)dev->priv; + priv->tq_memupdate.routine=ether00_mem_update; + priv->tq_memupdate.data=(void*) dev; + spin_lock_init(&priv->rx_lock); + + /* Install interrupt handlers */ + result=request_irq(dev->irq,ether00_int,0,"ether00",dev); + if(result) + goto open_err1; + + result=request_irq(2,ether00_phy_int,0,"ether00_phy",dev); + if(result) + goto open_err2; + + ether00_reset(dev); + result=ether00_mem_init(dev); + if(result) + goto open_err3; + + + ether00_setup_ethernet_address(dev); + + ether00_set_multicast(dev); + + result=ether00_write_phy(dev,PHY_CONTROL, PHY_CONTROL_ANEGEN_MSK | PHY_CONTROL_RANEG_MSK); + if(result) + goto open_err4; + result=ether00_write_phy(dev,PHY_IRQ_CONTROL, PHY_IRQ_CONTROL_LS_CHG_IE_MSK | + PHY_IRQ_CONTROL_ANEG_COMP_IE_MSK); + if(result) + goto open_err4; + + /* Start the device enable interrupts */ + writew(ETHER_RX_CTL_RXEN_MSK +// | ETHER_RX_CTL_STRIPCRC_MSK + | ETHER_RX_CTL_ENGOOD_MSK + | ETHER_RX_CTL_ENRXPAR_MSK| ETHER_RX_CTL_ENLONGERR_MSK + | ETHER_RX_CTL_ENOVER_MSK| ETHER_RX_CTL_ENCRCERR_MSK, + ETHER_RX_CTL(dev->base_addr)); + + writew(ETHER_TX_CTL_TXEN_MSK| + ETHER_TX_CTL_ENEXDEFER_MSK| + ETHER_TX_CTL_ENLCARR_MSK| + ETHER_TX_CTL_ENEXCOLL_MSK| + ETHER_TX_CTL_ENLATECOLL_MSK| + ETHER_TX_CTL_ENTXPAR_MSK| + ETHER_TX_CTL_ENCOMP_MSK, + ETHER_TX_CTL(dev->base_addr)); + + tmp=readl(ETHER_DMA_CTL(dev->base_addr)); + writel(tmp&~ETHER_DMA_CTL_INTMASK_MSK,ETHER_DMA_CTL(dev->base_addr)); + + return 0; + + open_err4: + ether00_reset(dev); + open_err3: + free_irq(2,dev); + open_err2: + free_irq(dev->irq,dev); + open_err1: + kfree(dev->priv); + return result; + +} + + +static int ether00_tx(struct sk_buff* skb, struct net_device* dev) +{ + struct net_priv *priv=dev->priv; + struct tx_fda_ent *fda_ptr; + int i; + + + /* + * Find an empty slot in which to stick the frame + */ + fda_ptr=(struct tx_fda_ent*)__dma_va(readl(ETHER_TXFRMPTR(dev->base_addr))); + i=0; + while(ifd.FDStat||(fda_ptr->fd.FDCtl & FDCTL_COWNSFD_MSK)){ + fda_ptr =(struct tx_fda_ent*) __dma_va((struct tx_fda_ent*)fda_ptr->fd.FDNext); + } + else { + break; + } + i++; + } + + /* Write the skb data from the cache*/ + consistent_sync(skb->data,skb->len,PCI_DMA_TODEVICE); + fda_ptr->bd.BuffData=(char*)__pa(skb->data); + fda_ptr->bd.BuffLength=(unsigned short)skb->len; + /* Save the pointer to the skb for freeing later */ + fda_ptr->fd.FDSystem=(unsigned int)skb; + fda_ptr->fd.FDStat=0; + /* Pass ownership of the buffers to the controller */ + fda_ptr->fd.FDCtl=1; + fda_ptr->fd.FDCtl|=FDCTL_COWNSFD_MSK; + + /* If the next buffer in the list is full, stop the queue */ + fda_ptr=(struct tx_fda_ent*)__dma_va(fda_ptr->fd.FDNext); + if ((fda_ptr->fd.FDStat)||(fda_ptr->fd.FDCtl & FDCTL_COWNSFD_MSK)){ + netif_stop_queue(dev); + priv->queue_stopped=1; + } + + return 0; +} + +static struct net_device_stats *ether00_stats(struct net_device* dev) +{ + struct net_priv *priv=dev->priv; + return &priv->stats; +} + + +static int ether00_stop(struct net_device* dev) +{ + struct net_priv *priv=dev->priv; + int tmp; + + /* Stop/disable the device. */ + tmp=readw(ETHER_RX_CTL(dev->base_addr)); + tmp&=~(ETHER_RX_CTL_RXEN_MSK | ETHER_RX_CTL_ENGOOD_MSK); + tmp|=ETHER_RX_CTL_RXHALT_MSK; + writew(tmp,ETHER_RX_CTL(dev->base_addr)); + + tmp=readl(ETHER_TX_CTL(dev->base_addr)); + tmp&=~ETHER_TX_CTL_TXEN_MSK; + tmp|=ETHER_TX_CTL_TXHALT_MSK; + writel(tmp,ETHER_TX_CTL(dev->base_addr)); + + /* Free up system resources */ + free_irq(dev->irq,dev); + free_irq(2,dev); + iounmap(priv->dma_data); + kfree(priv); + + return 0; +} + + +static void ether00_get_ethernet_address(struct net_device* dev) +{ + struct mtd_info *mymtd=NULL; + int i; + size_t retlen; + + /* + * For the Epxa10 dev board (camelot), the ethernet MAC + * address is of the form 00:aa:aa:00:xx:xx where + * 00:aa:aa is the Altera vendor ID and xx:xx is the + * last 2 bytes of the board serial number, as programmed + * into the OTP area of the flash device on EBI1. If this + * isn't an expa10 dev board, or there's no mtd support to + * read the serial number from flash then we'll force the + * use to set their own mac address using ifconfig. + */ + +#ifdef CONFIG_ARCH_CAMELOT +#ifdef CONFIG_MTD + /* get the mtd_info structure for the first mtd device*/ + for(i=0;iname,"EPXA10DB flash")) + break; + } + + if(!mymtd || !mymtd->read_user_prot_reg){ + printk(KERN_WARNING "%s: Failed to read MAC address from flash\n",dev->name); + }else{ + mymtd->read_user_prot_reg(mymtd,2,1,&retlen,&dev->dev_addr[5]); + mymtd->read_user_prot_reg(mymtd,3,1,&retlen,&dev->dev_addr[4]); + dev->dev_addr[3]=0; + dev->dev_addr[2]=vendor_id[1]; + dev->dev_addr[1]=vendor_id[0]; + dev->dev_addr[0]=0; + } +#else + printk(KERN_WARNING "%s: MTD support required to read MAC address from EPXA10 dev board\n", dev->name); +#endif +#endif + + if (!is_valid_ether_addr(dev->dev_addr)) + printk("%s: Invalid ethernet MAC address. Please set using " + "ifconfig\n", dev->name); + +} + +static int ether00_init(struct net_device* dev) +{ + + ether_setup(dev); + + dev->open=ether00_open; + dev->stop=ether00_stop; + dev->set_multicast_list=ether00_set_multicast; + dev->hard_start_xmit=ether00_tx; + dev->get_stats=ether00_stats; + + ether00_get_ethernet_address(dev); + + SET_MODULE_OWNER(dev); + return 0; +} + +/* + * Keep a mapping of dev_info addresses -> port lines to use when + * removing ports dev==NULL indicates unused entry + */ + + +static struct net_device* dev_list[ETH_NR]; + +static int ether00_add_device(struct pldhs_dev_info* dev_info,void* dev_ps_data) +{ + struct net_device *dev; + void *map_addr; + int result; + int i; + + + i=0; + while(dev_list[i]) + i++; + + if(i==ETH_NR){ + printk(KERN_WARNING "ether00: Maximum number of ports reached\n"); + return 0; + } + + + dev=kmalloc(sizeof(struct net_device),GFP_KERNEL); + if(!dev){ + return -ENOMEM; + } + memset(dev,0,sizeof(struct net_device)); + map_addr=ioremap_nocache(dev_info->base_addr,SZ_4K); + if(!map_addr){ + return -ENOMEM; + } + + dev->init=ether00_init; + strcpy(dev->name,"eth%d"); + dev->base_addr=(unsigned int)map_addr; + dev->irq=dev_info->irq; + dev->features=NETIF_F_DYNALLOC | NETIF_F_HW_CSUM; + + if(check_mem_region((unsigned int)map_addr, MAC_REG_SIZE)){ + return -EBUSY; + } + request_mem_region((unsigned int)map_addr, MAC_REG_SIZE, "ether00"); + + result=register_netdev(dev); + if(result){ + printk("Ether00: Error %i registering driver\n",result); + return result; + } + printk("registered ether00 device at %#x\n",dev_info->base_addr); + + dev_list[i]=dev; + + return result; +} + + +static int ether00_remove_devices(void) +{ + int i; + + for(i=0;ibase_addr); + release_mem_region(dev_list[i]->base_addr, MAC_REG_SIZE); + kfree(dev_list[i]); + dev_list[i]=0; + } + } + return 0; +} + +static struct pld_hotswap_ops ether00_pldhs_ops={ + name: ETHER00_NAME, + add_device: ether00_add_device, + remove_devices: ether00_remove_devices, +}; + + +static void __exit ether00_cleanup_module(void) +{ + int result; + result=ether00_remove_devices(); + if(result) + printk(KERN_WARNING "ether00: failed to remove all devices\n"); + + pldhs_unregister_driver(ETHER00_NAME); +} +module_exit(ether00_cleanup_module); + + +static int __init ether00_mod_init(void) +{ + printk("mod init\n"); + return pldhs_register_driver(ðer00_pldhs_ops); + +} + +module_init(ether00_mod_init); + diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c new file mode 100644 index 000000000000..758ff8bb8231 --- /dev/null +++ b/drivers/net/arm/ether1.c @@ -0,0 +1,1105 @@ +/* + * linux/drivers/acorn/net/ether1.c + * + * Copyright (C) 1996-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Acorn ether1 driver (82586 chip) for Acorn machines + * + * We basically keep two queues in the cards memory - one for transmit + * and one for receive. Each has a head and a tail. The head is where + * we/the chip adds packets to be transmitted/received, and the tail + * is where the transmitter has got to/where the receiver will stop. + * Both of these queues are circular, and since the chip is running + * all the time, we have to be careful when we modify the pointers etc + * so that the buffer memory contents is valid all the time. + * + * Change log: + * 1.00 RMK Released + * 1.01 RMK 19/03/1996 Transfers the last odd byte onto/off of the card now. + * 1.02 RMK 25/05/1997 Added code to restart RU if it goes not ready + * 1.03 RMK 14/09/1997 Cleaned up the handling of a reset during the TX interrupt. + * Should prevent lockup. + * 1.04 RMK 17/09/1997 Added more info when initialsation of chip goes wrong. + * TDR now only reports failure when chip reports non-zero + * TDR time-distance. + * 1.05 RMK 31/12/1997 Removed calls to dev_tint for 2.1 + * 1.06 RMK 10/02/2000 Updated for 2.3.43 + * 1.07 RMK 13/05/2000 Updated for 2.3.99-pre8 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define __ETHER1_C +#include "ether1.h" + +static unsigned int net_debug = NET_DEBUG; + +#define BUFFER_SIZE 0x10000 +#define TX_AREA_START 0x00100 +#define TX_AREA_END 0x05000 +#define RX_AREA_START 0x05000 +#define RX_AREA_END 0x0fc00 + +static int ether1_open(struct net_device *dev); +static int ether1_sendpacket(struct sk_buff *skb, struct net_device *dev); +static irqreturn_t ether1_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int ether1_close(struct net_device *dev); +static struct net_device_stats *ether1_getstats(struct net_device *dev); +static void ether1_setmulticastlist(struct net_device *dev); +static void ether1_timeout(struct net_device *dev); + +/* ------------------------------------------------------------------------- */ + +static char version[] __initdata = "ether1 ethernet driver (c) 2000 Russell King v1.07\n"; + +#define BUS_16 16 +#define BUS_8 8 + +/* ------------------------------------------------------------------------- */ + +#define DISABLEIRQS 1 +#define NORMALIRQS 0 + +#define ether1_inw(dev, addr, type, offset, svflgs) ether1_inw_p (dev, addr + (int)(&((type *)0)->offset), svflgs) +#define ether1_outw(dev, val, addr, type, offset, svflgs) ether1_outw_p (dev, val, addr + (int)(&((type *)0)->offset), svflgs) + +static inline unsigned short +ether1_inw_p (struct net_device *dev, int addr, int svflgs) +{ + unsigned long flags; + unsigned short ret; + + if (svflgs) + local_irq_save (flags); + + outb (addr >> 12, REG_PAGE); + ret = inw (ETHER1_RAM + ((addr & 4095) >> 1)); + if (svflgs) + local_irq_restore (flags); + return ret; +} + +static inline void +ether1_outw_p (struct net_device *dev, unsigned short val, int addr, int svflgs) +{ + unsigned long flags; + + if (svflgs) + local_irq_save (flags); + + outb (addr >> 12, REG_PAGE); + outw (val, ETHER1_RAM + ((addr & 4095) >> 1)); + if (svflgs) + local_irq_restore (flags); +} + +/* + * Some inline assembler to allow fast transfers on to/off of the card. + * Since this driver depends on some features presented by the ARM + * specific architecture, and that you can't configure this driver + * without specifiing ARM mode, this is not a problem. + * + * This routine is essentially an optimised memcpy from the card's + * onboard RAM to kernel memory. + */ +static void +ether1_writebuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) +{ + unsigned int page, thislen, offset, addr; + + offset = start & 4095; + page = start >> 12; + addr = ioaddr(ETHER1_RAM + (offset >> 1)); + + if (offset + length > 4096) + thislen = 4096 - offset; + else + thislen = length; + + do { + int used; + + outb(page, REG_PAGE); + length -= thislen; + + __asm__ __volatile__( + "subs %3, %3, #2 + bmi 2f +1: ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bmi 2f + ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bmi 2f + ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bmi 2f + ldr %0, [%1], #2 + mov %0, %0, lsl #16 + orr %0, %0, %0, lsr #16 + str %0, [%2], #4 + subs %3, %3, #2 + bpl 1b +2: adds %3, %3, #1 + ldreqb %0, [%1] + streqb %0, [%2]" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + + addr = ioaddr(ETHER1_RAM); + + thislen = length; + if (thislen > 4096) + thislen = 4096; + page++; + } while (thislen); +} + +static void +ether1_readbuffer (struct net_device *dev, void *data, unsigned int start, unsigned int length) +{ + unsigned int page, thislen, offset, addr; + + offset = start & 4095; + page = start >> 12; + addr = ioaddr(ETHER1_RAM + (offset >> 1)); + + if (offset + length > 4096) + thislen = 4096 - offset; + else + thislen = length; + + do { + int used; + + outb(page, REG_PAGE); + length -= thislen; + + __asm__ __volatile__( + "subs %3, %3, #2 + bmi 2f +1: ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bmi 2f + ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bmi 2f + ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bmi 2f + ldr %0, [%2], #4 + strb %0, [%1], #1 + mov %0, %0, lsr #8 + strb %0, [%1], #1 + subs %3, %3, #2 + bpl 1b +2: adds %3, %3, #1 + ldreqb %0, [%2] + streqb %0, [%1]" + : "=&r" (used), "=&r" (data) + : "r" (addr), "r" (thislen), "1" (data)); + + addr = ioaddr(ETHER1_RAM); + + thislen = length; + if (thislen > 4096) + thislen = 4096; + page++; + } while (thislen); +} + +static int __init +ether1_ramtest(struct net_device *dev, unsigned char byte) +{ + unsigned char *buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL); + int i, ret = BUFFER_SIZE; + int max_errors = 15; + int bad = -1; + int bad_start = 0; + + if (!buffer) + return 1; + + memset (buffer, byte, BUFFER_SIZE); + ether1_writebuffer (dev, buffer, 0, BUFFER_SIZE); + memset (buffer, byte ^ 0xff, BUFFER_SIZE); + ether1_readbuffer (dev, buffer, 0, BUFFER_SIZE); + + for (i = 0; i < BUFFER_SIZE; i++) { + if (buffer[i] != byte) { + if (max_errors >= 0 && bad != buffer[i]) { + if (bad != -1) + printk ("\n"); + printk (KERN_CRIT "%s: RAM failed with (%02X instead of %02X) at 0x%04X", + dev->name, buffer[i], byte, i); + ret = -ENODEV; + max_errors --; + bad = buffer[i]; + bad_start = i; + } + } else { + if (bad != -1) { + if (bad_start == i - 1) + printk ("\n"); + else + printk (" - 0x%04X\n", i - 1); + bad = -1; + } + } + } + + if (bad != -1) + printk (" - 0x%04X\n", BUFFER_SIZE); + kfree (buffer); + + return ret; +} + +static int +ether1_reset (struct net_device *dev) +{ + outb (CTRL_RST|CTRL_ACK, REG_CONTROL); + return BUS_16; +} + +static int __init +ether1_init_2(struct net_device *dev) +{ + int i; + dev->mem_start = 0; + + i = ether1_ramtest (dev, 0x5a); + + if (i > 0) + i = ether1_ramtest (dev, 0x1e); + + if (i <= 0) + return -ENODEV; + + dev->mem_end = i; + return 0; +} + +/* + * These are the structures that are loaded into the ether RAM card to + * initialise the 82586 + */ + +/* at 0x0100 */ +#define NOP_ADDR (TX_AREA_START) +#define NOP_SIZE (0x06) +static nop_t init_nop = { + 0, + CMD_NOP, + NOP_ADDR +}; + +/* at 0x003a */ +#define TDR_ADDR (0x003a) +#define TDR_SIZE (0x08) +static tdr_t init_tdr = { + 0, + CMD_TDR | CMD_INTR, + NOP_ADDR, + 0 +}; + +/* at 0x002e */ +#define MC_ADDR (0x002e) +#define MC_SIZE (0x0c) +static mc_t init_mc = { + 0, + CMD_SETMULTICAST, + TDR_ADDR, + 0, + { { 0, } } +}; + +/* at 0x0022 */ +#define SA_ADDR (0x0022) +#define SA_SIZE (0x0c) +static sa_t init_sa = { + 0, + CMD_SETADDRESS, + MC_ADDR, + { 0, } +}; + +/* at 0x0010 */ +#define CFG_ADDR (0x0010) +#define CFG_SIZE (0x12) +static cfg_t init_cfg = { + 0, + CMD_CONFIG, + SA_ADDR, + 8, + 8, + CFG8_SRDY, + CFG9_PREAMB8 | CFG9_ADDRLENBUF | CFG9_ADDRLEN(6), + 0, + 0x60, + 0, + CFG13_RETRY(15) | CFG13_SLOTH(2), + 0, +}; + +/* at 0x0000 */ +#define SCB_ADDR (0x0000) +#define SCB_SIZE (0x10) +static scb_t init_scb = { + 0, + SCB_CMDACKRNR | SCB_CMDACKCNA | SCB_CMDACKFR | SCB_CMDACKCX, + CFG_ADDR, + RX_AREA_START, + 0, + 0, + 0, + 0 +}; + +/* at 0xffee */ +#define ISCP_ADDR (0xffee) +#define ISCP_SIZE (0x08) +static iscp_t init_iscp = { + 1, + SCB_ADDR, + 0x0000, + 0x0000 +}; + +/* at 0xfff6 */ +#define SCP_ADDR (0xfff6) +#define SCP_SIZE (0x0a) +static scp_t init_scp = { + SCP_SY_16BBUS, + { 0, 0 }, + ISCP_ADDR, + 0 +}; + +#define RFD_SIZE (0x16) +static rfd_t init_rfd = { + 0, + 0, + 0, + 0, + { 0, }, + { 0, }, + 0 +}; + +#define RBD_SIZE (0x0a) +static rbd_t init_rbd = { + 0, + 0, + 0, + 0, + ETH_FRAME_LEN + 8 +}; + +#define TX_SIZE (0x08) +#define TBD_SIZE (0x08) + +static int +ether1_init_for_open (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int i, status, addr, next, next2; + int failures = 0; + + outb (CTRL_RST|CTRL_ACK, REG_CONTROL); + + for (i = 0; i < 6; i++) + init_sa.sa_addr[i] = dev->dev_addr[i]; + + /* load data structures into ether1 RAM */ + ether1_writebuffer (dev, &init_scp, SCP_ADDR, SCP_SIZE); + ether1_writebuffer (dev, &init_iscp, ISCP_ADDR, ISCP_SIZE); + ether1_writebuffer (dev, &init_scb, SCB_ADDR, SCB_SIZE); + ether1_writebuffer (dev, &init_cfg, CFG_ADDR, CFG_SIZE); + ether1_writebuffer (dev, &init_sa, SA_ADDR, SA_SIZE); + ether1_writebuffer (dev, &init_mc, MC_ADDR, MC_SIZE); + ether1_writebuffer (dev, &init_tdr, TDR_ADDR, TDR_SIZE); + ether1_writebuffer (dev, &init_nop, NOP_ADDR, NOP_SIZE); + + if (ether1_inw (dev, CFG_ADDR, cfg_t, cfg_command, NORMALIRQS) != CMD_CONFIG) { + printk (KERN_ERR "%s: detected either RAM fault or compiler bug\n", + dev->name); + return 1; + } + + /* + * setup circularly linked list of { rfd, rbd, buffer }, with + * all rfds circularly linked, rbds circularly linked. + * First rfd is linked to scp, first rbd is linked to first + * rfd. Last rbd has a suspend command. + */ + addr = RX_AREA_START; + do { + next = addr + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; + next2 = next + RFD_SIZE + RBD_SIZE + ETH_FRAME_LEN + 10; + + if (next2 >= RX_AREA_END) { + next = RX_AREA_START; + init_rfd.rfd_command = RFD_CMDEL | RFD_CMDSUSPEND; + priv->rx_tail = addr; + } else + init_rfd.rfd_command = 0; + if (addr == RX_AREA_START) + init_rfd.rfd_rbdoffset = addr + RFD_SIZE; + else + init_rfd.rfd_rbdoffset = 0; + init_rfd.rfd_link = next; + init_rbd.rbd_link = next + RFD_SIZE; + init_rbd.rbd_bufl = addr + RFD_SIZE + RBD_SIZE; + + ether1_writebuffer (dev, &init_rfd, addr, RFD_SIZE); + ether1_writebuffer (dev, &init_rbd, addr + RFD_SIZE, RBD_SIZE); + addr = next; + } while (next2 < RX_AREA_END); + + priv->tx_link = NOP_ADDR; + priv->tx_head = NOP_ADDR + NOP_SIZE; + priv->tx_tail = TDR_ADDR; + priv->rx_head = RX_AREA_START; + + /* release reset & give 586 a prod */ + priv->resetting = 1; + priv->initialising = 1; + outb (CTRL_RST, REG_CONTROL); + outb (0, REG_CONTROL); + outb (CTRL_CA, REG_CONTROL); + + /* 586 should now unset iscp.busy */ + i = jiffies + HZ/2; + while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) { + if (time_after(jiffies, i)) { + printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name); + return 1; + } + } + + /* check status of commands that we issued */ + i += HZ/10; + while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, i)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: config status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + i += HZ/10; + while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, i)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: set address status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + i += HZ/10; + while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, i)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't initialise 82586: set multicast status %04X\n", dev->name, status); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + failures += 1; + } + + i += HZ; + while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) + & STAT_COMPLETE) == 0) { + if (time_after(jiffies, i)) + break; + } + + if ((status & (STAT_COMPLETE | STAT_OK)) != (STAT_COMPLETE | STAT_OK)) { + printk (KERN_WARNING "%s: can't tdr (ignored)\n", dev->name); + printk (KERN_DEBUG "%s: SCB=[STS=%04X CMD=%04X CBL=%04X RFA=%04X]\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_command, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS), + ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, NORMALIRQS)); + } else { + status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_result, DISABLEIRQS); + if (status & TDR_XCVRPROB) + printk (KERN_WARNING "%s: i/f failed tdr: transceiver problem\n", dev->name); + else if ((status & (TDR_SHORT|TDR_OPEN)) && (status & TDR_TIME)) { +#ifdef FANCY + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d.%d us away\n", dev->name, + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME) / 10, + (status & TDR_TIME) % 10); +#else + printk (KERN_WARNING "%s: i/f failed tdr: cable %s %d clks away\n", dev->name, + status & TDR_SHORT ? "short" : "open", (status & TDR_TIME)); +#endif + } + } + + if (failures) + ether1_reset (dev); + return failures ? 1 : 0; +} + +/* ------------------------------------------------------------------------- */ + +static int +ether1_txalloc (struct net_device *dev, int size) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int start, tail; + + size = (size + 1) & ~1; + tail = priv->tx_tail; + + if (priv->tx_head + size > TX_AREA_END) { + if (tail > priv->tx_head) + return -1; + start = TX_AREA_START; + if (start + size > tail) + return -1; + priv->tx_head = start + size; + } else { + if (priv->tx_head < tail && (priv->tx_head + size) > tail) + return -1; + start = priv->tx_head; + priv->tx_head += size; + } + + return start; +} + +static int +ether1_open (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_WARNING "%s: invalid ethernet MAC address\n", + dev->name); + return -EINVAL; + } + + if (request_irq(dev->irq, ether1_interrupt, 0, "ether1", dev)) + return -EAGAIN; + + memset (&priv->stats, 0, sizeof (struct net_device_stats)); + + if (ether1_init_for_open (dev)) { + free_irq (dev->irq, dev); + return -EAGAIN; + } + + netif_start_queue(dev); + + return 0; +} + +static void +ether1_timeout(struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + + printk(KERN_WARNING "%s: transmit timeout, network cable problem?\n", + dev->name); + printk(KERN_WARNING "%s: resetting device\n", dev->name); + + ether1_reset (dev); + + if (ether1_init_for_open (dev)) + printk (KERN_ERR "%s: unable to restart interface\n", dev->name); + + priv->stats.tx_errors++; + netif_wake_queue(dev); +} + +static int +ether1_sendpacket (struct sk_buff *skb, struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + int tmp, tst, nopaddr, txaddr, tbdaddr, dataddr; + unsigned long flags; + tx_t tx; + tbd_t tbd; + nop_t nop; + + if (priv->restart) { + printk(KERN_WARNING "%s: resetting device\n", dev->name); + + ether1_reset(dev); + + if (ether1_init_for_open(dev)) + printk(KERN_ERR "%s: unable to restart interface\n", dev->name); + else + priv->restart = 0; + } + + /* + * insert packet followed by a nop + */ + txaddr = ether1_txalloc (dev, TX_SIZE); + tbdaddr = ether1_txalloc (dev, TBD_SIZE); + dataddr = ether1_txalloc (dev, len); + nopaddr = ether1_txalloc (dev, NOP_SIZE); + + tx.tx_status = 0; + tx.tx_command = CMD_TX | CMD_INTR; + tx.tx_link = nopaddr; + tx.tx_tbdoffset = tbdaddr; + tbd.tbd_opts = TBD_EOL | len; + tbd.tbd_link = I82586_NULL; + tbd.tbd_bufl = dataddr; + tbd.tbd_bufh = 0; + nop.nop_status = 0; + nop.nop_command = CMD_NOP; + nop.nop_link = nopaddr; + + local_irq_save(flags); + ether1_writebuffer (dev, &tx, txaddr, TX_SIZE); + ether1_writebuffer (dev, &tbd, tbdaddr, TBD_SIZE); + ether1_writebuffer (dev, skb->data, dataddr, len); + ether1_writebuffer (dev, &nop, nopaddr, NOP_SIZE); + tmp = priv->tx_link; + priv->tx_link = nopaddr; + + /* now reset the previous nop pointer */ + ether1_outw (dev, txaddr, tmp, nop_t, nop_link, NORMALIRQS); + + local_irq_restore(flags); + + /* handle transmit */ + dev->trans_start = jiffies; + + /* check to see if we have room for a full sized ether frame */ + tmp = priv->tx_head; + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); + priv->tx_head = tmp; + dev_kfree_skb (skb); + + if (tst == -1) + netif_stop_queue(dev); + + return 0; +} + +static void +ether1_xmit_done (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + nop_t nop; + int caddr, tst; + + caddr = priv->tx_tail; + +again: + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + + switch (nop.nop_command & CMD_MASK) { + case CMD_TDR: + /* special case */ + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) + != (unsigned short)I82586_NULL) { + ether1_outw(dev, SCB_CMDCUCSTART | SCB_CMDRXSTART, SCB_ADDR, scb_t, + scb_command, NORMALIRQS); + outb (CTRL_CA, REG_CONTROL); + } + priv->tx_tail = NOP_ADDR; + return; + + case CMD_NOP: + if (nop.nop_link == caddr) { + if (priv->initialising == 0) + printk (KERN_WARNING "%s: strange command complete with no tx command!\n", dev->name); + else + priv->initialising = 0; + return; + } + if (caddr == nop.nop_link) + return; + caddr = nop.nop_link; + goto again; + + case CMD_TX: + if (nop.nop_status & STAT_COMPLETE) + break; + printk (KERN_ERR "%s: strange command complete without completed command\n", dev->name); + priv->restart = 1; + return; + + default: + printk (KERN_WARNING "%s: strange command %d complete! (offset %04X)", dev->name, + nop.nop_command & CMD_MASK, caddr); + priv->restart = 1; + return; + } + + while (nop.nop_status & STAT_COMPLETE) { + if (nop.nop_status & STAT_OK) { + priv->stats.tx_packets ++; + priv->stats.collisions += (nop.nop_status & STAT_COLLISIONS); + } else { + priv->stats.tx_errors ++; + + if (nop.nop_status & STAT_COLLAFTERTX) + priv->stats.collisions ++; + if (nop.nop_status & STAT_NOCARRIER) + priv->stats.tx_carrier_errors ++; + if (nop.nop_status & STAT_TXLOSTCTS) + printk (KERN_WARNING "%s: cts lost\n", dev->name); + if (nop.nop_status & STAT_TXSLOWDMA) + priv->stats.tx_fifo_errors ++; + if (nop.nop_status & STAT_COLLEXCESSIVE) + priv->stats.collisions += 16; + } + + if (nop.nop_link == caddr) { + printk (KERN_ERR "%s: tx buffer chaining error: tx command points to itself\n", dev->name); + break; + } + + caddr = nop.nop_link; + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + if ((nop.nop_command & CMD_MASK) != CMD_NOP) { + printk (KERN_ERR "%s: tx buffer chaining error: no nop after tx command\n", dev->name); + break; + } + + if (caddr == nop.nop_link) + break; + + caddr = nop.nop_link; + ether1_readbuffer (dev, &nop, caddr, NOP_SIZE); + if ((nop.nop_command & CMD_MASK) != CMD_TX) { + printk (KERN_ERR "%s: tx buffer chaining error: no tx command after nop\n", dev->name); + break; + } + } + priv->tx_tail = caddr; + + caddr = priv->tx_head; + tst = ether1_txalloc (dev, TX_SIZE + TBD_SIZE + NOP_SIZE + ETH_FRAME_LEN); + priv->tx_head = caddr; + if (tst != -1) + netif_wake_queue(dev); +} + +static void +ether1_recv_done (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int status; + int nexttail, rbdaddr; + rbd_t rbd; + + do { + status = ether1_inw (dev, priv->rx_head, rfd_t, rfd_status, NORMALIRQS); + if ((status & RFD_COMPLETE) == 0) + break; + + rbdaddr = ether1_inw (dev, priv->rx_head, rfd_t, rfd_rbdoffset, NORMALIRQS); + ether1_readbuffer (dev, &rbd, rbdaddr, RBD_SIZE); + + if ((rbd.rbd_status & (RBD_EOF | RBD_ACNTVALID)) == (RBD_EOF | RBD_ACNTVALID)) { + int length = rbd.rbd_status & RBD_ACNT; + struct sk_buff *skb; + + length = (length + 1) & ~1; + skb = dev_alloc_skb (length + 2); + + if (skb) { + skb->dev = dev; + skb_reserve (skb, 2); + + ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); + + skb->protocol = eth_type_trans (skb, dev); + netif_rx (skb); + priv->stats.rx_packets ++; + } else + priv->stats.rx_dropped ++; + } else { + printk(KERN_WARNING "%s: %s\n", dev->name, + (rbd.rbd_status & RBD_EOF) ? "oversized packet" : "acnt not valid"); + priv->stats.rx_dropped ++; + } + + nexttail = ether1_inw (dev, priv->rx_tail, rfd_t, rfd_link, NORMALIRQS); + /* nexttail should be rx_head */ + if (nexttail != priv->rx_head) + printk(KERN_ERR "%s: receiver buffer chaining error (%04X != %04X)\n", + dev->name, nexttail, priv->rx_head); + ether1_outw (dev, RFD_CMDEL | RFD_CMDSUSPEND, nexttail, rfd_t, rfd_command, NORMALIRQS); + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_command, NORMALIRQS); + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_status, NORMALIRQS); + ether1_outw (dev, 0, priv->rx_tail, rfd_t, rfd_rbdoffset, NORMALIRQS); + + priv->rx_tail = nexttail; + priv->rx_head = ether1_inw (dev, priv->rx_head, rfd_t, rfd_link, NORMALIRQS); + } while (1); +} + +static irqreturn_t +ether1_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + int status; + + status = ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS); + + if (status) { + ether1_outw(dev, status & (SCB_STRNR | SCB_STCNA | SCB_STFR | SCB_STCX), + SCB_ADDR, scb_t, scb_command, NORMALIRQS); + outb (CTRL_CA | CTRL_ACK, REG_CONTROL); + if (status & SCB_STCX) { + ether1_xmit_done (dev); + } + if (status & SCB_STCNA) { + if (priv->resetting == 0) + printk (KERN_WARNING "%s: CU went not ready ???\n", dev->name); + else + priv->resetting += 1; + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_cbl_offset, NORMALIRQS) + != (unsigned short)I82586_NULL) { + ether1_outw (dev, SCB_CMDCUCSTART, SCB_ADDR, scb_t, scb_command, NORMALIRQS); + outb (CTRL_CA, REG_CONTROL); + } + if (priv->resetting == 2) + priv->resetting = 0; + } + if (status & SCB_STFR) { + ether1_recv_done (dev); + } + if (status & SCB_STRNR) { + if (ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS) & SCB_STRXSUSP) { + printk (KERN_WARNING "%s: RU went not ready: RU suspended\n", dev->name); + ether1_outw (dev, SCB_CMDRXRESUME, SCB_ADDR, scb_t, scb_command, NORMALIRQS); + outb (CTRL_CA, REG_CONTROL); + priv->stats.rx_dropped ++; /* we suspended due to lack of buffer space */ + } else + printk(KERN_WARNING "%s: RU went not ready: %04X\n", dev->name, + ether1_inw (dev, SCB_ADDR, scb_t, scb_status, NORMALIRQS)); + printk (KERN_WARNING "RU ptr = %04X\n", ether1_inw (dev, SCB_ADDR, scb_t, scb_rfa_offset, + NORMALIRQS)); + } + } else + outb (CTRL_ACK, REG_CONTROL); + + return IRQ_HANDLED; +} + +static int +ether1_close (struct net_device *dev) +{ + ether1_reset (dev); + + free_irq(dev->irq, dev); + + return 0; +} + +static struct net_device_stats * +ether1_getstats (struct net_device *dev) +{ + struct ether1_priv *priv = (struct ether1_priv *)dev->priv; + return &priv->stats; +} + +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets. + * num_addrs == 0 Normal mode, clear multicast list. + * num_addrs > 0 Multicast mode, receive normal and MC packets, and do + * best-effort filtering. + */ +static void +ether1_setmulticastlist (struct net_device *dev) +{ +} + +/* ------------------------------------------------------------------------- */ + +static void __init ether1_banner(void) +{ + static unsigned int version_printed = 0; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static int __devinit +ether1_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct net_device *dev; + struct ether1_priv *priv; + int i, ret = 0; + + ether1_banner(); + + dev = init_etherdev(NULL, sizeof(struct ether1_priv)); + if (!dev) { + ret = -ENOMEM; + goto out; + } + + SET_MODULE_OWNER(dev); + + dev->base_addr = ecard_address(ec, ECARD_IOC, ECARD_FAST); + dev->irq = ec->irq; + + /* + * these will not fail - the nature of the bus ensures this + */ + request_region(dev->base_addr, 16, dev->name); + request_region(dev->base_addr + 0x800, 4096, dev->name); + + priv = (struct ether1_priv *)dev->priv; + if ((priv->bus_type = ether1_reset(dev)) == 0) { + ret = -ENODEV; + goto release; + } + + printk(KERN_INFO "%s: ether1 in slot %d, ", + dev->name, ec->slot_no); + + for (i = 0; i < 6; i++) { + dev->dev_addr[i] = inb(IDPROM_ADDRESS + i); + printk ("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + } + + if (ether1_init_2(dev)) { + ret = -ENODEV; + goto release; + } + + dev->open = ether1_open; + dev->stop = ether1_close; + dev->hard_start_xmit = ether1_sendpacket; + dev->get_stats = ether1_getstats; + dev->set_multicast_list = ether1_setmulticastlist; + dev->tx_timeout = ether1_timeout; + dev->watchdog_timeo = 5 * HZ / 100; + + ecard_set_drvdata(ec, dev); + return 0; + +release: + release_region(dev->base_addr, 16); + release_region(dev->base_addr + 0x800, 4096); + unregister_netdev(dev); + kfree(dev); +out: + return ret; +} + +static void __devexit ether1_remove(struct expansion_card *ec) +{ + struct net_device *dev = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + unregister_netdev(dev); + + release_region(dev->base_addr, 16); + release_region(dev->base_addr + 0x800, 4096); + kfree(dev); +} + +static const struct ecard_id ether1_ids[] = { + { MANU_ACORN, PROD_ACORN_ETHER1 }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver ether1_driver = { + .probe = ether1_probe, + .remove = __devexit_p(ether1_remove), + .id_table = ether1_ids, + .drv = { + .name = "ether1", + }, +}; + +static int __init ether1_init(void) +{ + return ecard_register_driver(ðer1_driver); +} + +static void __exit ether1_exit(void) +{ + ecard_remove_driver(ðer1_driver); +} + +module_init(ether1_init); +module_exit(ether1_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arm/ether1.h b/drivers/net/arm/ether1.h new file mode 100644 index 000000000000..790bb97cac29 --- /dev/null +++ b/drivers/net/arm/ether1.h @@ -0,0 +1,278 @@ +/* + * linux/drivers/acorn/net/ether1.h + * + * Copyright (C) 1996 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Network driver for Acorn Ether1 cards. + */ + +#ifndef _LINUX_ether1_H +#define _LINUX_ether1_H + +#ifdef __ETHER1_C +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* Page register */ +#define REG_PAGE (dev->base_addr + 0x00) + +/* Control register */ +#define REG_CONTROL (dev->base_addr + 0x01) +#define CTRL_RST 0x01 +#define CTRL_LOOPBACK 0x02 +#define CTRL_CA 0x04 +#define CTRL_ACK 0x08 + +#define ETHER1_RAM (dev->base_addr + 0x800) + +/* HW address */ +#define IDPROM_ADDRESS (dev->base_addr + 0x09) + +struct ether1_priv { + struct net_device_stats stats; + unsigned int tx_link; + unsigned int tx_head; + volatile unsigned int tx_tail; + volatile unsigned int rx_head; + volatile unsigned int rx_tail; + unsigned char bus_type; + unsigned char resetting; + unsigned char initialising : 1; + unsigned char restart : 1; +}; + +#define I82586_NULL (-1) + +typedef struct { /* tdr */ + unsigned short tdr_status; + unsigned short tdr_command; + unsigned short tdr_link; + unsigned short tdr_result; +#define TDR_TIME (0x7ff) +#define TDR_SHORT (1 << 12) +#define TDR_OPEN (1 << 13) +#define TDR_XCVRPROB (1 << 14) +#define TDR_LNKOK (1 << 15) +} tdr_t; + +typedef struct { /* transmit */ + unsigned short tx_status; + unsigned short tx_command; + unsigned short tx_link; + unsigned short tx_tbdoffset; +} tx_t; + +typedef struct { /* tbd */ + unsigned short tbd_opts; +#define TBD_CNT (0x3fff) +#define TBD_EOL (1 << 15) + unsigned short tbd_link; + unsigned short tbd_bufl; + unsigned short tbd_bufh; +} tbd_t; + +typedef struct { /* rfd */ + unsigned short rfd_status; +#define RFD_NOEOF (1 << 6) +#define RFD_FRAMESHORT (1 << 7) +#define RFD_DMAOVRN (1 << 8) +#define RFD_NORESOURCES (1 << 9) +#define RFD_ALIGNERROR (1 << 10) +#define RFD_CRCERROR (1 << 11) +#define RFD_OK (1 << 13) +#define RFD_FDCONSUMED (1 << 14) +#define RFD_COMPLETE (1 << 15) + unsigned short rfd_command; +#define RFD_CMDSUSPEND (1 << 14) +#define RFD_CMDEL (1 << 15) + unsigned short rfd_link; + unsigned short rfd_rbdoffset; + unsigned char rfd_dest[6]; + unsigned char rfd_src[6]; + unsigned short rfd_len; +} rfd_t; + +typedef struct { /* rbd */ + unsigned short rbd_status; +#define RBD_ACNT (0x3fff) +#define RBD_ACNTVALID (1 << 14) +#define RBD_EOF (1 << 15) + unsigned short rbd_link; + unsigned short rbd_bufl; + unsigned short rbd_bufh; + unsigned short rbd_len; +} rbd_t; + +typedef struct { /* nop */ + unsigned short nop_status; + unsigned short nop_command; + unsigned short nop_link; +} nop_t; + +typedef struct { /* set multicast */ + unsigned short mc_status; + unsigned short mc_command; + unsigned short mc_link; + unsigned short mc_cnt; + unsigned char mc_addrs[1][6]; +} mc_t; + +typedef struct { /* set address */ + unsigned short sa_status; + unsigned short sa_command; + unsigned short sa_link; + unsigned char sa_addr[6]; +} sa_t; + +typedef struct { /* config command */ + unsigned short cfg_status; + unsigned short cfg_command; + unsigned short cfg_link; + unsigned char cfg_bytecnt; /* size foll data: 4 - 12 */ + unsigned char cfg_fifolim; /* FIFO threshold */ + unsigned char cfg_byte8; +#define CFG8_SRDY (1 << 6) +#define CFG8_SAVEBADF (1 << 7) + unsigned char cfg_byte9; +#define CFG9_ADDRLEN(x) (x) +#define CFG9_ADDRLENBUF (1 << 3) +#define CFG9_PREAMB2 (0 << 4) +#define CFG9_PREAMB4 (1 << 4) +#define CFG9_PREAMB8 (2 << 4) +#define CFG9_PREAMB16 (3 << 4) +#define CFG9_ILOOPBACK (1 << 6) +#define CFG9_ELOOPBACK (1 << 7) + unsigned char cfg_byte10; +#define CFG10_LINPRI(x) (x) +#define CFG10_ACR(x) (x << 4) +#define CFG10_BOFMET (1 << 7) + unsigned char cfg_ifs; + unsigned char cfg_slotl; + unsigned char cfg_byte13; +#define CFG13_SLOTH(x) (x) +#define CFG13_RETRY(x) (x << 4) + unsigned char cfg_byte14; +#define CFG14_PROMISC (1 << 0) +#define CFG14_DISBRD (1 << 1) +#define CFG14_MANCH (1 << 2) +#define CFG14_TNCRS (1 << 3) +#define CFG14_NOCRC (1 << 4) +#define CFG14_CRC16 (1 << 5) +#define CFG14_BTSTF (1 << 6) +#define CFG14_FLGPAD (1 << 7) + unsigned char cfg_byte15; +#define CFG15_CSTF(x) (x) +#define CFG15_ICSS (1 << 3) +#define CFG15_CDTF(x) (x << 4) +#define CFG15_ICDS (1 << 7) + unsigned short cfg_minfrmlen; +} cfg_t; + +typedef struct { /* scb */ + unsigned short scb_status; /* status of 82586 */ +#define SCB_STRXMASK (7 << 4) /* Receive unit status */ +#define SCB_STRXIDLE (0 << 4) /* Idle */ +#define SCB_STRXSUSP (1 << 4) /* Suspended */ +#define SCB_STRXNRES (2 << 4) /* No resources */ +#define SCB_STRXRDY (4 << 4) /* Ready */ +#define SCB_STCUMASK (7 << 8) /* Command unit status */ +#define SCB_STCUIDLE (0 << 8) /* Idle */ +#define SCB_STCUSUSP (1 << 8) /* Suspended */ +#define SCB_STCUACTV (2 << 8) /* Active */ +#define SCB_STRNR (1 << 12) /* Receive unit not ready */ +#define SCB_STCNA (1 << 13) /* Command unit not ready */ +#define SCB_STFR (1 << 14) /* Frame received */ +#define SCB_STCX (1 << 15) /* Command completed */ + unsigned short scb_command; /* Next command */ +#define SCB_CMDRXSTART (1 << 4) /* Start (at rfa_offset) */ +#define SCB_CMDRXRESUME (2 << 4) /* Resume reception */ +#define SCB_CMDRXSUSPEND (3 << 4) /* Suspend reception */ +#define SCB_CMDRXABORT (4 << 4) /* Abort reception */ +#define SCB_CMDCUCSTART (1 << 8) /* Start (at cbl_offset) */ +#define SCB_CMDCUCRESUME (2 << 8) /* Resume execution */ +#define SCB_CMDCUCSUSPEND (3 << 8) /* Suspend execution */ +#define SCB_CMDCUCABORT (4 << 8) /* Abort execution */ +#define SCB_CMDACKRNR (1 << 12) /* Ack RU not ready */ +#define SCB_CMDACKCNA (1 << 13) /* Ack CU not ready */ +#define SCB_CMDACKFR (1 << 14) /* Ack Frame received */ +#define SCB_CMDACKCX (1 << 15) /* Ack Command complete */ + unsigned short scb_cbl_offset; /* Offset of first command unit */ + unsigned short scb_rfa_offset; /* Offset of first receive frame area */ + unsigned short scb_crc_errors; /* Properly aligned frame with CRC error*/ + unsigned short scb_aln_errors; /* Misaligned frames */ + unsigned short scb_rsc_errors; /* Frames lost due to no space */ + unsigned short scb_ovn_errors; /* Frames lost due to slow bus */ +} scb_t; + +typedef struct { /* iscp */ + unsigned short iscp_busy; /* set by CPU before CA */ + unsigned short iscp_offset; /* offset of SCB */ + unsigned short iscp_basel; /* base of SCB */ + unsigned short iscp_baseh; +} iscp_t; + + /* this address must be 0xfff6 */ +typedef struct { /* scp */ + unsigned short scp_sysbus; /* bus size */ +#define SCP_SY_16BBUS 0x00 +#define SCP_SY_8BBUS 0x01 + unsigned short scp_junk[2]; /* junk */ + unsigned short scp_iscpl; /* lower 16 bits of iscp */ + unsigned short scp_iscph; /* upper 16 bits of iscp */ +} scp_t; + +/* commands */ +#define CMD_NOP 0 +#define CMD_SETADDRESS 1 +#define CMD_CONFIG 2 +#define CMD_SETMULTICAST 3 +#define CMD_TX 4 +#define CMD_TDR 5 +#define CMD_DUMP 6 +#define CMD_DIAGNOSE 7 + +#define CMD_MASK 7 + +#define CMD_INTR (1 << 13) +#define CMD_SUSP (1 << 14) +#define CMD_EOL (1 << 15) + +#define STAT_COLLISIONS (15) +#define STAT_COLLEXCESSIVE (1 << 5) +#define STAT_COLLAFTERTX (1 << 6) +#define STAT_TXDEFERRED (1 << 7) +#define STAT_TXSLOWDMA (1 << 8) +#define STAT_TXLOSTCTS (1 << 9) +#define STAT_NOCARRIER (1 << 10) +#define STAT_FAIL (1 << 11) +#define STAT_ABORTED (1 << 12) +#define STAT_OK (1 << 13) +#define STAT_BUSY (1 << 14) +#define STAT_COMPLETE (1 << 15) +#endif +#endif + +/* + * Ether1 card definitions: + * + * FAST accesses: + * +0 Page register + * 16 pages + * +4 Control + * '1' = reset + * '2' = loopback + * '4' = CA + * '8' = int ack + * + * RAM at address + 0x2000 + * Pod. Prod id = 3 + * Words after ID block [base + 8 words] + * +0 pcb issue (0x0c and 0xf3 invalid) + * +1 - +6 eth hw address + */ diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c new file mode 100644 index 000000000000..2a2e8f6cc948 --- /dev/null +++ b/drivers/net/arm/ether3.c @@ -0,0 +1,947 @@ +/* + * linux/drivers/acorn/net/ether3.c + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * SEEQ nq8005 ethernet driver for Acorn/ANT Ether3 card + * for Acorn machines + * + * By Russell King, with some suggestions from borris@ant.co.uk + * + * Changelog: + * 1.04 RMK 29/02/1996 Won't pass packets that are from our ethernet + * address up to the higher levels - they're + * silently ignored. I/F can now be put into + * multicast mode. Receiver routine optimised. + * 1.05 RMK 30/02/1996 Now claims interrupt at open when part of + * the kernel rather than when a module. + * 1.06 RMK 02/03/1996 Various code cleanups + * 1.07 RMK 13/10/1996 Optimised interrupt routine and transmit + * routines. + * 1.08 RMK 14/10/1996 Fixed problem with too many packets, + * prevented the kernel message about dropped + * packets appearing too many times a second. + * Now does not disable all IRQs, only the IRQ + * used by this card. + * 1.09 RMK 10/11/1996 Only enables TX irq when buffer space is low, + * but we still service the TX queue if we get a + * RX interrupt. + * 1.10 RMK 15/07/1997 Fixed autoprobing of NQ8004. + * 1.11 RMK 16/11/1997 Fixed autoprobing of NQ8005A. + * 1.12 RMK 31/12/1997 Removed reference to dev_tint for Linux 2.1. + * RMK 27/06/1998 Changed asm/delay.h to linux/delay.h. + * 1.13 RMK 29/06/1998 Fixed problem with transmission of packets. + * Chip seems to have a bug in, whereby if the + * packet starts two bytes from the end of the + * buffer, it corrupts the receiver chain, and + * never updates the transmit status correctly. + * 1.14 RMK 07/01/1998 Added initial code for ETHERB addressing. + * 1.15 RMK 30/04/1999 More fixes to the transmit routine for buggy + * hardware. + * 1.16 RMK 10/02/2000 Updated for 2.3.43 + * 1.17 RMK 13/05/2000 Updated for 2.3.99-pre8 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static char version[] __initdata = "ether3 ethernet driver (c) 1995-2000 R.M.King v1.17\n"; + +#include "ether3.h" + +static unsigned int net_debug = NET_DEBUG; + +static void ether3_setmulticastlist(struct net_device *dev); +static int ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt); +static void ether3_tx(struct net_device *dev, struct dev_priv *priv); +static int ether3_open (struct net_device *dev); +static int ether3_sendpacket (struct sk_buff *skb, struct net_device *dev); +static irqreturn_t ether3_interrupt (int irq, void *dev_id, struct pt_regs *regs); +static int ether3_close (struct net_device *dev); +static struct net_device_stats *ether3_getstats (struct net_device *dev); +static void ether3_setmulticastlist (struct net_device *dev); +static void ether3_timeout(struct net_device *dev); + +#define BUS_16 2 +#define BUS_8 1 +#define BUS_UNKNOWN 0 + +/* --------------------------------------------------------------------------- */ + +typedef enum { + buffer_write, + buffer_read +} buffer_rw_t; + +/* + * ether3 read/write. Slow things down a bit... + * The SEEQ8005 doesn't like us writing to its registers + * too quickly. + */ +static inline void ether3_outb(int v, const int r) +{ + outb(v, r); + udelay(1); +} + +static inline void ether3_outw(int v, const int r) +{ + outw(v, r); + udelay(1); +} +#define ether3_inb(r) ({ unsigned int __v = inb((r)); udelay(1); __v; }) +#define ether3_inw(r) ({ unsigned int __v = inw((r)); udelay(1); __v; }) + +static int +ether3_setbuffer(struct net_device *dev, buffer_rw_t read, int start) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int timeout = 1000; + + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); + ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); + + while ((ether3_inw(REG_STATUS) & STAT_FIFOEMPTY) == 0) { + if (!timeout--) { + printk("%s: setbuffer broken\n", dev->name); + priv->broken = 1; + return 1; + } + udelay(1); + } + + if (read == buffer_read) { + ether3_outw(start, REG_DMAADDR); + ether3_outw(priv->regs.command | CMD_FIFOREAD, REG_COMMAND); + } else { + ether3_outw(priv->regs.command | CMD_FIFOWRITE, REG_COMMAND); + ether3_outw(start, REG_DMAADDR); + } + return 0; +} + +/* + * write data to the buffer memory + */ +#define ether3_writebuffer(dev,data,length) \ + outsw(REG_BUFWIN, (data), (length) >> 1) + +#define ether3_writeword(dev,data) \ + outw((data), REG_BUFWIN) + +#define ether3_writelong(dev,data) { \ + unsigned long reg_bufwin = REG_BUFWIN; \ + outw((data), reg_bufwin); \ + outw((data) >> 16, reg_bufwin); \ +} + +/* + * read data from the buffer memory + */ +#define ether3_readbuffer(dev,data,length) \ + insw(REG_BUFWIN, (data), (length) >> 1) + +#define ether3_readword(dev) \ + inw(REG_BUFWIN) + +#define ether3_readlong(dev) \ + inw(REG_BUFWIN) | (inw(REG_BUFWIN) << 16) + +/* + * Switch LED off... + */ +static void +ether3_ledoff(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct dev_priv *priv = (struct dev_priv *)dev->priv; + ether3_outw(priv->regs.config2 |= CFG2_CTRLO, REG_CONFIG2); +} + +/* + * switch LED on... + */ +static inline void +ether3_ledon(struct net_device *dev, struct dev_priv *priv) +{ + del_timer(&priv->timer); + priv->timer.expires = jiffies + HZ / 50; /* leave on for 1/50th second */ + priv->timer.data = (unsigned long)dev; + priv->timer.function = ether3_ledoff; + add_timer(&priv->timer); + if (priv->regs.config2 & CFG2_CTRLO) + ether3_outw(priv->regs.config2 &= ~CFG2_CTRLO, REG_CONFIG2); +} + +/* + * Read the ethernet address string from the on board rom. + * This is an ascii string!!! + */ +static int __init +ether3_addr(char *addr, struct expansion_card *ec) +{ + struct in_chunk_dir cd; + char *s; + + if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { + int i; + for (i = 0; i<6; i++) { + addr[i] = simple_strtoul(s + 1, &s, 0x10); + if (*s != (i==5?')' : ':' )) + break; + } + if (i == 6) + return 0; + } + /* I wonder if we should even let the user continue in this case + * - no, it would be better to disable the device + */ + printk(KERN_ERR "ether3: Couldn't read a valid MAC address from card.\n"); + return -ENODEV; +} + +/* --------------------------------------------------------------------------- */ + +static int __init +ether3_ramtest(struct net_device *dev, unsigned char byte) +{ + unsigned char *buffer = kmalloc(RX_END, GFP_KERNEL); + int i,ret = 0; + int max_errors = 4; + int bad = -1; + + if (!buffer) + return 1; + + memset(buffer, byte, RX_END); + ether3_setbuffer(dev, buffer_write, 0); + ether3_writebuffer(dev, buffer, TX_END); + ether3_setbuffer(dev, buffer_write, RX_START); + ether3_writebuffer(dev, buffer + RX_START, RX_LEN); + memset(buffer, byte ^ 0xff, RX_END); + ether3_setbuffer(dev, buffer_read, 0); + ether3_readbuffer(dev, buffer, TX_END); + ether3_setbuffer(dev, buffer_read, RX_START); + ether3_readbuffer(dev, buffer + RX_START, RX_LEN); + + for (i = 0; i < RX_END; i++) { + if (buffer[i] != byte) { + if (max_errors > 0 && bad != buffer[i]) { + printk("%s: RAM failed with (%02X instead of %02X) at 0x%04X", + dev->name, buffer[i], byte, i); + ret = 2; + max_errors--; + bad = i; + } + } else { + if (bad != -1) { + if (bad != i - 1) + printk(" - 0x%04X\n", i - 1); + printk("\n"); + bad = -1; + } + } + } + if (bad != -1) + printk(" - 0xffff\n"); + kfree(buffer); + + return ret; +} + +/* ------------------------------------------------------------------------------- */ + +static int __init +ether3_init_2(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int i; + + priv->regs.config1 = CFG1_RECVCOMPSTAT0|CFG1_DMABURST8; + priv->regs.config2 = CFG2_CTRLO|CFG2_RECVCRC|CFG2_ERRENCRC; + priv->regs.command = 0; + + /* + * Set up our hardware address + */ + ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); + for (i = 0; i < 6; i++) + ether3_outb(dev->dev_addr[i], REG_BUFWIN); + + if (dev->flags & IFF_PROMISC) + priv->regs.config1 |= CFG1_RECVPROMISC; + else if (dev->flags & IFF_MULTICAST) + priv->regs.config1 |= CFG1_RECVSPECBRMULTI; + else + priv->regs.config1 |= CFG1_RECVSPECBROAD; + + /* + * There is a problem with the NQ8005 in that it occasionally loses the + * last two bytes. To get round this problem, we receive the CRC as + * well. That way, if we do lose the last two, then it doesn't matter. + */ + ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); + ether3_outw((TX_END>>8) - 1, REG_BUFWIN); + ether3_outw(priv->rx_head, REG_RECVPTR); + ether3_outw(0, REG_TRANSMITPTR); + ether3_outw(priv->rx_head >> 8, REG_RECVEND); + ether3_outw(priv->regs.config2, REG_CONFIG2); + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); + ether3_outw(priv->regs.command, REG_COMMAND); + + i = ether3_ramtest(dev, 0x5A); + if(i) + return i; + i = ether3_ramtest(dev, 0x1E); + if(i) + return i; + + ether3_setbuffer(dev, buffer_write, 0); + ether3_writelong(dev, 0); + return 0; +} + +static void +ether3_init_for_open(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + int i; + + memset(&priv->stats, 0, sizeof(struct net_device_stats)); + + /* Reset the chip */ + ether3_outw(CFG2_RESET, REG_CONFIG2); + udelay(4); + + priv->regs.command = 0; + ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); + while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); + + ether3_outw(priv->regs.config1 | CFG1_BUFSELSTAT0, REG_CONFIG1); + for (i = 0; i < 6; i++) + ether3_outb(dev->dev_addr[i], REG_BUFWIN); + + priv->tx_head = 0; + priv->tx_tail = 0; + priv->regs.config2 |= CFG2_CTRLO; + priv->rx_head = RX_START; + + ether3_outw(priv->regs.config1 | CFG1_TRANSEND, REG_CONFIG1); + ether3_outw((TX_END>>8) - 1, REG_BUFWIN); + ether3_outw(priv->rx_head, REG_RECVPTR); + ether3_outw(priv->rx_head >> 8, REG_RECVEND); + ether3_outw(0, REG_TRANSMITPTR); + ether3_outw(priv->regs.config2, REG_CONFIG2); + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); + + ether3_setbuffer(dev, buffer_write, 0); + ether3_writelong(dev, 0); + + priv->regs.command = CMD_ENINTRX | CMD_ENINTTX; + ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); +} + +static inline int +ether3_probe_bus_8(struct net_device *dev, int val) +{ + int write_low, write_high, read_low, read_high; + + write_low = val & 255; + write_high = val >> 8; + + printk(KERN_DEBUG "ether3_probe: write8 [%02X:%02X]", write_high, write_low); + + ether3_outb(write_low, REG_RECVPTR); + ether3_outb(write_high, REG_RECVPTR + 1); + + read_low = ether3_inb(REG_RECVPTR); + read_high = ether3_inb(REG_RECVPTR + 1); + + printk(", read8 [%02X:%02X]\n", read_high, read_low); + + return read_low == write_low && read_high == write_high; +} + +static inline int +ether3_probe_bus_16(struct net_device *dev, int val) +{ + int read_val; + + ether3_outw(val, REG_RECVPTR); + read_val = ether3_inw(REG_RECVPTR); + + printk(KERN_DEBUG "ether3_probe: write16 [%04X], read16 [%04X]\n", val, read_val); + + return read_val == val; +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int +ether3_open(struct net_device *dev) +{ + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_WARNING "%s: invalid ethernet MAC address\n", + dev->name); + return -EINVAL; + } + + if (request_irq(dev->irq, ether3_interrupt, 0, "ether3", dev)) + return -EAGAIN; + + ether3_init_for_open(dev); + + netif_start_queue(dev); + + return 0; +} + +/* + * The inverse routine to ether3_open(). + */ +static int +ether3_close(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + + netif_stop_queue(dev); + + disable_irq(dev->irq); + + ether3_outw(CMD_RXOFF|CMD_TXOFF, REG_COMMAND); + priv->regs.command = 0; + while (ether3_inw(REG_STATUS) & (STAT_RXON|STAT_TXON)); + ether3_outb(0x80, REG_CONFIG2 + 1); + ether3_outw(0, REG_COMMAND); + + free_irq(dev->irq, dev); + + return 0; +} + +/* + * Get the current statistics. This may be called with the card open or + * closed. + */ +static struct net_device_stats *ether3_getstats(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + return &priv->stats; +} + +/* + * Set or clear promiscuous/multicast mode filter for this adaptor. + * + * We don't attempt any packet filtering. The card may have a SEEQ 8004 + * in which does not have the other ethernet address registers present... + */ +static void ether3_setmulticastlist(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + + priv->regs.config1 &= ~CFG1_RECVPROMISC; + + if (dev->flags & IFF_PROMISC) { + /* promiscuous mode */ + priv->regs.config1 |= CFG1_RECVPROMISC; + } else if (dev->flags & IFF_ALLMULTI) { + priv->regs.config1 |= CFG1_RECVSPECBRMULTI; + } else + priv->regs.config1 |= CFG1_RECVSPECBROAD; + + ether3_outw(priv->regs.config1 | CFG1_LOCBUFMEM, REG_CONFIG1); +} + +static void +ether3_timeout(struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + + del_timer(&priv->timer); + + local_irq_save(flags); + printk(KERN_ERR "%s: transmit timed out, network cable problem?\n", dev->name); + printk(KERN_ERR "%s: state: { status=%04X cfg1=%04X cfg2=%04X }\n", dev->name, + ether3_inw(REG_STATUS), ether3_inw(REG_CONFIG1), ether3_inw(REG_CONFIG2)); + printk(KERN_ERR "%s: { rpr=%04X rea=%04X tpr=%04X }\n", dev->name, + ether3_inw(REG_RECVPTR), ether3_inw(REG_RECVEND), ether3_inw(REG_TRANSMITPTR)); + printk(KERN_ERR "%s: tx head=%X tx tail=%X\n", dev->name, + priv->tx_head, priv->tx_tail); + ether3_setbuffer(dev, buffer_read, priv->tx_tail); + printk(KERN_ERR "%s: packet status = %08X\n", dev->name, ether3_readlong(dev)); + local_irq_restore(flags); + + priv->regs.config2 |= CFG2_CTRLO; + priv->stats.tx_errors += 1; + ether3_outw(priv->regs.config2, REG_CONFIG2); + priv->tx_head = priv->tx_tail = 0; + + netif_wake_queue(dev); +} + +/* + * Transmit a packet + */ +static int +ether3_sendpacket(struct sk_buff *skb, struct net_device *dev) +{ + struct dev_priv *priv = (struct dev_priv *)dev->priv; + unsigned long flags; + unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned int ptr, next_ptr; + + length = (length + 1) & ~1; + + if (priv->broken) { + dev_kfree_skb(skb); + priv->stats.tx_dropped ++; + netif_start_queue(dev); + return 0; + } + + next_ptr = (priv->tx_head + 1) & 15; + + local_irq_save(flags); + + if (priv->tx_tail == next_ptr) { + local_irq_restore(flags); + return 1; /* unable to queue */ + } + + dev->trans_start = jiffies; + ptr = 0x600 * priv->tx_head; + priv->tx_head = next_ptr; + next_ptr *= 0x600; + +#define TXHDR_FLAGS (TXHDR_TRANSMIT|TXHDR_CHAINCONTINUE|TXHDR_DATAFOLLOWS|TXHDR_ENSUCCESS) + + ether3_setbuffer(dev, buffer_write, next_ptr); + ether3_writelong(dev, 0); + ether3_setbuffer(dev, buffer_write, ptr); + ether3_writelong(dev, 0); + ether3_writebuffer(dev, skb->data, length); + ether3_writeword(dev, htons(next_ptr)); + ether3_writeword(dev, TXHDR_CHAINCONTINUE >> 16); + ether3_setbuffer(dev, buffer_write, ptr); + ether3_writeword(dev, htons((ptr + length + 4))); + ether3_writeword(dev, TXHDR_FLAGS >> 16); + ether3_ledon(dev, priv); + + if (!(ether3_inw(REG_STATUS) & STAT_TXON)) { + ether3_outw(ptr, REG_TRANSMITPTR); + ether3_outw(priv->regs.command | CMD_TXON, REG_COMMAND); + } + + next_ptr = (priv->tx_head + 1) & 15; + local_irq_restore(flags); + + dev_kfree_skb(skb); + + if (priv->tx_tail == next_ptr) + netif_stop_queue(dev); + + return 0; +} + +static irqreturn_t +ether3_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct dev_priv *priv; + unsigned int status, handled = IRQ_NONE; + +#if NET_DEBUG > 1 + if(net_debug & DEBUG_INT) + printk("eth3irq: %d ", irq); +#endif + + priv = (struct dev_priv *)dev->priv; + + status = ether3_inw(REG_STATUS); + + if (status & STAT_INTRX) { + ether3_outw(CMD_ACKINTRX | priv->regs.command, REG_COMMAND); + ether3_rx(dev, priv, 12); + handled = IRQ_HANDLED; + } + + if (status & STAT_INTTX) { + ether3_outw(CMD_ACKINTTX | priv->regs.command, REG_COMMAND); + ether3_tx(dev, priv); + handled = IRQ_HANDLED; + } + +#if NET_DEBUG > 1 + if(net_debug & DEBUG_INT) + printk("done\n"); +#endif + return handled; +} + +/* + * If we have a good packet(s), get it/them out of the buffers. + */ +static int +ether3_rx(struct net_device *dev, struct dev_priv *priv, unsigned int maxcnt) +{ + unsigned int next_ptr = priv->rx_head, received = 0; + ether3_ledon(dev, priv); + + do { + unsigned int this_ptr, status; + unsigned char addrs[16]; + + /* + * read the first 16 bytes from the buffer. + * This contains the status bytes etc and ethernet addresses, + * and we also check the source ethernet address to see if + * it originated from us. + */ + { + unsigned int temp_ptr; + ether3_setbuffer(dev, buffer_read, next_ptr); + temp_ptr = ether3_readword(dev); + status = ether3_readword(dev); + if ((status & (RXSTAT_DONE | RXHDR_CHAINCONTINUE | RXHDR_RECEIVE)) != + (RXSTAT_DONE | RXHDR_CHAINCONTINUE) || !temp_ptr) + break; + + this_ptr = next_ptr + 4; + next_ptr = ntohs(temp_ptr); + } + ether3_setbuffer(dev, buffer_read, this_ptr); + ether3_readbuffer(dev, addrs+2, 12); + +if (next_ptr < RX_START || next_ptr >= RX_END) { + int i; + printk("%s: bad next pointer @%04X: ", dev->name, priv->rx_head); + printk("%02X %02X %02X %02X ", next_ptr >> 8, next_ptr & 255, status & 255, status >> 8); + for (i = 2; i < 14; i++) + printk("%02X ", addrs[i]); + printk("\n"); + next_ptr = priv->rx_head; + break; +} + /* + * ignore our own packets... + */ + if (!(*(unsigned long *)&dev->dev_addr[0] ^ *(unsigned long *)&addrs[2+6]) && + !(*(unsigned short *)&dev->dev_addr[4] ^ *(unsigned short *)&addrs[2+10])) { + maxcnt ++; /* compensate for loopedback packet */ + ether3_outw(next_ptr >> 8, REG_RECVEND); + } else + if (!(status & (RXSTAT_OVERSIZE|RXSTAT_CRCERROR|RXSTAT_DRIBBLEERROR|RXSTAT_SHORTPACKET))) { + unsigned int length = next_ptr - this_ptr; + struct sk_buff *skb; + + if (next_ptr <= this_ptr) + length += RX_END - RX_START; + + skb = dev_alloc_skb(length + 2); + if (skb) { + unsigned char *buf; + + skb->dev = dev; + skb_reserve(skb, 2); + buf = skb_put(skb, length); + ether3_readbuffer(dev, buf + 12, length - 12); + ether3_outw(next_ptr >> 8, REG_RECVEND); + *(unsigned short *)(buf + 0) = *(unsigned short *)(addrs + 2); + *(unsigned long *)(buf + 2) = *(unsigned long *)(addrs + 4); + *(unsigned long *)(buf + 6) = *(unsigned long *)(addrs + 8); + *(unsigned short *)(buf + 10) = *(unsigned short *)(addrs + 12); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + received ++; + } else + goto dropping; + } else { + struct net_device_stats *stats = &priv->stats; + ether3_outw(next_ptr >> 8, REG_RECVEND); + if (status & RXSTAT_OVERSIZE) stats->rx_over_errors ++; + if (status & RXSTAT_CRCERROR) stats->rx_crc_errors ++; + if (status & RXSTAT_DRIBBLEERROR) stats->rx_fifo_errors ++; + if (status & RXSTAT_SHORTPACKET) stats->rx_length_errors ++; + stats->rx_errors++; + } + } + while (-- maxcnt); + +done: + priv->stats.rx_packets += received; + priv->rx_head = next_ptr; + /* + * If rx went off line, then that means that the buffer may be full. We + * have dropped at least one packet. + */ + if (!(ether3_inw(REG_STATUS) & STAT_RXON)) { + priv->stats.rx_dropped ++; + ether3_outw(next_ptr, REG_RECVPTR); + ether3_outw(priv->regs.command | CMD_RXON, REG_COMMAND); + } + + return maxcnt; + +dropping:{ + static unsigned long last_warned; + + ether3_outw(next_ptr >> 8, REG_RECVEND); + /* + * Don't print this message too many times... + */ + if (time_after(jiffies, last_warned + 10 * HZ)) { + last_warned = jiffies; + printk("%s: memory squeeze, dropping packet.\n", dev->name); + } + priv->stats.rx_dropped ++; + goto done; + } +} + +/* + * Update stats for the transmitted packet(s) + */ +static void +ether3_tx(struct net_device *dev, struct dev_priv *priv) +{ + unsigned int tx_tail = priv->tx_tail; + int max_work = 14; + + do { + unsigned long status; + + /* + * Read the packet header + */ + ether3_setbuffer(dev, buffer_read, tx_tail * 0x600); + status = ether3_readlong(dev); + + /* + * Check to see if this packet has been transmitted + */ + if ((status & (TXSTAT_DONE | TXHDR_TRANSMIT)) != + (TXSTAT_DONE | TXHDR_TRANSMIT)) + break; + + /* + * Update errors + */ + if (!(status & (TXSTAT_BABBLED | TXSTAT_16COLLISIONS))) + priv->stats.tx_packets++; + else { + priv->stats.tx_errors ++; + if (status & TXSTAT_16COLLISIONS) priv->stats.collisions += 16; + if (status & TXSTAT_BABBLED) priv->stats.tx_fifo_errors ++; + } + + tx_tail = (tx_tail + 1) & 15; + } while (--max_work); + + if (priv->tx_tail != tx_tail) { + priv->tx_tail = tx_tail; + netif_wake_queue(dev); + } +} + +static void __init ether3_banner(void) +{ + static unsigned version_printed = 0; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +static const char * __init +ether3_get_dev(struct net_device *dev, struct expansion_card *ec) +{ + const char *name = "ether3"; + + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + dev->irq = ec->irq; + + if (ec->cid.manufacturer == MANU_ANT && + ec->cid.product == PROD_ANT_ETHERB) { + dev->base_addr += 0x200; + name = "etherb"; + } + + ec->irqaddr = (volatile unsigned char *)ioaddr(dev->base_addr); + ec->irqmask = 0xf0; + + ether3_addr(dev->dev_addr, ec); + + return name; +} + +static int __devinit +ether3_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct net_device *dev; + struct dev_priv *priv; + const char *name; + int i, bus_type, ret; + + ether3_banner(); + + dev = init_etherdev(NULL, sizeof(struct dev_priv)); + if (!dev) { + ret = -ENOMEM; + goto out; + } + + SET_MODULE_OWNER(dev); + + name = ether3_get_dev(dev, ec); + if (!name) { + ret = -ENODEV; + goto free; + } + + /* + * this will not fail - the nature of the bus ensures this + */ + if (!request_region(dev->base_addr, 128, dev->name)) { + ret = -EBUSY; + goto free; + } + + priv = (struct dev_priv *) dev->priv; + init_timer(&priv->timer); + + /* Reset card... + */ + ether3_outb(0x80, REG_CONFIG2 + 1); + bus_type = BUS_UNKNOWN; + udelay(4); + + /* Test using Receive Pointer (16-bit register) to find out + * how the ether3 is connected to the bus... + */ + if (ether3_probe_bus_8(dev, 0x100) && + ether3_probe_bus_8(dev, 0x201)) + bus_type = BUS_8; + + if (bus_type == BUS_UNKNOWN && + ether3_probe_bus_16(dev, 0x101) && + ether3_probe_bus_16(dev, 0x201)) + bus_type = BUS_16; + + switch (bus_type) { + case BUS_UNKNOWN: + printk(KERN_ERR "%s: unable to identify bus width\n", dev->name); + ret = -ENODEV; + goto failed; + + case BUS_8: + printk(KERN_ERR "%s: %s found, but is an unsupported " + "8-bit card\n", dev->name, name); + ret = -ENODEV; + goto failed; + + default: + break; + } + + printk("%s: %s in slot %d, ", dev->name, name, ec->slot_no); + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + + if (ether3_init_2(dev)) { + ret = -ENODEV; + goto failed; + } + + dev->open = ether3_open; + dev->stop = ether3_close; + dev->hard_start_xmit = ether3_sendpacket; + dev->get_stats = ether3_getstats; + dev->set_multicast_list = ether3_setmulticastlist; + dev->tx_timeout = ether3_timeout; + dev->watchdog_timeo = 5 * HZ / 100; + + ecard_set_drvdata(ec, dev); + return 0; + +failed: + release_region(dev->base_addr, 128); +free: + unregister_netdev(dev); + kfree(dev); +out: + return ret; +} + +static void __devexit ether3_remove(struct expansion_card *ec) +{ + struct net_device *dev = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + unregister_netdev(dev); + release_region(dev->base_addr, 128); + kfree(dev); +} + +static const struct ecard_id ether3_ids[] = { + { MANU_ANT2, PROD_ANT_ETHER3 }, + { MANU_ANT, PROD_ANT_ETHER3 }, + { MANU_ANT, PROD_ANT_ETHERB }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver ether3_driver = { + .probe = ether3_probe, + .remove = __devexit_p(ether3_remove), + .id_table = ether3_ids, + .drv = { + .name = "ether3", + }, +}; + +static int __init ether3_init(void) +{ + return ecard_register_driver(ðer3_driver); +} + +static void __exit ether3_exit(void) +{ + ecard_remove_driver(ðer3_driver); +} + +module_init(ether3_init); +module_exit(ether3_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arm/ether3.h b/drivers/net/arm/ether3.h new file mode 100644 index 000000000000..fd0c2edf57fe --- /dev/null +++ b/drivers/net/arm/ether3.h @@ -0,0 +1,168 @@ +/* + * linux/drivers/acorn/net/ether3.h + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * network driver for Acorn/ANT Ether3 cards + */ + +#ifndef _LINUX_ether3_H +#define _LINUX_ether3_H + +/* use 0 for production, 1 for verification, >2 for debug. debug flags: */ +#define DEBUG_TX 2 +#define DEBUG_RX 4 +#define DEBUG_INT 8 +#define DEBUG_IC 16 +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* Command register definitions & bits */ +#define REG_COMMAND (dev->base_addr + 0x00) +#define CMD_ENINTDMA 0x0001 +#define CMD_ENINTRX 0x0002 +#define CMD_ENINTTX 0x0004 +#define CMD_ENINTBUFWIN 0x0008 +#define CMD_ACKINTDMA 0x0010 +#define CMD_ACKINTRX 0x0020 +#define CMD_ACKINTTX 0x0040 +#define CMD_ACKINTBUFWIN 0x0080 +#define CMD_DMAON 0x0100 +#define CMD_RXON 0x0200 +#define CMD_TXON 0x0400 +#define CMD_DMAOFF 0x0800 +#define CMD_RXOFF 0x1000 +#define CMD_TXOFF 0x2000 +#define CMD_FIFOREAD 0x4000 +#define CMD_FIFOWRITE 0x8000 + +/* status register */ +#define REG_STATUS (dev->base_addr + 0x00) +#define STAT_ENINTSTAT 0x0001 +#define STAT_ENINTRX 0x0002 +#define STAT_ENINTTX 0x0004 +#define STAT_ENINTBUFWIN 0x0008 +#define STAT_INTDMA 0x0010 +#define STAT_INTRX 0x0020 +#define STAT_INTTX 0x0040 +#define STAT_INTBUFWIN 0x0080 +#define STAT_DMAON 0x0100 +#define STAT_RXON 0x0200 +#define STAT_TXON 0x0400 +#define STAT_FIFOFULL 0x2000 +#define STAT_FIFOEMPTY 0x4000 +#define STAT_FIFODIR 0x8000 + +/* configuration register 1 */ +#define REG_CONFIG1 (dev->base_addr + 0x10) +#define CFG1_BUFSELSTAT0 0x0000 +#define CFG1_BUFSELSTAT1 0x0001 +#define CFG1_BUFSELSTAT2 0x0002 +#define CFG1_BUFSELSTAT3 0x0003 +#define CFG1_BUFSELSTAT4 0x0004 +#define CFG1_BUFSELSTAT5 0x0005 +#define CFG1_ADDRPROM 0x0006 +#define CFG1_TRANSEND 0x0007 +#define CFG1_LOCBUFMEM 0x0008 +#define CFG1_INTVECTOR 0x0009 +#define CFG1_RECVSPECONLY 0x0000 +#define CFG1_RECVSPECBROAD 0x4000 +#define CFG1_RECVSPECBRMULTI 0x8000 +#define CFG1_RECVPROMISC 0xC000 + +/* The following aren't in 8004 */ +#define CFG1_DMABURSTCONT 0x0000 +#define CFG1_DMABURST800NS 0x0010 +#define CFG1_DMABURST1600NS 0x0020 +#define CFG1_DMABURST3200NS 0x0030 +#define CFG1_DMABURST1 0x0000 +#define CFG1_DMABURST4 0x0040 +#define CFG1_DMABURST8 0x0080 +#define CFG1_DMABURST16 0x00C0 +#define CFG1_RECVCOMPSTAT0 0x0100 +#define CFG1_RECVCOMPSTAT1 0x0200 +#define CFG1_RECVCOMPSTAT2 0x0400 +#define CFG1_RECVCOMPSTAT3 0x0800 +#define CFG1_RECVCOMPSTAT4 0x1000 +#define CFG1_RECVCOMPSTAT5 0x2000 + +/* configuration register 2 */ +#define REG_CONFIG2 (dev->base_addr + 0x20) +#define CFG2_BYTESWAP 0x0001 +#define CFG2_ERRENCRC 0x0008 +#define CFG2_ERRENDRIBBLE 0x0010 +#define CFG2_ERRSHORTFRAME 0x0020 +#define CFG2_SLOTSELECT 0x0040 +#define CFG2_PREAMSELECT 0x0080 +#define CFG2_ADDRLENGTH 0x0100 +#define CFG2_RECVCRC 0x0200 +#define CFG2_XMITNOCRC 0x0400 +#define CFG2_LOOPBACK 0x0800 +#define CFG2_CTRLO 0x1000 +#define CFG2_RESET 0x8000 + +#define REG_RECVEND (dev->base_addr + 0x30) + +#define REG_BUFWIN (dev->base_addr + 0x40) + +#define REG_RECVPTR (dev->base_addr + 0x50) + +#define REG_TRANSMITPTR (dev->base_addr + 0x60) + +#define REG_DMAADDR (dev->base_addr + 0x70) + +/* + * Cards transmit/receive headers + */ +#define TX_NEXT (0xffff) +#define TXHDR_ENBABBLEINT (1 << 16) +#define TXHDR_ENCOLLISIONINT (1 << 17) +#define TXHDR_EN16COLLISION (1 << 18) +#define TXHDR_ENSUCCESS (1 << 19) +#define TXHDR_DATAFOLLOWS (1 << 21) +#define TXHDR_CHAINCONTINUE (1 << 22) +#define TXHDR_TRANSMIT (1 << 23) +#define TXSTAT_BABBLED (1 << 24) +#define TXSTAT_COLLISION (1 << 25) +#define TXSTAT_16COLLISIONS (1 << 26) +#define TXSTAT_DONE (1 << 31) + +#define RX_NEXT (0xffff) +#define RXHDR_CHAINCONTINUE (1 << 6) +#define RXHDR_RECEIVE (1 << 7) +#define RXSTAT_OVERSIZE (1 << 8) +#define RXSTAT_CRCERROR (1 << 9) +#define RXSTAT_DRIBBLEERROR (1 << 10) +#define RXSTAT_SHORTPACKET (1 << 11) +#define RXSTAT_DONE (1 << 15) + + +#define TX_START 0x0000 +#define TX_END 0x6000 +#define RX_START 0x6000 +#define RX_LEN 0xA000 +#define RX_END 0x10000 +/* must be a power of 2 and greater than MAX_TX_BUFFERED */ +#define MAX_TXED 16 +#define MAX_TX_BUFFERED 10 + +struct dev_priv { + struct { + unsigned int command; + unsigned int config1; + unsigned int config2; + } regs; + unsigned char tx_head; /* buffer nr to insert next packet */ + unsigned char tx_tail; /* buffer nr of transmitting packet */ + unsigned int rx_head; /* address to fetch next packet from */ + struct net_device_stats stats; + struct timer_list timer; + int broken; /* 0 = ok, 1 = something went wrong */ +}; + +#endif diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c new file mode 100644 index 000000000000..72e6726230a4 --- /dev/null +++ b/drivers/net/arm/etherh.c @@ -0,0 +1,763 @@ +/* + * linux/drivers/acorn/net/etherh.c + * + * Copyright (C) 2000-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * NS8390 I-cubed EtherH and ANT EtherM specific driver + * Thanks to I-Cubed for information on their cards. + * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton + * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan) + * EtherM integration re-engineered by Russell King. + * + * Changelog: + * 08-12-1996 RMK 1.00 Created + * RMK 1.03 Added support for EtherLan500 cards + * 23-11-1997 RMK 1.04 Added media autodetection + * 16-04-1998 RMK 1.05 Improved media autodetection + * 10-02-2000 RMK 1.06 Updated for 2.3.43 + * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8 + * 12-10-1999 CK/TEW EtherM driver first release + * 21-12-2000 TTC EtherH/EtherM integration + * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver. + * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../8390.h" + +#define NET_DEBUG 0 +#define DEBUG_INIT 2 + +static unsigned int net_debug = NET_DEBUG; + +struct etherh_priv { + struct ei_device eidev; + unsigned int id; + unsigned int ctrl_port; + unsigned int ctrl; +}; + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("EtherH/EtherM driver"); +MODULE_LICENSE("GPL"); + +static char version[] __initdata = + "EtherH/EtherM Driver (c) 2002 Russell King v1.09\n"; + +#define ETHERH500_DATAPORT 0x200 /* MEMC */ +#define ETHERH500_NS8390 0x000 /* MEMC */ +#define ETHERH500_CTRLPORT 0x200 /* IOC */ + +#define ETHERH600_DATAPORT 16 /* MEMC */ +#define ETHERH600_NS8390 0x200 /* MEMC */ +#define ETHERH600_CTRLPORT 0x080 /* MEMC */ + +#define ETHERH_CP_IE 1 +#define ETHERH_CP_IF 2 +#define ETHERH_CP_HEARTBEAT 2 + +#define ETHERH_TX_START_PAGE 1 +#define ETHERH_STOP_PAGE 127 + +/* + * These came from CK/TEW + */ +#define ETHERM_DATAPORT 0x080 /* MEMC */ +#define ETHERM_NS8390 0x200 /* MEMC */ +#define ETHERM_CTRLPORT 0x08f /* MEMC */ + +#define ETHERM_TX_START_PAGE 64 +#define ETHERM_STOP_PAGE 127 + +/* ------------------------------------------------------------------------ */ + +static inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned int mask) +{ + eh->ctrl |= mask; + outb(eh->ctrl, eh->ctrl_port); +} + +static inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned int mask) +{ + eh->ctrl &= ~mask; + outb(eh->ctrl, eh->ctrl_port); +} + +static inline unsigned int etherh_get_stat(struct etherh_priv *eh) +{ + return inb(eh->ctrl_port); +} + + + + +static void etherh_irq_enable(ecard_t *ec, int irqnr) +{ + struct etherh_priv *eh = ec->irq_data; + + etherh_set_ctrl(eh, ETHERH_CP_IE); +} + +static void etherh_irq_disable(ecard_t *ec, int irqnr) +{ + struct etherh_priv *eh = ec->irq_data; + + etherh_clr_ctrl(eh, ETHERH_CP_IE); +} + +static expansioncard_ops_t etherh_ops = { + .irqenable = etherh_irq_enable, + .irqdisable = etherh_irq_disable, +}; + + + + +static void +etherh_setif(struct net_device *dev) +{ + struct etherh_priv *eh = (struct etherh_priv *)dev->priv; + struct ei_device *ei_local = &eh->eidev; + unsigned long addr, flags; + + local_irq_save(flags); + + /* set the interface type */ + switch (eh->id) { + case PROD_I3_ETHERLAN600: + case PROD_I3_ETHERLAN600A: + addr = dev->base_addr + EN0_RCNTHI; + + switch (dev->if_port) { + case IF_PORT_10BASE2: + outb((inb(addr) & 0xf8) | 1, addr); + break; + case IF_PORT_10BASET: + outb((inb(addr) & 0xf8), addr); + break; + } + break; + + case PROD_I3_ETHERLAN500: + switch (dev->if_port) { + case IF_PORT_10BASE2: + etherh_clr_ctrl(eh, ETHERH_CP_IF); + break; + + case IF_PORT_10BASET: + etherh_set_ctrl(eh, ETHERH_CP_IF); + break; + } + break; + + default: + break; + } + + local_irq_restore(flags); +} + +static int +etherh_getifstat(struct net_device *dev) +{ + struct etherh_priv *eh = (struct etherh_priv *)dev->priv; + struct ei_device *ei_local = &eh->eidev; + int stat = 0; + + switch (eh->id) { + case PROD_I3_ETHERLAN600: + case PROD_I3_ETHERLAN600A: + switch (dev->if_port) { + case IF_PORT_10BASE2: + stat = 1; + break; + case IF_PORT_10BASET: + stat = inb(dev->base_addr+EN0_RCNTHI) & 4; + break; + } + break; + + case PROD_I3_ETHERLAN500: + switch (dev->if_port) { + case IF_PORT_10BASE2: + stat = 1; + break; + case IF_PORT_10BASET: + stat = etherh_get_stat(eh) & ETHERH_CP_HEARTBEAT; + break; + } + break; + + default: + stat = 0; + break; + } + + return stat != 0; +} + +/* + * Configure the interface. Note that we ignore the other + * parts of ifmap, since its mostly meaningless for this driver. + */ +static int etherh_set_config(struct net_device *dev, struct ifmap *map) +{ + switch (map->port) { + case IF_PORT_10BASE2: + case IF_PORT_10BASET: + /* + * If the user explicitly sets the interface + * media type, turn off automedia detection. + */ + dev->flags &= ~IFF_AUTOMEDIA; + dev->if_port = map->port; + break; + + default: + return -EINVAL; + } + + etherh_setif(dev); + + return 0; +} + +/* + * Reset the 8390 (hard reset). Note that we can't actually do this. + */ +static void +etherh_reset(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + + outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, dev->base_addr); + + /* + * See if we need to change the interface type. + * Note that we use 'interface_num' as a flag + * to indicate that we need to change the media. + */ + if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) { + ei_local->interface_num = 0; + + if (dev->if_port == IF_PORT_10BASET) + dev->if_port = IF_PORT_10BASE2; + else + dev->if_port = IF_PORT_10BASET; + + etherh_setif(dev); + } +} + +/* + * Write a block of data out to the 8390 + */ +static void +etherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + unsigned int addr, dma_addr; + unsigned long dma_start; + + if (ei_local->dmaing) { + printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " + " DMAstat %d irqlock %d\n", dev->name, + ei_local->dmaing, ei_local->irqlock); + return; + } + + /* + * Make sure we have a round number of bytes if we're in word mode. + */ + if (count & 1 && ei_local->word16) + count++; + + ei_local->dmaing = 1; + + addr = dev->base_addr; + dma_addr = dev->mem_start; + + count = (count + 1) & ~1; + outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); + + outb (0x42, addr + EN0_RCNTLO); + outb (0x00, addr + EN0_RCNTHI); + outb (0x42, addr + EN0_RSARLO); + outb (0x00, addr + EN0_RSARHI); + outb (E8390_RREAD | E8390_START, addr + E8390_CMD); + + udelay (1); + + outb (ENISR_RDC, addr + EN0_ISR); + outb (count, addr + EN0_RCNTLO); + outb (count >> 8, addr + EN0_RCNTHI); + outb (0, addr + EN0_RSARLO); + outb (start_page, addr + EN0_RSARHI); + outb (E8390_RWRITE | E8390_START, addr + E8390_CMD); + + if (ei_local->word16) + outsw (dma_addr, buf, count >> 1); + else + outsb (dma_addr, buf, count); + + dma_start = jiffies; + + while ((inb (addr + EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2*HZ/100) { /* 20ms */ + printk(KERN_ERR "%s: timeout waiting for TX RDC\n", + dev->name); + etherh_reset (dev); + NS8390_init (dev, 1); + break; + } + + outb (ENISR_RDC, addr + EN0_ISR); + ei_local->dmaing = 0; +} + +/* + * Read a block of data from the 8390 + */ +static void +etherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + unsigned int addr, dma_addr; + unsigned char *buf; + + if (ei_local->dmaing) { + printk(KERN_ERR "%s: DMAing conflict in etherh_block_input: " + " DMAstat %d irqlock %d\n", dev->name, + ei_local->dmaing, ei_local->irqlock); + return; + } + + ei_local->dmaing = 1; + + addr = dev->base_addr; + dma_addr = dev->mem_start; + + buf = skb->data; + outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); + outb (count, addr + EN0_RCNTLO); + outb (count >> 8, addr + EN0_RCNTHI); + outb (ring_offset, addr + EN0_RSARLO); + outb (ring_offset >> 8, addr + EN0_RSARHI); + outb (E8390_RREAD | E8390_START, addr + E8390_CMD); + + if (ei_local->word16) { + insw (dma_addr, buf, count >> 1); + if (count & 1) + buf[count - 1] = inb (dma_addr); + } else + insb (dma_addr, buf, count); + + outb (ENISR_RDC, addr + EN0_ISR); + ei_local->dmaing = 0; +} + +/* + * Read a header from the 8390 + */ +static void +etherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + unsigned int addr, dma_addr; + + if (ei_local->dmaing) { + printk(KERN_ERR "%s: DMAing conflict in etherh_get_header: " + " DMAstat %d irqlock %d\n", dev->name, + ei_local->dmaing, ei_local->irqlock); + return; + } + + ei_local->dmaing = 1; + + addr = dev->base_addr; + dma_addr = dev->mem_start; + + outb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); + outb (sizeof (*hdr), addr + EN0_RCNTLO); + outb (0, addr + EN0_RCNTHI); + outb (0, addr + EN0_RSARLO); + outb (ring_page, addr + EN0_RSARHI); + outb (E8390_RREAD | E8390_START, addr + E8390_CMD); + + if (ei_local->word16) + insw (dma_addr, hdr, sizeof (*hdr) >> 1); + else + insb (dma_addr, hdr, sizeof (*hdr)); + + outb (ENISR_RDC, addr + EN0_ISR); + ei_local->dmaing = 0; +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ +static int +etherh_open(struct net_device *dev) +{ + struct ei_device *ei_local = (struct ei_device *) dev->priv; + + if (!is_valid_ether_addr(dev->dev_addr)) { + printk(KERN_WARNING "%s: invalid ethernet MAC address\n", + dev->name); + return -EINVAL; + } + + if (request_irq(dev->irq, ei_interrupt, 0, dev->name, dev)) + return -EAGAIN; + + /* + * Make sure that we aren't going to change the + * media type on the next reset - we are about to + * do automedia manually now. + */ + ei_local->interface_num = 0; + + /* + * If we are doing automedia detection, do it now. + * This is more reliable than the 8390's detection. + */ + if (dev->flags & IFF_AUTOMEDIA) { + dev->if_port = IF_PORT_10BASET; + etherh_setif(dev); + mdelay(1); + if (!etherh_getifstat(dev)) { + dev->if_port = IF_PORT_10BASE2; + etherh_setif(dev); + } + } else + etherh_setif(dev); + + etherh_reset(dev); + ei_open(dev); + + return 0; +} + +/* + * The inverse routine to etherh_open(). + */ +static int +etherh_close(struct net_device *dev) +{ + ei_close (dev); + free_irq (dev->irq, dev); + return 0; +} + +/* + * Initialisation + */ + +static void __init etherh_banner(void) +{ + static int version_printed; + + if (net_debug && version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + +/* + * Read the ethernet address string from the on board rom. + * This is an ascii string... + */ +static int __init etherh_addr(char *addr, struct expansion_card *ec) +{ + struct in_chunk_dir cd; + char *s; + + if (ecard_readchunk(&cd, ec, 0xf5, 0) && (s = strchr(cd.d.string, '('))) { + int i; + for (i = 0; i < 6; i++) { + addr[i] = simple_strtoul(s + 1, &s, 0x10); + if (*s != (i == 5? ')' : ':')) + break; + } + if (i == 6) + return 0; + } + return -ENODEV; +} + +/* + * Create an ethernet address from the system serial number. + */ +static int __init etherm_addr(char *addr) +{ + unsigned int serial; + + if (system_serial_low == 0 && system_serial_high == 0) + return -ENODEV; + + serial = system_serial_low | system_serial_high; + + addr[0] = 0; + addr[1] = 0; + addr[2] = 0xa4; + addr[3] = 0x10 + (serial >> 24); + addr[4] = serial >> 16; + addr[5] = serial >> 8; + return 0; +} + +static u32 etherh_regoffsets[16]; +static u32 etherm_regoffsets[16]; + +static int __init +etherh_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct ei_device *ei_local; + struct net_device *dev; + struct etherh_priv *eh; + const char *dev_type; + int i, size, ret; + + etherh_banner(); + + dev = init_etherdev(NULL, sizeof(struct etherh_priv)); + if (!dev) { + ret = -ENOMEM; + goto out; + } + + /* + * init_etherdev allocs and zeros dev->priv + */ + eh = dev->priv; + + spin_lock_init(&eh->eidev.page_lock); + + SET_MODULE_OWNER(dev); + + dev->open = etherh_open; + dev->stop = etherh_close; + dev->set_config = etherh_set_config; + dev->irq = ec->irq; + dev->base_addr = ecard_address(ec, ECARD_MEMC, 0); + + /* + * IRQ and control port handling + */ + if (ec->irq != 11) { + ec->ops = ðerh_ops; + ec->irq_data = eh; + } + eh->ctrl = 0; + eh->id = ec->cid.product; + + switch (ec->cid.product) { + case PROD_ANT_ETHERM: + etherm_addr(dev->dev_addr); + dev->base_addr += ETHERM_NS8390; + dev->mem_start = dev->base_addr + ETHERM_DATAPORT; + eh->ctrl_port = dev->base_addr + ETHERM_CTRLPORT; + break; + + case PROD_I3_ETHERLAN500: + etherh_addr(dev->dev_addr, ec); + dev->base_addr += ETHERH500_NS8390; + dev->mem_start = dev->base_addr + ETHERH500_DATAPORT; + eh->ctrl_port = ecard_address (ec, ECARD_IOC, ECARD_FAST) + + ETHERH500_CTRLPORT; + break; + + case PROD_I3_ETHERLAN600: + case PROD_I3_ETHERLAN600A: + etherh_addr(dev->dev_addr, ec); + dev->base_addr += ETHERH600_NS8390; + dev->mem_start = dev->base_addr + ETHERH600_DATAPORT; + eh->ctrl_port = dev->base_addr + ETHERH600_CTRLPORT; + break; + + default: + printk(KERN_ERR "%s: unknown card type %x\n", + dev->name, ec->cid.product); + ret = -ENODEV; + goto free; + } + + size = 16; + if (ec->cid.product == PROD_ANT_ETHERM) + size <<= 3; + + if (!request_region(dev->base_addr, size, dev->name)) { + ret = -EBUSY; + goto free; + } + + if (ethdev_init(dev)) { + ret = -ENODEV; + goto release; + } + + /* + * If we're in the NIC slot, make sure the IRQ is enabled + */ + if (dev->irq == 11) + etherh_set_ctrl(eh, ETHERH_CP_IE); + + /* + * Unfortunately, ethdev_init eventually calls + * ether_setup, which re-writes dev->flags. + */ + switch (ec->cid.product) { + case PROD_ANT_ETHERM: + dev_type = "ANT EtherM"; + dev->if_port = IF_PORT_UNKNOWN; + break; + + case PROD_I3_ETHERLAN500: + dev_type = "i3 EtherH 500"; + dev->if_port = IF_PORT_UNKNOWN; + break; + + case PROD_I3_ETHERLAN600: + dev_type = "i3 EtherH 600"; + dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; + dev->if_port = IF_PORT_10BASET; + break; + + case PROD_I3_ETHERLAN600A: + dev_type = "i3 EtherH 600A"; + dev->flags |= IFF_PORTSEL | IFF_AUTOMEDIA; + dev->if_port = IF_PORT_10BASET; + break; + + default: + dev_type = "unknown"; + break; + } + + printk(KERN_INFO "%s: %s in slot %d, ", + dev->name, dev_type, ec->slot_no); + + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + + ei_local = (struct ei_device *) dev->priv; + if (ec->cid.product == PROD_ANT_ETHERM) { + ei_local->tx_start_page = ETHERM_TX_START_PAGE; + ei_local->stop_page = ETHERM_STOP_PAGE; + ei_local->reg_offset = etherm_regoffsets; + } else { + ei_local->tx_start_page = ETHERH_TX_START_PAGE; + ei_local->stop_page = ETHERH_STOP_PAGE; + ei_local->reg_offset = etherh_regoffsets; + } + + ei_local->name = dev->name; + ei_local->word16 = 1; + ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES; + ei_local->reset_8390 = etherh_reset; + ei_local->block_input = etherh_block_input; + ei_local->block_output = etherh_block_output; + ei_local->get_8390_hdr = etherh_get_header; + ei_local->interface_num = 0; + + etherh_reset(dev); + NS8390_init(dev, 0); + + ecard_set_drvdata(ec, dev); + + return 0; + +release: + release_region(dev->base_addr, 16); +free: + unregister_netdev(dev); + kfree(dev->priv); + kfree(dev); +out: + return ret; +} + +static void __devexit etherh_remove(struct expansion_card *ec) +{ + struct net_device *dev = ecard_get_drvdata(ec); + int size = 16; + + ecard_set_drvdata(ec, NULL); + + unregister_netdev(dev); + if (ec->cid.product == PROD_ANT_ETHERM) + size <<= 3; + release_region(dev->base_addr, size); + kfree(dev); + + ec->ops = NULL; + kfree(ec->irq_data); +} + +static const struct ecard_id etherh_ids[] = { + { MANU_ANT, PROD_ANT_ETHERM }, + { MANU_I3, PROD_I3_ETHERLAN500 }, + { MANU_I3, PROD_I3_ETHERLAN600 }, + { MANU_I3, PROD_I3_ETHERLAN600A }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver etherh_driver = { + .probe = etherh_probe, + .remove = __devexit_p(etherh_remove), + .id_table = etherh_ids, + .drv = { + .name = "etherh", + }, +}; + +static int __init etherh_init(void) +{ + int i; + + for (i = 0; i < 16; i++) { + etherh_regoffsets[i] = i; + etherm_regoffsets[i] = i << 3; + } + + return ecard_register_driver(ðerh_driver); +} + +static void __exit etherh_exit(void) +{ + ecard_remove_driver(ðerh_driver); +} + +module_init(etherh_init); +module_exit(etherh_exit); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 7c79bb82cc20..ed511c0a45e5 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1602,7 +1602,7 @@ config SCSI_MAC53C94 whenever you want). If you want to compile it as a module, say M here and read . -source "drivers/acorn/scsi/Kconfig" +source "drivers/scsi/arm/Kconfig" config JAZZ_ESP bool "MIPS JAZZ FAS216 SCSI support" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 908c44892a47..299f0d04883e 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -114,7 +114,7 @@ obj-$(CONFIG_SCSI_CPQFCTS) += cpqfc.o obj-$(CONFIG_SCSI_LASI700) += lasi700.o 53c700.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o -obj-$(CONFIG_ARCH_ACORN) += ../acorn/scsi/ +obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_CHR_DEV_ST) += st.o obj-$(CONFIG_CHR_DEV_OSST) += osst.o diff --git a/drivers/scsi/arm/Kconfig b/drivers/scsi/arm/Kconfig new file mode 100644 index 000000000000..54b32868aaf7 --- /dev/null +++ b/drivers/scsi/arm/Kconfig @@ -0,0 +1,89 @@ +# +# SCSI driver configuration for Acorn +# +config SCSI_ACORNSCSI_3 + tristate "Acorn SCSI card (aka30) support" + depends on ARCH_ACORN && SCSI + help + This enables support for the Acorn SCSI card (aka30). If you have an + Acorn system with one of these, say Y. If unsure, say N. + +config SCSI_ACORNSCSI_TAGGED_QUEUE + bool "Support SCSI 2 Tagged queueing" + depends on SCSI_ACORNSCSI_3 + help + Say Y here to enable tagged queuing support on the Acorn SCSI card. + + This is a feature of SCSI-2 which improves performance: the host + adapter can send several SCSI commands to a device's queue even if + previous commands haven't finished yet. Some SCSI devices don't + implement this properly, so the safe answer is N. + +config SCSI_ACORNSCSI_SYNC + bool "Support SCSI 2 Synchronous Transfers" + depends on SCSI_ACORNSCSI_3 + help + Say Y here to enable synchronous transfer negotiation with all + targets on the Acorn SCSI card. + + In general, this improves performance; however some SCSI devices + don't implement it properly, so the safe answer is N. + +config SCSI_ARXESCSI + tristate "ARXE SCSI support" + depends on ARCH_ACORN && SCSI + help + Around 1991, Arxe Systems Limited released a high density floppy + disc interface for the Acorn Archimedes range, to allow the use of + HD discs from the then new A5000 on earlier models. This interface + was either sold on its own or with an integral SCSI controller. + Technical details on this NCR53c94-based device are available at + + Say Y here to compile in support for the SCSI controller. + +config SCSI_CUMANA_2 + tristate "CumanaSCSI II support" + depends on ARCH_ACORN && SCSI + help + This enables support for the Cumana SCSI II card. If you have an + Acorn system with one of these, say Y. If unsure, say N. + +config SCSI_EESOXSCSI + tristate "EESOX support" + depends on ARCH_ACORN && SCSI + help + This enables support for the EESOX SCSI card. If you have an Acorn + system with one of these, say Y, otherwise say N. + +config SCSI_POWERTECSCSI + tristate "PowerTec support" + depends on ARCH_ACORN && SCSI + help + This enables support for the Powertec SCSI card on Acorn systems. If + you have one of these, say Y. If unsure, say N. + +comment "The following drivers are not fully supported" + depends on ARCH_ACORN && EXPERIMENTAL + +config SCSI_CUMANA_1 + tristate "CumanaSCSI I support (EXPERIMENTAL)" + depends on ARCH_ACORN && EXPERIMENTAL && SCSI + help + This enables support for the Cumana SCSI I card. If you have an + Acorn system with one of these, say Y. If unsure, say N. + +config SCSI_ECOSCSI + tristate "EcoScsi support (EXPERIMENTAL)" + depends on ARCH_ACORN && EXPERIMENTAL && (ARCH_ARC || ARCH_A5K) && SCSI + help + This enables support for the EcoSCSI card -- a small card that sits + in the Econet socket. If you have an Acorn system with one of these, + say Y. If unsure, say N. + +config SCSI_OAK1 + tristate "Oak SCSI support (EXPERIMENTAL)" + depends on ARCH_ACORN && EXPERIMENTAL && SCSI + help + This enables support for the Oak SCSI card. If you have an Acorn + system with one of these, say Y. If unsure, say N. + diff --git a/drivers/scsi/arm/Makefile b/drivers/scsi/arm/Makefile new file mode 100644 index 000000000000..e8db17924c1d --- /dev/null +++ b/drivers/scsi/arm/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for drivers/scsi/arm +# + +acornscsi_mod-objs := acornscsi.o acornscsi-io.o + +obj-$(CONFIG_SCSI_ACORNSCSI_3) += acornscsi_mod.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_ARXESCSI) += arxescsi.o fas216.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_CUMANA_1) += cumana_1.o +obj-$(CONFIG_SCSI_CUMANA_2) += cumana_2.o fas216.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_ECOSCSI) += ecoscsi.o +obj-$(CONFIG_SCSI_OAK1) += oak.o +obj-$(CONFIG_SCSI_POWERTECSCSI) += powertec.o fas216.o queue.o msgqueue.o +obj-$(CONFIG_SCSI_EESOXSCSI) += eesox.o fas216.o queue.o msgqueue.o diff --git a/drivers/scsi/arm/acornscsi-io.S b/drivers/scsi/arm/acornscsi-io.S new file mode 100644 index 000000000000..93467e6ac923 --- /dev/null +++ b/drivers/scsi/arm/acornscsi-io.S @@ -0,0 +1,145 @@ +/* + * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include + +#include +#include + +#if (IO_BASE == (PCIO_BASE & 0xff000000)) +#define ADDR(off,reg) \ + tst off, $0x80000000 ;\ + mov reg, $IO_BASE ;\ + orreq reg, reg, $(PCIO_BASE & 0x00ff0000) +#else +#define ADDR(off,reg) \ + tst off, $0x80000000 ;\ + movne reg, $IO_BASE ;\ + moveq reg, $(PCIO_BASE & 0xff000000) ;\ + orreq reg, reg, $(PCIO_BASE & 0x00ff0000) +#endif + +@ Purpose: transfer a block of data from the acorn scsi card to memory +@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) +@ Returns: nothing + + .align +ENTRY(__acornscsi_in) + stmfd sp!, {r4 - r7, lr} + bic r0, r0, #3 + mov lr, #0xff + orr lr, lr, #0xff00 +acornscsi_in16lp: + subs r2, r2, #16 + bmi acornscsi_in8 + ldmia r0!, {r3, r4, r5, r6} + and r3, r3, lr + orr r3, r3, r4, lsl #16 + and r4, r5, lr + orr r4, r4, r6, lsl #16 + ldmia r0!, {r5, r6, r7, ip} + and r5, r5, lr + orr r5, r5, r6, lsl #16 + and r6, r7, lr + orr r6, r6, ip, lsl #16 + stmia r1!, {r3 - r6} + bne acornscsi_in16lp + LOADREGS(fd, sp!, {r4 - r7, pc}) + +acornscsi_in8: adds r2, r2, #8 + bmi acornscsi_in4 + ldmia r0!, {r3, r4, r5, r6} + and r3, r3, lr + orr r3, r3, r4, lsl #16 + and r4, r5, lr + orr r4, r4, r6, lsl #16 + stmia r1!, {r3 - r4} + LOADREGS(eqfd, sp!, {r4 - r7, pc}) + sub r2, r2, #8 + +acornscsi_in4: adds r2, r2, #4 + bmi acornscsi_in2 + ldmia r0!, {r3, r4} + and r3, r3, lr + orr r3, r3, r4, lsl #16 + str r3, [r1], #4 + LOADREGS(eqfd, sp!, {r4 - r7, pc}) + sub r2, r2, #4 + +acornscsi_in2: adds r2, r2, #2 + ldr r3, [r0], #4 + and r3, r3, lr + strb r3, [r1], #1 + mov r3, r3, lsr #8 + strplb r3, [r1], #1 + LOADREGS(fd, sp!, {r4 - r7, pc}) + +@ Purpose: transfer a block of data from memory to the acorn scsi card +@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length) +@ Returns: nothing + +ENTRY(__acornscsi_out) + stmfd sp!, {r4 - r6, lr} + bic r0, r0, #3 +acornscsi_out16lp: + subs r2, r2, #16 + bmi acornscsi_out8 + ldmia r1!, {r4, r6, ip, lr} + mov r3, r4, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, r4, lsr #16 + orr r4, r4, r4, lsl #16 + mov r5, r6, lsl #16 + orr r5, r5, r5, lsr #16 + mov r6, r6, lsr #16 + orr r6, r6, r6, lsl #16 + stmia r0!, {r3, r4, r5, r6} + mov r3, ip, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, ip, lsr #16 + orr r4, r4, r4, lsl #16 + mov ip, lr, lsl #16 + orr ip, ip, ip, lsr #16 + mov lr, lr, lsr #16 + orr lr, lr, lr, lsl #16 + stmia r0!, {r3, r4, ip, lr} + bne acornscsi_out16lp + LOADREGS(fd, sp!, {r4 - r6, pc}) + +acornscsi_out8: adds r2, r2, #8 + bmi acornscsi_out4 + ldmia r1!, {r4, r6} + mov r3, r4, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, r4, lsr #16 + orr r4, r4, r4, lsl #16 + mov r5, r6, lsl #16 + orr r5, r5, r5, lsr #16 + mov r6, r6, lsr #16 + orr r6, r6, r6, lsl #16 + stmia r0!, {r3, r4, r5, r6} + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + + sub r2, r2, #8 +acornscsi_out4: adds r2, r2, #4 + bmi acornscsi_out2 + ldr r4, [r1], #4 + mov r3, r4, lsl #16 + orr r3, r3, r3, lsr #16 + mov r4, r4, lsr #16 + orr r4, r4, r4, lsl #16 + stmia r0!, {r3, r4} + LOADREGS(eqfd, sp!, {r4 - r6, pc}) + + sub r2, r2, #4 +acornscsi_out2: adds r2, r2, #2 + ldr r3, [r1], #2 + strb r3, [r0], #1 + mov r3, r3, lsr #8 + strplb r3, [r0], #1 + LOADREGS(fd, sp!, {r4 - r6, pc}) + diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c new file mode 100644 index 000000000000..c891ca6018dc --- /dev/null +++ b/drivers/scsi/arm/acornscsi.c @@ -0,0 +1,3126 @@ +/* + * linux/drivers/acorn/scsi/acornscsi.c + * + * Acorn SCSI 3 driver + * By R.M.King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Abandoned using the Select and Transfer command since there were + * some nasty races between our software and the target devices that + * were not easy to solve, and the device errata had a lot of entries + * for this command, some of them quite nasty... + * + * Changelog: + * 26-Sep-1997 RMK Re-jigged to use the queue module. + * Re-coded state machine to be based on driver + * state not scsi state. Should be easier to debug. + * Added acornscsi_release to clean up properly. + * Updated proc/scsi reporting. + * 05-Oct-1997 RMK Implemented writing to SCSI devices. + * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/ + * reconnect race condition causing a warning message. + * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. + * 15-Oct-1997 RMK Improved handling of commands. + * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. + * 13-Dec-1998 RMK Better abort code and command handling. Extra state + * transitions added to allow dodgy devices to work. + */ +#define DEBUG_NO_WRITE 1 +#define DEBUG_QUEUES 2 +#define DEBUG_DMA 4 +#define DEBUG_ABORT 8 +#define DEBUG_DISCON 16 +#define DEBUG_CONNECT 32 +#define DEBUG_PHASES 64 +#define DEBUG_WRITE 128 +#define DEBUG_LINK 256 +#define DEBUG_MESSAGES 512 +#define DEBUG_RESET 1024 +#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ + DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ + DEBUG_DMA|DEBUG_QUEUES) + +/* DRIVER CONFIGURATION + * + * SCSI-II Tagged queue support. + * + * I don't have any SCSI devices that support it, so it is totally untested + * (except to make sure that it doesn't interfere with any non-tagging + * devices). It is not fully implemented either - what happens when a + * tagging device reconnects??? + * + * You can tell if you have a device that supports tagged queueing my + * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported + * as '2 TAG'. + * + * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config + * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug, + * comment out the undef. + */ +#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE +/* + * SCSI-II Linked command support. + * + * The higher level code doesn't support linked commands yet, and so the option + * is undef'd here. + */ +#undef CONFIG_SCSI_ACORNSCSI_LINK +/* + * SCSI-II Synchronous transfer support. + * + * Tried and tested... + * + * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max) + * SDTR_PERIOD - period of REQ signal (min=125, max=1020) + * DEFAULT_PERIOD - default REQ period. + */ +#define SDTR_SIZE 12 +#define SDTR_PERIOD 125 +#define DEFAULT_PERIOD 500 + +/* + * Debugging information + * + * DEBUG - bit mask from list above + * DEBUG_TARGET - is defined to the target number if you want to debug + * a specific target. [only recon/write/dma]. + */ +#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE) +/* only allow writing to SCSI device 0 */ +#define NO_WRITE 0xFE +/*#define DEBUG_TARGET 2*/ +/* + * Select timeout time (in 10ms units) + * + * This is the timeout used between the start of selection and the WD33C93 + * chip deciding that the device isn't responding. + */ +#define TIMEOUT_TIME 10 +/* + * Define this if you want to have verbose explaination of SCSI + * status/messages. + */ +#undef CONFIG_ACORNSCSI_CONSTANTS +/* + * Define this if you want to use the on board DMAC [don't remove this option] + * If not set, then use PIO mode (not currently supported). + */ +#define USE_DMAC + +/* + * ==================================================================================== + */ + +#ifdef DEBUG_TARGET +#define DBG(cmd,xxx...) \ + if (cmd->device->id == DEBUG_TARGET) { \ + xxx; \ + } +#else +#define DBG(cmd,xxx...) xxx +#endif + +#ifndef STRINGIFY +#define STRINGIFY(x) #x +#endif +#define STRx(x) STRINGIFY(x) +#define NO_WRITE_STR STRx(NO_WRITE) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "acornscsi.h" +#include "msgqueue.h" +#include "scsi.h" + +#include + +#define VER_MAJOR 2 +#define VER_MINOR 0 +#define VER_PATCH 6 + +#ifndef ABORT_TAG +#define ABORT_TAG 0xd +#else +#error "Yippee! ABORT TAG is now defined! Remove this error!" +#endif + +#ifdef CONFIG_SCSI_ACORNSCSI_LINK +#error SCSI2 LINKed commands not supported (yet)! +#endif + +#ifdef USE_DMAC +/* + * DMAC setup parameters + */ +#define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP) +#define INIT_DEVCON1 (DEVCON1_BHLD) +#define DMAC_READ (MODECON_READ) +#define DMAC_WRITE (MODECON_WRITE) +#define INIT_SBICDMA (CTRL_DMABURST) + +#define scsi_xferred have_data_in + +/* + * Size of on-board DMA buffer + */ +#define DMAC_BUFFER_SIZE 65536 +#endif + +#define STATUS_BUFFER_TO_PRINT 24 + +unsigned int sdtr_period = SDTR_PERIOD; +unsigned int sdtr_size = SDTR_SIZE; + +static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); +static int acornscsi_reconnect_finish(AS_Host *host); +static void acornscsi_dma_cleanup(AS_Host *host); +static void acornscsi_abortcmd(AS_Host *host, unsigned char tag); + +/* ==================================================================================== + * Miscellaneous + */ + +static inline void +sbic_arm_write(unsigned int io_port, int reg, int value) +{ + __raw_writeb(reg, io_port); + __raw_writeb(value, io_port + 4); +} + +#define sbic_arm_writenext(io,val) \ + __raw_writeb((val), (io) + 4) + +static inline +int sbic_arm_read(unsigned int io_port, int reg) +{ + if(reg == ASR) + return __raw_readl(io_port) & 255; + __raw_writeb(reg, io_port); + return __raw_readl(io_port + 4) & 255; +} + +#define sbic_arm_readnext(io) \ + __raw_readb((io) + 4) + +#ifdef USE_DMAC +#define dmac_read(io_port,reg) \ + inb((io_port) + (reg)) + +#define dmac_write(io_port,reg,value) \ + ({ outb((value), (io_port) + (reg)); }) + +#define dmac_clearintr(io_port) \ + ({ outb(0, (io_port)); }) + +static inline +unsigned int dmac_address(unsigned int io_port) +{ + return dmac_read(io_port, TXADRHI) << 16 | + dmac_read(io_port, TXADRMD) << 8 | + dmac_read(io_port, TXADRLO); +} + +static +void acornscsi_dumpdma(AS_Host *host, char *where) +{ + unsigned int mode, addr, len; + + mode = dmac_read(host->dma.io_port, MODECON); + addr = dmac_address(host->dma.io_port); + len = dmac_read(host->dma.io_port, TXCNTHI) << 8 | + dmac_read(host->dma.io_port, TXCNTLO); + + printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", + host->host->host_no, where, + mode, addr, (len + 1) & 0xffff, + dmac_read(host->dma.io_port, MASKREG)); + + printk("DMA @%06x, ", host->dma.start_addr); + printk("BH @%p +%04x, ", host->scsi.SCp.ptr, + host->scsi.SCp.this_residual); + printk("DT @+%04x ST @+%04x", host->dma.transferred, + host->scsi.SCp.scsi_xferred); + printk("\n"); +} +#endif + +static +unsigned long acornscsi_sbic_xfcount(AS_Host *host) +{ + unsigned long length; + + length = sbic_arm_read(host->scsi.io_port, TRANSCNTH) << 16; + length |= sbic_arm_readnext(host->scsi.io_port) << 8; + length |= sbic_arm_readnext(host->scsi.io_port); + + return length; +} + +static int +acornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg) +{ + int asr; + + do { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if ((asr & stat_mask) == stat) + return 0; + + udelay(1); + } while (--timeout); + + printk("scsi%d: timeout while %s\n", host->host->host_no, msg); + + return -1; +} + +static +int acornscsi_sbic_issuecmd(AS_Host *host, int command) +{ + if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) + return -1; + + sbic_arm_write(host->scsi.io_port, CMND, command); + + return 0; +} + +static void +acornscsi_csdelay(unsigned int cs) +{ + unsigned long target_jiffies, flags; + + target_jiffies = jiffies + 1 + cs * HZ / 100; + + local_save_flags(flags); + local_irq_enable(); + + while (time_before(jiffies, target_jiffies)) barrier(); + + local_irq_restore(flags); +} + +static +void acornscsi_resetcard(AS_Host *host) +{ + unsigned int i, timeout; + + /* assert reset line */ + host->card.page_reg = 0x80; + outb(host->card.page_reg, host->card.io_page); + + /* wait 3 cs. SCSI standard says 25ms. */ + acornscsi_csdelay(3); + + host->card.page_reg = 0; + outb(host->card.page_reg, host->card.io_page); + + /* + * Should get a reset from the card + */ + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", + host->host->host_no); + + sbic_arm_read(host->scsi.io_port, ASR); + sbic_arm_read(host->scsi.io_port, SSR); + + /* setup sbic - WD33C93A */ + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); + + /* + * Command should cause a reset interrupt + */ + timeout = 1000; + do { + if (inb(host->card.io_intr) & 8) + break; + udelay(1); + } while (--timeout); + + if (timeout == 0) + printk("scsi%d: timeout while resetting card\n", + host->host->host_no); + + sbic_arm_read(host->scsi.io_port, ASR); + if (sbic_arm_read(host->scsi.io_port, SSR) != 0x01) + printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", + host->host->host_no); + + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + + host->card.page_reg = 0x40; + outb(host->card.page_reg, host->card.io_page); + + /* setup dmac - uPC71071 */ + dmac_write(host->dma.io_port, INIT, 0); +#ifdef USE_DMAC + dmac_write(host->dma.io_port, INIT, INIT_8BIT); + dmac_write(host->dma.io_port, CHANNEL, CHANNEL_0); + dmac_write(host->dma.io_port, DEVCON0, INIT_DEVCON0); + dmac_write(host->dma.io_port, DEVCON1, INIT_DEVCON1); +#endif + + host->SCpnt = NULL; + host->scsi.phase = PHASE_IDLE; + host->scsi.disconnectable = 0; + + memset(host->busyluns, 0, sizeof(host->busyluns)); + + for (i = 0; i < 8; i++) { + host->device[i].sync_state = SYNC_NEGOCIATE; + host->device[i].disconnect_ok = 1; + } + + /* wait 25 cs. SCSI standard says 250ms. */ + acornscsi_csdelay(25); +} + +/*============================================================================================= + * Utility routines (eg. debug) + */ +#ifdef CONFIG_ACORNSCSI_CONSTANTS +static char *acornscsi_interrupttype[] = { + "rst", "suc", "p/a", "3", + "term", "5", "6", "7", + "serv", "9", "a", "b", + "c", "d", "e", "f" +}; + +static signed char acornscsi_map[] = { + 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + +static char *acornscsi_interruptcode[] = { + /* 0 */ + "reset - normal mode", /* 00 */ + "reset - advanced mode", /* 01 */ + + /* 2 */ + "sel", /* 11 */ + "sel+xfer", /* 16 */ + "data-out", /* 18 */ + "data-in", /* 19 */ + "cmd", /* 1A */ + "stat", /* 1B */ + "??-out", /* 1C */ + "??-in", /* 1D */ + "msg-out", /* 1E */ + "msg-in", /* 1F */ + + /* 12 */ + "/ACK asserted", /* 20 */ + "save-data-ptr", /* 21 */ + "{re}sel", /* 22 */ + + /* 15 */ + "inv cmd", /* 40 */ + "unexpected disconnect", /* 41 */ + "sel timeout", /* 42 */ + "P err", /* 43 */ + "P err+ATN", /* 44 */ + "bad status byte", /* 47 */ + + /* 21 */ + "resel, no id", /* 80 */ + "resel", /* 81 */ + "discon", /* 85 */ +}; + +static +void print_scsi_status(unsigned int ssr) +{ + if (acornscsi_map[ssr] != -1) + printk("%s:%s", + acornscsi_interrupttype[(ssr >> 4)], + acornscsi_interruptcode[acornscsi_map[ssr]]); + else + printk("%X:%X", ssr >> 4, ssr & 0x0f); +} +#endif + +static +void print_sbic_status(int asr, int ssr, int cmdphase) +{ +#ifdef CONFIG_ACORNSCSI_CONSTANTS + printk("sbic: %c%c%c%c%c%c ", + asr & ASR_INT ? 'I' : 'i', + asr & ASR_LCI ? 'L' : 'l', + asr & ASR_BSY ? 'B' : 'b', + asr & ASR_CIP ? 'C' : 'c', + asr & ASR_PE ? 'P' : 'p', + asr & ASR_DBR ? 'D' : 'd'); + printk("scsi: "); + print_scsi_status(ssr); + printk(" ph %02X\n", cmdphase); +#else + printk("sbic: %02X scsi: %X:%X ph: %02X\n", + asr, (ssr & 0xf0)>>4, ssr & 0x0f, cmdphase); +#endif +} + +static void +acornscsi_dumplogline(AS_Host *host, int target, int line) +{ + unsigned long prev; + signed int ptr; + + ptr = host->status_ptr[target] - STATUS_BUFFER_TO_PRINT; + if (ptr < 0) + ptr += STATUS_BUFFER_SIZE; + + printk("%c: %3s:", target == 8 ? 'H' : '0' + target, + line == 0 ? "ph" : line == 1 ? "ssr" : "int"); + + prev = host->status[target][ptr].when; + + for (; ptr != host->status_ptr[target]; ptr = (ptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + unsigned long time_diff; + + if (!host->status[target][ptr].when) + continue; + + switch (line) { + case 0: + printk("%c%02X", host->status[target][ptr].irq ? '-' : ' ', + host->status[target][ptr].ph); + break; + + case 1: + printk(" %02X", host->status[target][ptr].ssr); + break; + + case 2: + time_diff = host->status[target][ptr].when - prev; + prev = host->status[target][ptr].when; + if (time_diff == 0) + printk("==^"); + else if (time_diff >= 100) + printk(" "); + else + printk(" %02ld", time_diff); + break; + } + } + + printk("\n"); +} + +static +void acornscsi_dumplog(AS_Host *host, int target) +{ + do { + acornscsi_dumplogline(host, target, 0); + acornscsi_dumplogline(host, target, 1); + acornscsi_dumplogline(host, target, 2); + + if (target == 8) + break; + + target = 8; + } while (1); +} + +static +char acornscsi_target(AS_Host *host) +{ + if (host->SCpnt) + return '0' + host->SCpnt->device->id; + return 'H'; +} + +/* + * Prototype: cmdtype_t acornscsi_cmdtype(int command) + * Purpose : differentiate READ from WRITE from other commands + * Params : command - command to interpret + * Returns : CMD_READ - command reads data, + * CMD_WRITE - command writes data, + * CMD_MISC - everything else + */ +static inline +cmdtype_t acornscsi_cmdtype(int command) +{ + switch (command) { + case WRITE_6: case WRITE_10: case WRITE_12: + return CMD_WRITE; + case READ_6: case READ_10: case READ_12: + return CMD_READ; + default: + return CMD_MISC; + } +} + +/* + * Prototype: int acornscsi_datadirection(int command) + * Purpose : differentiate between commands that have a DATA IN phase + * and a DATA OUT phase + * Params : command - command to interpret + * Returns : DATADIR_OUT - data out phase expected + * DATADIR_IN - data in phase expected + */ +static +datadir_t acornscsi_datadirection(int command) +{ + switch (command) { + case CHANGE_DEFINITION: case COMPARE: case COPY: + case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT: + case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER: + case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case WRITE_6: case WRITE_10: case WRITE_VERIFY: + case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME: + case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12: + case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW: + case MEDIUM_SCAN: case SEND_VOLUME_TAG: case 0xea: + return DATADIR_OUT; + default: + return DATADIR_IN; + } +} + +/* + * Purpose : provide values for synchronous transfers with 33C93. + * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting + * Modified by Russell King for 8MHz WD33C93A + */ +static struct sync_xfer_tbl { + unsigned int period_ns; + unsigned char reg_value; +} sync_xfer_table[] = { + { 1, 0x20 }, { 249, 0x20 }, { 374, 0x30 }, + { 499, 0x40 }, { 624, 0x50 }, { 749, 0x60 }, + { 874, 0x70 }, { 999, 0x00 }, { 0, 0 } +}; + +/* + * Prototype: int acornscsi_getperiod(unsigned char syncxfer) + * Purpose : period for the synchronous transfer setting + * Params : syncxfer SYNCXFER register value + * Returns : period in ns. + */ +static +int acornscsi_getperiod(unsigned char syncxfer) +{ + int i; + + syncxfer &= 0xf0; + if (syncxfer == 0x10) + syncxfer = 0; + + for (i = 1; sync_xfer_table[i].period_ns; i++) + if (syncxfer == sync_xfer_table[i].reg_value) + return sync_xfer_table[i].period_ns; + return 0; +} + +/* + * Prototype: int round_period(unsigned int period) + * Purpose : return index into above table for a required REQ period + * Params : period - time (ns) for REQ + * Returns : table index + * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting + */ +static inline +int round_period(unsigned int period) +{ + int i; + + for (i = 1; sync_xfer_table[i].period_ns; i++) { + if ((period <= sync_xfer_table[i].period_ns) && + (period > sync_xfer_table[i - 1].period_ns)) + return i; + } + return 7; +} + +/* + * Prototype: unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) + * Purpose : calculate value for 33c93s SYNC register + * Params : period - time (ns) for REQ + * offset - offset in bytes between REQ/ACK + * Returns : value for SYNC register + * Copyright: Copyright (c) 1996 John Shifflett, GeoLog Consulting + */ +static +unsigned char calc_sync_xfer(unsigned int period, unsigned int offset) +{ + return sync_xfer_table[round_period(period)].reg_value | + ((offset < SDTR_SIZE) ? offset : SDTR_SIZE); +} + +/* ==================================================================================== + * Command functions + */ +/* + * Function: acornscsi_kick(AS_Host *host) + * Purpose : kick next command to interface + * Params : host - host to send command to + * Returns : INTR_IDLE if idle, otherwise INTR_PROCESSING + * Notes : interrupts are always disabled! + */ +static +intr_ret_t acornscsi_kick(AS_Host *host) +{ + int from_queue = 0; + Scsi_Cmnd *SCpnt; + + /* first check to see if a command is waiting to be executed */ + SCpnt = host->origSCpnt; + host->origSCpnt = NULL; + + /* retrieve next command */ + if (!SCpnt) { + SCpnt = queue_remove_exclude(&host->queues.issue, host->busyluns); + if (!SCpnt) + return INTR_IDLE; + + from_queue = 1; + } + + if (host->scsi.disconnectable && host->SCpnt) { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); + host->scsi.disconnectable = 0; +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: moved command to disconnected queue\n", + host->host->host_no, acornscsi_target(host))); +#endif + host->SCpnt = NULL; + } + + /* + * If we have an interrupt pending, then we may have been reselected. + * In this case, we don't want to write to the registers + */ + if (!(sbic_arm_read(host->scsi.io_port, ASR) & (ASR_INT|ASR_BSY|ASR_CIP))) { + sbic_arm_write(host->scsi.io_port, DESTID, SCpnt->device->id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_SELWITHATN); + } + + /* + * claim host busy - all of these must happen atomically wrt + * our interrupt routine. Failure means command loss. + */ + host->scsi.phase = PHASE_CONNECTING; + host->SCpnt = SCpnt; + host->scsi.SCp = SCpnt->SCp; + host->dma.xfer_setup = 0; + host->dma.xfer_required = 0; + host->dma.xfer_done = 0; + +#if (DEBUG & (DEBUG_ABORT|DEBUG_CONNECT)) + DBG(SCpnt,printk("scsi%d.%c: starting cmd %02X\n", + host->host->host_no, '0' + SCpnt->device->id, + SCpnt->cmnd[0])); +#endif + + if (from_queue) { +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + /* + * tagged queueing - allocate a new tag to this command + */ + if (SCpnt->device->tagged_queue) { + SCpnt->device->current_tag += 1; + if (SCpnt->device->current_tag == 0) + SCpnt->device->current_tag = 1; + SCpnt->tag = SCpnt->device->current_tag; + } else +#endif + set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + + host->stats.removes += 1; + + switch (acornscsi_cmdtype(SCpnt->cmnd[0])) { + case CMD_WRITE: + host->stats.writes += 1; + break; + case CMD_READ: + host->stats.reads += 1; + break; + case CMD_MISC: + host->stats.miscs += 1; + break; + } + } + + return INTR_PROCESSING; +} + +/* + * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) + * Purpose : complete processing for command + * Params : host - interface that completed + * result - driver byte of result + */ +static +void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) +{ + Scsi_Cmnd *SCpnt = *SCpntp; + + /* clean up */ + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + + host->stats.fins += 1; + + if (SCpnt) { + *SCpntp = NULL; + + acornscsi_dma_cleanup(host); + + SCpnt->result = result << 16 | host->scsi.SCp.Message << 8 | host->scsi.SCp.Status; + + /* + * In theory, this should not happen. In practice, it seems to. + * Only trigger an error if the device attempts to report all happy + * but with untransferred buffers... If we don't do something, then + * data loss will occur. Should we check SCpnt->underflow here? + * It doesn't appear to be set to something meaningful by the higher + * levels all the time. + */ + if (result == DID_OK) { + int xfer_warn = 0; + + if (SCpnt->underflow == 0) { + if (host->scsi.SCp.ptr && + acornscsi_cmdtype(SCpnt->cmnd[0]) != CMD_MISC) + xfer_warn = 1; + } else { + if (host->scsi.SCp.scsi_xferred < SCpnt->underflow || + host->scsi.SCp.scsi_xferred != host->dma.transferred) + xfer_warn = 1; + } + + /* ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.6) + * Targets which break data transfers into multiple + * connections shall end each successful connection + * (except possibly the last) with a SAVE DATA + * POINTER - DISCONNECT message sequence. + * + * This makes it difficult to ensure that a transfer has + * completed. If we reach the end of a transfer during + * the command, then we can only have finished the transfer. + * therefore, if we seem to have some data remaining, this + * is not a problem. + */ + if (host->dma.xfer_done) + xfer_warn = 0; + + if (xfer_warn) { + switch (status_byte(SCpnt->result)) { + case CHECK_CONDITION: + case COMMAND_TERMINATED: + case BUSY: + case QUEUE_FULL: + case RESERVATION_CONFLICT: + break; + + default: + printk(KERN_ERR "scsi%d.H: incomplete data transfer detected: result=%08X command=", + host->host->host_no, SCpnt->result); + print_command(SCpnt->cmnd); + acornscsi_dumpdma(host, "done"); + acornscsi_dumplog(host, SCpnt->device->id); + SCpnt->result &= 0xffff; + SCpnt->result |= DID_ERROR << 16; + } + } + } + + if (!SCpnt->scsi_done) + panic("scsi%d.H: null scsi_done function in acornscsi_done", host->host->host_no); + + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + + SCpnt->scsi_done(SCpnt); + } else + printk("scsi%d: null command in acornscsi_done", host->host->host_no); + + host->scsi.phase = PHASE_IDLE; +} + +/* ==================================================================================== + * DMA routines + */ +/* + * Purpose : update SCSI Data Pointer + * Notes : this will only be one SG entry or less + */ +static +void acornscsi_data_updateptr(AS_Host *host, Scsi_Pointer *SCp, unsigned int length) +{ + SCp->ptr += length; + SCp->this_residual -= length; + + if (SCp->this_residual == 0 && next_SCp(SCp) == 0) + host->dma.xfer_done = 1; +} + +/* + * Prototype: void acornscsi_data_read(AS_Host *host, char *ptr, + * unsigned int start_addr, unsigned int length) + * Purpose : read data from DMA RAM + * Params : host - host to transfer from + * ptr - DRAM address + * start_addr - host mem address + * length - number of bytes to transfer + * Notes : this will only be one SG entry or less + */ +static +void acornscsi_data_read(AS_Host *host, char *ptr, + unsigned int start_addr, unsigned int length) +{ + extern void __acornscsi_in(int port, char *buf, int len); + unsigned int page, offset, len = length; + + page = (start_addr >> 12); + offset = start_addr & ((1 << 12) - 1); + + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + + while (len > 0) { + unsigned int this_len; + + if (len + offset > (1 << 12)) + this_len = (1 << 12) - offset; + else + this_len = len; + + __acornscsi_in(host->card.io_ram + (offset << 1), ptr, this_len); + + offset += this_len; + ptr += this_len; + len -= this_len; + + if (offset == (1 << 12)) { + offset = 0; + page ++; + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + } + } + outb(host->card.page_reg, host->card.io_page); +} + +/* + * Prototype: void acornscsi_data_write(AS_Host *host, char *ptr, + * unsigned int start_addr, unsigned int length) + * Purpose : write data to DMA RAM + * Params : host - host to transfer from + * ptr - DRAM address + * start_addr - host mem address + * length - number of bytes to transfer + * Notes : this will only be one SG entry or less + */ +static +void acornscsi_data_write(AS_Host *host, char *ptr, + unsigned int start_addr, unsigned int length) +{ + extern void __acornscsi_out(int port, char *buf, int len); + unsigned int page, offset, len = length; + + page = (start_addr >> 12); + offset = start_addr & ((1 << 12) - 1); + + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + + while (len > 0) { + unsigned int this_len; + + if (len + offset > (1 << 12)) + this_len = (1 << 12) - offset; + else + this_len = len; + + __acornscsi_out(host->card.io_ram + (offset << 1), ptr, this_len); + + offset += this_len; + ptr += this_len; + len -= this_len; + + if (offset == (1 << 12)) { + offset = 0; + page ++; + outb((page & 0x3f) | host->card.page_reg, host->card.io_page); + } + } + outb(host->card.page_reg, host->card.io_page); +} + +/* ========================================================================================= + * On-board DMA routines + */ +#ifdef USE_DMAC +/* + * Prototype: void acornscsi_dmastop(AS_Host *host) + * Purpose : stop all DMA + * Params : host - host on which to stop DMA + * Notes : This is called when leaving DATA IN/OUT phase, + * or when interface is RESET + */ +static inline +void acornscsi_dma_stop(AS_Host *host) +{ + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "stop")); +#endif +} + +/* + * Function: void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) + * Purpose : setup DMA controller for data transfer + * Params : host - host to setup + * direction - data transfer direction + * Notes : This is called when entering DATA I/O phase, not + * while we're in a DATA I/O phase + */ +static +void acornscsi_dma_setup(AS_Host *host, dmadir_t direction) +{ + unsigned int address, length, mode; + + host->dma.direction = direction; + + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + + if (direction == DMA_OUT) { +#if (DEBUG & DEBUG_NO_WRITE) + if (NO_WRITE & (1 << host->SCpnt->device->id)) { + printk(KERN_CRIT "scsi%d.%c: I can't handle DMA_OUT!\n", + host->host->host_no, acornscsi_target(host)); + return; + } +#endif + mode = DMAC_WRITE; + } else + mode = DMAC_READ; + + /* + * Allocate some buffer space, limited to half the buffer size + */ + length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + if (length) { + host->dma.start_addr = address = host->dma.free_addr; + host->dma.free_addr = (host->dma.free_addr + length) & + (DMAC_BUFFER_SIZE - 1); + + /* + * Transfer data to DMA memory + */ + if (direction == DMA_OUT) + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, + length); + + length -= 1; + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MODECON, mode); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "strt")); +#endif + host->dma.xfer_setup = 1; + } +} + +/* + * Function: void acornscsi_dma_cleanup(AS_Host *host) + * Purpose : ensure that all DMA transfers are up-to-date & host->scsi.SCp is correct + * Params : host - host to finish + * Notes : This is called when a command is: + * terminating, RESTORE_POINTERS, SAVE_POINTERS, DISCONECT + * : This must not return until all transfers are completed. + */ +static +void acornscsi_dma_cleanup(AS_Host *host) +{ + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); + + /* + * Check for a pending transfer + */ + if (host->dma.xfer_required) { + host->dma.xfer_required = 0; + if (host->dma.direction == DMA_IN) + acornscsi_data_read(host, host->dma.xfer_ptr, + host->dma.xfer_start, host->dma.xfer_length); + } + + /* + * Has a transfer been setup? + */ + if (host->dma.xfer_setup) { + unsigned int transferred; + + host->dma.xfer_setup = 0; + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "cupi")); +#endif + + /* + * Calculate number of bytes transferred from DMA. + */ + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; + host->dma.transferred += transferred; + + if (host->dma.direction == DMA_IN) + acornscsi_data_read(host, host->scsi.SCp.ptr, + host->dma.start_addr, transferred); + + /* + * Update SCSI pointers + */ + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "cupo")); +#endif + } +} + +/* + * Function: void acornscsi_dmacintr(AS_Host *host) + * Purpose : handle interrupts from DMAC device + * Params : host - host to process + * Notes : If reading, we schedule the read to main memory & + * allow the transfer to continue. + * : If writing, we fill the onboard DMA memory from main + * memory. + * : Called whenever DMAC finished it's current transfer. + */ +static +void acornscsi_dma_intr(AS_Host *host) +{ + unsigned int address, length, transferred; + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "inti")); +#endif + + dmac_write(host->dma.io_port, MASKREG, MASK_ON); + dmac_clearintr(host->dma.io_intr_clear); + + /* + * Calculate amount transferred via DMA + */ + transferred = dmac_address(host->dma.io_port) - host->dma.start_addr; + host->dma.transferred += transferred; + + /* + * Schedule DMA transfer off board + */ + if (host->dma.direction == DMA_IN) { + host->dma.xfer_start = host->dma.start_addr; + host->dma.xfer_length = transferred; + host->dma.xfer_ptr = host->scsi.SCp.ptr; + host->dma.xfer_required = 1; + } + + acornscsi_data_updateptr(host, &host->scsi.SCp, transferred); + + /* + * Allocate some buffer space, limited to half the on-board RAM size + */ + length = min_t(unsigned int, host->scsi.SCp.this_residual, DMAC_BUFFER_SIZE / 2); + if (length) { + host->dma.start_addr = address = host->dma.free_addr; + host->dma.free_addr = (host->dma.free_addr + length) & + (DMAC_BUFFER_SIZE - 1); + + /* + * Transfer data to DMA memory + */ + if (host->dma.direction == DMA_OUT) + acornscsi_data_write(host, host->scsi.SCp.ptr, host->dma.start_addr, + length); + + length -= 1; + dmac_write(host->dma.io_port, TXCNTLO, length); + dmac_write(host->dma.io_port, TXCNTHI, length >> 8); + dmac_write(host->dma.io_port, TXADRLO, address); + dmac_write(host->dma.io_port, TXADRMD, address >> 8); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); + +#if (DEBUG & DEBUG_DMA) + DBG(host->SCpnt, acornscsi_dumpdma(host, "into")); +#endif + } else { + host->dma.xfer_setup = 0; +#if 0 + /* + * If the interface still wants more, then this is an error. + * We give it another byte, but we also attempt to raise an + * attention condition. We continue giving one byte until + * the device recognises the attention. + */ + if (dmac_read(host->dma.io_port, STATUS) & STATUS_RQ0) { + acornscsi_abortcmd(host, host->SCpnt->tag); + + dmac_write(host->dma.io_port, TXCNTLO, 0); + dmac_write(host->dma.io_port, TXCNTHI, 0); + dmac_write(host->dma.io_port, TXADRLO, 0); + dmac_write(host->dma.io_port, TXADRMD, 0); + dmac_write(host->dma.io_port, TXADRHI, 0); + dmac_write(host->dma.io_port, MASKREG, MASK_OFF); + } +#endif + } +} + +/* + * Function: void acornscsi_dma_xfer(AS_Host *host) + * Purpose : transfer data between AcornSCSI and memory + * Params : host - host to process + */ +static +void acornscsi_dma_xfer(AS_Host *host) +{ + host->dma.xfer_required = 0; + + if (host->dma.direction == DMA_IN) + acornscsi_data_read(host, host->dma.xfer_ptr, + host->dma.xfer_start, host->dma.xfer_length); +} + +/* + * Function: void acornscsi_dma_adjust(AS_Host *host) + * Purpose : adjust DMA pointers & count for bytes transferred to + * SBIC but not SCSI bus. + * Params : host - host to adjust DMA count for + */ +static +void acornscsi_dma_adjust(AS_Host *host) +{ + if (host->dma.xfer_setup) { + signed long transferred; +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) + DBG(host->SCpnt, acornscsi_dumpdma(host, "adji")); +#endif + /* + * Calculate correct DMA address - DMA is ahead of SCSI bus while + * writing. + * host->scsi.SCp.scsi_xferred is the number of bytes + * actually transferred to/from the SCSI bus. + * host->dma.transferred is the number of bytes transferred + * over DMA since host->dma.start_addr was last set. + * + * real_dma_addr = host->dma.start_addr + host->scsi.SCp.scsi_xferred + * - host->dma.transferred + */ + transferred = host->scsi.SCp.scsi_xferred - host->dma.transferred; + if (transferred < 0) + printk("scsi%d.%c: Ack! DMA write correction %ld < 0!\n", + host->host->host_no, acornscsi_target(host), transferred); + else if (transferred == 0) + host->dma.xfer_setup = 0; + else { + transferred += host->dma.start_addr; + dmac_write(host->dma.io_port, TXADRLO, transferred); + dmac_write(host->dma.io_port, TXADRMD, transferred >> 8); + dmac_write(host->dma.io_port, TXADRHI, transferred >> 16); +#if (DEBUG & (DEBUG_DMA|DEBUG_WRITE)) + DBG(host->SCpnt, acornscsi_dumpdma(host, "adjo")); +#endif + } + } +} +#endif + +/* ========================================================================================= + * Data I/O + */ +static int +acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int max_timeout) +{ + unsigned int asr, timeout = max_timeout; + int my_ptr = *ptr; + + while (my_ptr < len) { + asr = sbic_arm_read(host->scsi.io_port, ASR); + + if (asr & ASR_DBR) { + timeout = max_timeout; + + sbic_arm_write(host->scsi.io_port, DATA, bytes[my_ptr++]); + } else if (asr & ASR_INT) + break; + else if (--timeout == 0) + break; + udelay(1); + } + + *ptr = my_ptr; + + return (timeout == 0) ? -1 : 0; +} + +/* + * Function: void acornscsi_sendcommand(AS_Host *host) + * Purpose : send a command to a target + * Params : host - host which is connected to target + */ +static void +acornscsi_sendcommand(AS_Host *host) +{ + Scsi_Cmnd *SCpnt = host->SCpnt; + + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, SCpnt->cmd_len - host->scsi.SCp.sent_command); + + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + + if (acornscsi_write_pio(host, SCpnt->cmnd, + (int *)&host->scsi.SCp.sent_command, SCpnt->cmd_len, 1000000)) + printk("scsi%d: timeout while sending command\n", host->host->host_no); + + host->scsi.phase = PHASE_COMMAND; +} + +static +void acornscsi_sendmessage(AS_Host *host) +{ + unsigned int message_length = msgqueue_msglength(&host->scsi.msgs); + unsigned int msgnr; + struct message *msg; + +#if (DEBUG & DEBUG_MESSAGES) + printk("scsi%d.%c: sending message ", + host->host->host_no, acornscsi_target(host)); +#endif + + switch (message_length) { + case 0: + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 1"); + + sbic_arm_write(host->scsi.io_port, DATA, NOP); + + host->scsi.last_message = NOP; +#if (DEBUG & DEBUG_MESSAGES) + printk("NOP"); +#endif + break; + + case 1: + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + msg = msgqueue_getmsg(&host->scsi.msgs, 0); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "sending message 2"); + + sbic_arm_write(host->scsi.io_port, DATA, msg->msg[0]); + + host->scsi.last_message = msg->msg[0]; +#if (DEBUG & DEBUG_MESSAGES) + print_msg(msg->msg); +#endif + break; + + default: + /* + * ANSI standard says: (SCSI-2 Rev 10c Sect 5.6.14) + * 'When a target sends this (MESSAGE_REJECT) message, it + * shall change to MESSAGE IN phase and send this message + * prior to requesting additional message bytes from the + * initiator. This provides an interlock so that the + * initiator can determine which message byte is rejected. + */ + sbic_arm_write(host->scsi.io_port, TRANSCNTH, 0); + sbic_arm_writenext(host->scsi.io_port, 0); + sbic_arm_writenext(host->scsi.io_port, message_length); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + + msgnr = 0; + while ((msg = msgqueue_getmsg(&host->scsi.msgs, msgnr++)) != NULL) { + unsigned int i; +#if (DEBUG & DEBUG_MESSAGES) + print_msg(msg); +#endif + i = 0; + if (acornscsi_write_pio(host, msg->msg, &i, msg->length, 1000000)) + printk("scsi%d: timeout while sending message\n", host->host->host_no); + + host->scsi.last_message = msg->msg[0]; + if (msg->msg[0] == EXTENDED_MESSAGE) + host->scsi.last_message |= msg->msg[2] << 8; + + if (i != msg->length) + break; + } + break; + } +#if (DEBUG & DEBUG_MESSAGES) + printk("\n"); +#endif +} + +/* + * Function: void acornscsi_readstatusbyte(AS_Host *host) + * Purpose : Read status byte from connected target + * Params : host - host connected to target + */ +static +void acornscsi_readstatusbyte(AS_Host *host) +{ + acornscsi_sbic_issuecmd(host, CMND_XFERINFO|CMND_SBT); + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "reading status byte"); + host->scsi.SCp.Status = sbic_arm_read(host->scsi.io_port, DATA); +} + +/* + * Function: unsigned char acornscsi_readmessagebyte(AS_Host *host) + * Purpose : Read one message byte from connected target + * Params : host - host connected to target + */ +static +unsigned char acornscsi_readmessagebyte(AS_Host *host) +{ + unsigned char message; + + acornscsi_sbic_issuecmd(host, CMND_XFERINFO | CMND_SBT); + + acornscsi_sbic_wait(host, ASR_DBR, ASR_DBR, 1000, "for message byte"); + + message = sbic_arm_read(host->scsi.io_port, DATA); + + /* wait for MSGIN-XFER-PAUSED */ + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after message byte"); + + sbic_arm_read(host->scsi.io_port, SSR); + + return message; +} + +/* + * Function: void acornscsi_message(AS_Host *host) + * Purpose : Read complete message from connected target & action message + * Params : host - host connected to target + */ +static +void acornscsi_message(AS_Host *host) +{ + unsigned char message[16]; + unsigned int msgidx = 0, msglen = 1; + + do { + message[msgidx] = acornscsi_readmessagebyte(host); + + switch (msgidx) { + case 0: + if (message[0] == EXTENDED_MESSAGE || + (message[0] >= 0x20 && message[0] <= 0x2f)) + msglen = 2; + break; + + case 1: + if (message[0] == EXTENDED_MESSAGE) + msglen += message[msgidx]; + break; + } + msgidx += 1; + if (msgidx < msglen) { + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); + + /* wait for next msg-in */ + acornscsi_sbic_wait(host, ASR_INT, ASR_INT, 1000, "for interrupt after negate ack"); + sbic_arm_read(host->scsi.io_port, SSR); + } + } while (msgidx < msglen); + +#if (DEBUG & DEBUG_MESSAGES) + printk("scsi%d.%c: message in: ", + host->host->host_no, acornscsi_target(host)); + print_msg(message); + printk("\n"); +#endif + + if (host->scsi.phase == PHASE_RECONNECTED) { + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) + * 'Whenever a target reconnects to an initiator to continue + * a tagged I/O process, the SIMPLE QUEUE TAG message shall + * be sent immediately following the IDENTIFY message...' + */ + if (message[0] == SIMPLE_QUEUE_TAG) + host->scsi.reconnected.tag = message[1]; + if (acornscsi_reconnect_finish(host)) + host->scsi.phase = PHASE_MSGIN; + } + + switch (message[0]) { + case ABORT: + case ABORT_TAG: + case COMMAND_COMPLETE: + if (host->scsi.phase != PHASE_STATUSIN) { + printk(KERN_ERR "scsi%d.%c: command complete following non-status in phase?\n", + host->host->host_no, acornscsi_target(host)); + acornscsi_dumplog(host, host->SCpnt->device->id); + } + host->scsi.phase = PHASE_DONE; + host->scsi.SCp.Message = message[0]; + break; + + case SAVE_POINTERS: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.20) + * 'The SAVE DATA POINTER message is sent from a target to + * direct the initiator to copy the active data pointer to + * the saved data pointer for the current I/O process. + */ + acornscsi_dma_cleanup(host); + host->SCpnt->SCp = host->scsi.SCp; + host->SCpnt->SCp.sent_command = 0; + host->scsi.phase = PHASE_MSGIN; + break; + + case RESTORE_POINTERS: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.19) + * 'The RESTORE POINTERS message is sent from a target to + * direct the initiator to copy the most recently saved + * command, data, and status pointers for the I/O process + * to the corresponding active pointers. The command and + * status pointers shall be restored to the beginning of + * the present command and status areas.' + */ + acornscsi_dma_cleanup(host); + host->scsi.SCp = host->SCpnt->SCp; + host->scsi.phase = PHASE_MSGIN; + break; + + case DISCONNECT: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 6.4.2) + * 'On those occasions when an error or exception condition occurs + * and the target elects to repeat the information transfer, the + * target may repeat the transfer either issuing a RESTORE POINTERS + * message or by disconnecting without issuing a SAVE POINTERS + * message. When reconnection is completed, the most recent + * saved pointer values are restored.' + */ + acornscsi_dma_cleanup(host); + host->scsi.phase = PHASE_DISCONNECT; + break; + + case MESSAGE_REJECT: +#if 0 /* this isn't needed any more */ + /* + * If we were negociating sync transfer, we don't yet know if + * this REJECT is for the sync transfer or for the tagged queue/wide + * transfer. Re-initiate sync transfer negociation now, and if + * we got a REJECT in response to SDTR, then it'll be set to DONE. + */ + if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) + host->device[host->SCpnt->device->id].sync_state = SYNC_NEGOCIATE; +#endif + + /* + * If we have any messages waiting to go out, then assert ATN now + */ + if (msgqueue_msglength(&host->scsi.msgs)) + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + + switch (host->scsi.last_message) { +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + case HEAD_OF_QUEUE_TAG: + case ORDERED_QUEUE_TAG: + case SIMPLE_QUEUE_TAG: + /* + * ANSI standard says: (Section SCSI-2 Rev. 10c Sect 5.6.17) + * If a target does not implement tagged queuing and a queue tag + * message is received, it shall respond with a MESSAGE REJECT + * message and accept the I/O process as if it were untagged. + */ + printk(KERN_NOTICE "scsi%d.%c: disabling tagged queueing\n", + host->host->host_no, acornscsi_target(host)); + host->SCpnt->device->tagged_queue = 0; + set_bit(host->SCpnt->device->id * 8 + host->SCpnt->device->lun, host->busyluns); + break; +#endif + case EXTENDED_MESSAGE | (EXTENDED_SDTR << 8): + /* + * Target can't handle synchronous transfers + */ + printk(KERN_NOTICE "scsi%d.%c: Using asynchronous transfer\n", + host->host->host_no, acornscsi_target(host)); + host->device[host->SCpnt->device->id].sync_xfer = SYNCHTRANSFER_2DBA; + host->device[host->SCpnt->device->id].sync_state = SYNC_ASYNCHRONOUS; + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); + break; + + default: + break; + } + break; + + case QUEUE_FULL: + /* TODO: target queue is full */ + break; + + case SIMPLE_QUEUE_TAG: + /* tag queue reconnect... message[1] = queue tag. Print something to indicate something happened! */ + printk("scsi%d.%c: reconnect queue tag %02X\n", + host->host->host_no, acornscsi_target(host), + message[1]); + break; + + case EXTENDED_MESSAGE: + switch (message[2]) { +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + case EXTENDED_SDTR: + if (host->device[host->SCpnt->device->id].sync_state == SYNC_SENT_REQUEST) { + /* + * We requested synchronous transfers. This isn't quite right... + * We can only say if this succeeded if we proceed on to execute the + * command from this message. If we get a MESSAGE PARITY ERROR, + * and the target retries fail, then we fallback to asynchronous mode + */ + host->device[host->SCpnt->device->id].sync_state = SYNC_COMPLETED; + printk(KERN_NOTICE "scsi%d.%c: Using synchronous transfer, offset %d, %d ns\n", + host->host->host_no, acornscsi_target(host), + message[4], message[3] * 4); + host->device[host->SCpnt->device->id].sync_xfer = + calc_sync_xfer(message[3] * 4, message[4]); + } else { + unsigned char period, length; + /* + * Target requested synchronous transfers. The agreement is only + * to be in operation AFTER the target leaves message out phase. + */ + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + period = max_t(unsigned int, message[3], sdtr_period / 4); + length = min_t(unsigned int, message[4], sdtr_size); + msgqueue_addmsg(&host->scsi.msgs, 5, EXTENDED_MESSAGE, 3, + EXTENDED_SDTR, period, length); + host->device[host->SCpnt->device->id].sync_xfer = + calc_sync_xfer(period * 4, length); + } + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); + break; +#else + /* We do not accept synchronous transfers. Respond with a + * MESSAGE_REJECT. + */ +#endif + + case EXTENDED_WDTR: + /* The WD33C93A is only 8-bit. We respond with a MESSAGE_REJECT + * to a wide data transfer request. + */ + default: + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); + break; + } + break; + +#ifdef CONFIG_SCSI_ACORNSCSI_LINK + case LINKED_CMD_COMPLETE: + case LINKED_FLG_CMD_COMPLETE: + /* + * We don't support linked commands yet + */ + if (0) { +#if (DEBUG & DEBUG_LINK) + printk("scsi%d.%c: lun %d tag %d linked command complete\n", + host->host->host_no, acornscsi_target(host), host->SCpnt->tag); +#endif + /* + * A linked command should only terminate with one of these messages + * if there are more linked commands available. + */ + if (!host->SCpnt->next_link) { + printk(KERN_WARNING "scsi%d.%c: lun %d tag %d linked command complete, but no next_link\n", + instance->host_no, acornscsi_target(host), host->SCpnt->tag); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); + } else { + Scsi_Cmnd *SCpnt = host->SCpnt; + + acornscsi_dma_cleanup(host); + + host->SCpnt = host->SCpnt->next_link; + host->SCpnt->tag = SCpnt->tag; + SCpnt->result = DID_OK | host->scsi.SCp.Message << 8 | host->Scsi.SCp.Status; + SCpnt->done(SCpnt); + + /* initialise host->SCpnt->SCp */ + } + break; + } +#endif + + default: /* reject message */ + printk(KERN_ERR "scsi%d.%c: unrecognised message %02X, rejecting\n", + host->host->host_no, acornscsi_target(host), + message[0]); + acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); + msgqueue_flush(&host->scsi.msgs); + msgqueue_addmsg(&host->scsi.msgs, 1, MESSAGE_REJECT); + host->scsi.phase = PHASE_MSGIN; + break; + } + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); +} + +/* + * Function: int acornscsi_buildmessages(AS_Host *host) + * Purpose : build the connection messages for a host + * Params : host - host to add messages to + */ +static +void acornscsi_buildmessages(AS_Host *host) +{ +#if 0 + /* does the device need resetting? */ + if (cmd_reset) { + msgqueue_addmsg(&host->scsi.msgs, 1, BUS_DEVICE_RESET); + return; + } +#endif + + msgqueue_addmsg(&host->scsi.msgs, 1, + IDENTIFY(host->device[host->SCpnt->device->id].disconnect_ok, + host->SCpnt->device->lun)); + +#if 0 + /* does the device need the current command aborted */ + if (cmd_aborted) { + acornscsi_abortcmd(host->SCpnt->tag); + return; + } +#endif + +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + if (host->SCpnt->tag) { + unsigned int tag_type; + + if (host->SCpnt->cmnd[0] == REQUEST_SENSE || + host->SCpnt->cmnd[0] == TEST_UNIT_READY || + host->SCpnt->cmnd[0] == INQUIRY) + tag_type = HEAD_OF_QUEUE_TAG; + else + tag_type = SIMPLE_QUEUE_TAG; + msgqueue_addmsg(&host->scsi.msgs, 2, tag_type, host->SCpnt->tag); + } +#endif + +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + if (host->device[host->SCpnt->device->id].sync_state == SYNC_NEGOCIATE) { + host->device[host->SCpnt->device->id].sync_state = SYNC_SENT_REQUEST; + msgqueue_addmsg(&host->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + sdtr_period / 4, sdtr_size); + } +#endif +} + +/* + * Function: int acornscsi_starttransfer(AS_Host *host) + * Purpose : transfer data to/from connected target + * Params : host - host to which target is connected + * Returns : 0 if failure + */ +static +int acornscsi_starttransfer(AS_Host *host) +{ + int residual; + + if (!host->scsi.SCp.ptr /*&& host->scsi.SCp.this_residual*/) { + printk(KERN_ERR "scsi%d.%c: null buffer passed to acornscsi_starttransfer\n", + host->host->host_no, acornscsi_target(host)); + return 0; + } + + residual = host->SCpnt->request_bufflen - host->scsi.SCp.scsi_xferred; + + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, host->device[host->SCpnt->device->id].sync_xfer); + sbic_arm_writenext(host->scsi.io_port, residual >> 16); + sbic_arm_writenext(host->scsi.io_port, residual >> 8); + sbic_arm_writenext(host->scsi.io_port, residual); + acornscsi_sbic_issuecmd(host, CMND_XFERINFO); + return 1; +} + +/* ========================================================================================= + * Connection & Disconnection + */ +/* + * Function : acornscsi_reconnect(AS_Host *host) + * Purpose : reconnect a previously disconnected command + * Params : host - host specific data + * Remarks : SCSI spec says: + * 'The set of active pointers is restored from the set + * of saved pointers upon reconnection of the I/O process' + */ +static +int acornscsi_reconnect(AS_Host *host) +{ + unsigned int target, lun, ok = 0; + + target = sbic_arm_read(host->scsi.io_port, SOURCEID); + + if (!(target & 8)) + printk(KERN_ERR "scsi%d: invalid source id after reselection " + "- device fault?\n", + host->host->host_no); + + target &= 7; + + if (host->SCpnt && !host->scsi.disconnectable) { + printk(KERN_ERR "scsi%d.%d: reconnected while command in " + "progress to target %d?\n", + host->host->host_no, target, host->SCpnt->device->id); + host->SCpnt = NULL; + } + + lun = sbic_arm_read(host->scsi.io_port, DATA) & 7; + + host->scsi.reconnected.target = target; + host->scsi.reconnected.lun = lun; + host->scsi.reconnected.tag = 0; + + if (host->scsi.disconnectable && host->SCpnt && + host->SCpnt->device->id == target && host->SCpnt->device->lun == lun) + ok = 1; + + if (!ok && queue_probetgtlun(&host->queues.disconnected, target, lun)) + ok = 1; + + ADD_STATUS(target, 0x81, host->scsi.phase, 0); + + if (ok) { + host->scsi.phase = PHASE_RECONNECTED; + } else { + /* this doesn't seem to work */ + printk(KERN_ERR "scsi%d.%c: reselected with no command " + "to reconnect with\n", + host->host->host_no, '0' + target); + acornscsi_dumplog(host, target); + acornscsi_abortcmd(host, 0); + if (host->SCpnt) { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); + host->SCpnt = NULL; + } + } + acornscsi_sbic_issuecmd(host, CMND_NEGATEACK); + return !ok; +} + +/* + * Function: int acornscsi_reconect_finish(AS_Host *host) + * Purpose : finish reconnecting a command + * Params : host - host to complete + * Returns : 0 if failed + */ +static +int acornscsi_reconnect_finish(AS_Host *host) +{ + if (host->scsi.disconnectable && host->SCpnt) { + host->scsi.disconnectable = 0; + if (host->SCpnt->device->id == host->scsi.reconnected.target && + host->SCpnt->device->lun == host->scsi.reconnected.lun && + host->SCpnt->tag == host->scsi.reconnected.tag) { +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: reconnected", + host->host->host_no, acornscsi_target(host))); +#endif + } else { + queue_add_cmd_tail(&host->queues.disconnected, host->SCpnt); +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: had to move command " + "to disconnected queue\n", + host->host->host_no, acornscsi_target(host))); +#endif + host->SCpnt = NULL; + } + } + if (!host->SCpnt) { + host->SCpnt = queue_remove_tgtluntag(&host->queues.disconnected, + host->scsi.reconnected.target, + host->scsi.reconnected.lun, + host->scsi.reconnected.tag); +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + DBG(host->SCpnt, printk("scsi%d.%c: had to get command", + host->host->host_no, acornscsi_target(host))); +#endif + } + + if (!host->SCpnt) + acornscsi_abortcmd(host, host->scsi.reconnected.tag); + else { + /* + * Restore data pointer from SAVED pointers. + */ + host->scsi.SCp = host->SCpnt->SCp; +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + printk(", data pointers: [%p, %X]", + host->scsi.SCp.ptr, host->scsi.SCp.this_residual); +#endif + } +#if (DEBUG & (DEBUG_QUEUES|DEBUG_DISCON)) + printk("\n"); +#endif + + host->dma.transferred = host->scsi.SCp.scsi_xferred; + + return host->SCpnt != NULL; +} + +/* + * Function: void acornscsi_disconnect_unexpected(AS_Host *host) + * Purpose : handle an unexpected disconnect + * Params : host - host on which disconnect occurred + */ +static +void acornscsi_disconnect_unexpected(AS_Host *host) +{ + printk(KERN_ERR "scsi%d.%c: unexpected disconnect\n", + host->host->host_no, acornscsi_target(host)); +#if (DEBUG & DEBUG_ABORT) + acornscsi_dumplog(host, 8); +#endif + + acornscsi_done(host, &host->SCpnt, DID_ERROR); +} + +/* + * Function: void acornscsi_abortcmd(AS_host *host, unsigned char tag) + * Purpose : abort a currently executing command + * Params : host - host with connected command to abort + * tag - tag to abort + */ +static +void acornscsi_abortcmd(AS_Host *host, unsigned char tag) +{ + host->scsi.phase = PHASE_ABORTED; + sbic_arm_write(host->scsi.io_port, CMND, CMND_ASSERTATN); + + msgqueue_flush(&host->scsi.msgs); +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + if (tag) + msgqueue_addmsg(&host->scsi.msgs, 2, ABORT_TAG, tag); + else +#endif + msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); +} + +/* ========================================================================================== + * Interrupt routines. + */ +/* + * Function: int acornscsi_sbicintr(AS_Host *host) + * Purpose : handle interrupts from SCSI device + * Params : host - host to process + * Returns : INTR_PROCESS if expecting another SBIC interrupt + * INTR_IDLE if no interrupt + * INTR_NEXT_COMMAND if we have finished processing the command + */ +static +intr_ret_t acornscsi_sbicintr(AS_Host *host, int in_irq) +{ + unsigned int asr, ssr; + + asr = sbic_arm_read(host->scsi.io_port, ASR); + if (!(asr & ASR_INT)) + return INTR_IDLE; + + ssr = sbic_arm_read(host->scsi.io_port, SSR); + +#if (DEBUG & DEBUG_PHASES) + print_sbic_status(asr, ssr, host->scsi.phase); +#endif + + ADD_STATUS(8, ssr, host->scsi.phase, in_irq); + + if (host->SCpnt && !host->scsi.disconnectable) + ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); + + switch (ssr) { + case 0x00: /* reset state - not advanced */ + printk(KERN_ERR "scsi%d: reset in standard mode but wanted advanced mode.\n", + host->host->host_no); + /* setup sbic - WD33C93A */ + sbic_arm_write(host->scsi.io_port, OWNID, OWNID_EAF | host->host->this_id); + sbic_arm_write(host->scsi.io_port, CMND, CMND_RESET); + return INTR_IDLE; + + case 0x01: /* reset state - advanced */ + sbic_arm_write(host->scsi.io_port, CTRL, INIT_SBICDMA | CTRL_IDI); + sbic_arm_write(host->scsi.io_port, TIMEOUT, TIMEOUT_TIME); + sbic_arm_write(host->scsi.io_port, SYNCHTRANSFER, SYNCHTRANSFER_2DBA); + sbic_arm_write(host->scsi.io_port, SOURCEID, SOURCEID_ER | SOURCEID_DSP); + msgqueue_flush(&host->scsi.msgs); + return INTR_IDLE; + + case 0x41: /* unexpected disconnect aborted command */ + acornscsi_disconnect_unexpected(host); + return INTR_NEXT_COMMAND; + } + + switch (host->scsi.phase) { + case PHASE_CONNECTING: /* STATE: command removed from issue queue */ + switch (ssr) { + case 0x11: /* -> PHASE_CONNECTED */ + /* BUS FREE -> SELECTION */ + host->scsi.phase = PHASE_CONNECTED; + msgqueue_flush(&host->scsi.msgs); + host->dma.transferred = host->scsi.SCp.scsi_xferred; + /* 33C93 gives next interrupt indicating bus phase */ + asr = sbic_arm_read(host->scsi.io_port, ASR); + if (!(asr & ASR_INT)) + break; + ssr = sbic_arm_read(host->scsi.io_port, SSR); + ADD_STATUS(8, ssr, host->scsi.phase, 1); + ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, 1); + goto connected; + + case 0x42: /* select timed out */ + /* -> PHASE_IDLE */ + acornscsi_done(host, &host->SCpnt, DID_NO_CONNECT); + return INTR_NEXT_COMMAND; + + case 0x81: /* -> PHASE_RECONNECTED or PHASE_ABORTED */ + /* BUS FREE -> RESELECTION */ + host->origSCpnt = host->SCpnt; + host->SCpnt = NULL; + msgqueue_flush(&host->scsi.msgs); + acornscsi_reconnect(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTING, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); + } + return INTR_PROCESSING; + + connected: + case PHASE_CONNECTED: /* STATE: device selected ok */ + switch (ssr) { +#ifdef NONSTANDARD + case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + /* SELECTION -> COMMAND */ + acornscsi_sendcommand(host); + break; + + case 0x8b: /* -> PHASE_STATUS */ + /* SELECTION -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; +#endif + + case 0x8e: /* -> PHASE_MSGOUT */ + /* SELECTION ->MESSAGE OUT */ + host->scsi.phase = PHASE_MSGOUT; + acornscsi_buildmessages(host); + acornscsi_sendmessage(host); + break; + + /* these should not happen */ + case 0x85: /* target disconnected */ + acornscsi_done(host, &host->SCpnt, DID_ERROR); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_CONNECTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + acornscsi_abortcmd(host, host->SCpnt->tag); + } + return INTR_PROCESSING; + + case PHASE_MSGOUT: /* STATE: connected & sent IDENTIFY message */ + /* + * SCSI standard says that MESSAGE OUT phases can be followed by a + * DATA phase, STATUS phase, MESSAGE IN phase or COMMAND phase + */ + switch (ssr) { + case 0x8a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + case 0x1a: /* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + /* MESSAGE OUT -> COMMAND */ + acornscsi_sendcommand(host); + break; + + case 0x8b: /* -> PHASE_STATUS */ + case 0x1b: /* -> PHASE_STATUS */ + /* MESSAGE OUT -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x8e: /* -> PHASE_MSGOUT */ + /* MESSAGE_OUT(MESSAGE_IN) ->MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x4f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + /* MESSAGE OUT -> MESSAGE IN */ + acornscsi_message(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_MSGOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_COMMAND: /* STATE: connected & command sent */ + switch (ssr) { + case 0x18: /* -> PHASE_DATAOUT */ + /* COMMAND -> DATA OUT */ + if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAOUT; + return INTR_IDLE; + + case 0x19: /* -> PHASE_DATAIN */ + /* COMMAND -> DATA IN */ + if (host->scsi.SCp.sent_command != host->SCpnt->cmd_len) + acornscsi_abortcmd(host, host->SCpnt->tag); + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAIN; + return INTR_IDLE; + + case 0x1b: /* -> PHASE_STATUS */ + /* COMMAND -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + /* COMMAND -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x1f: /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + /* COMMAND -> MESSAGE IN */ + acornscsi_message(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_COMMAND, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DISCONNECT: /* STATE: connected, received DISCONNECT msg */ + if (ssr == 0x85) { /* -> PHASE_IDLE */ + host->scsi.disconnectable = 1; + host->scsi.reconnected.tag = 0; + host->scsi.phase = PHASE_IDLE; + host->stats.disconnects += 1; + } else { + printk(KERN_ERR "scsi%d.%c: PHASE_DISCONNECT, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_NEXT_COMMAND; + + case PHASE_IDLE: /* STATE: disconnected */ + if (ssr == 0x81) /* -> PHASE_RECONNECTED or PHASE_ABORTED */ + acornscsi_reconnect(host); + else { + printk(KERN_ERR "scsi%d.%c: PHASE_IDLE, SSR %02X while idle?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_RECONNECTED: /* STATE: device reconnected to initiator */ + /* + * Command reconnected - if MESGIN, get message - it may be + * the tag. If not, get command out of disconnected queue + */ + /* + * If we reconnected and we're not in MESSAGE IN phase after IDENTIFY, + * reconnect I_T_L command + */ + if (ssr != 0x8f && !acornscsi_reconnect_finish(host)) + return INTR_IDLE; + ADD_STATUS(host->SCpnt->device->id, ssr, host->scsi.phase, in_irq); + switch (ssr) { + case 0x88: /* data out phase */ + /* -> PHASE_DATAOUT */ + /* MESSAGE IN -> DATA OUT */ + acornscsi_dma_setup(host, DMA_OUT); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAOUT; + return INTR_IDLE; + + case 0x89: /* data in phase */ + /* -> PHASE_DATAIN */ + /* MESSAGE IN -> DATA IN */ + acornscsi_dma_setup(host, DMA_IN); + if (!acornscsi_starttransfer(host)) + acornscsi_abortcmd(host, host->SCpnt->tag); + host->scsi.phase = PHASE_DATAIN; + return INTR_IDLE; + + case 0x8a: /* command out */ + /* MESSAGE IN -> COMMAND */ + acornscsi_sendcommand(host);/* -> PHASE_COMMAND, PHASE_COMMANDPAUSED */ + break; + + case 0x8b: /* status in */ + /* -> PHASE_STATUSIN */ + /* MESSAGE IN -> STATUS */ + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x8e: /* message out */ + /* -> PHASE_MSGOUT */ + /* MESSAGE IN -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x8f: /* message in */ + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_RECONNECTED, SSR %02X after reconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DATAIN: /* STATE: transferred data in */ + /* + * This is simple - if we disconnect then the DMA address & count is + * correct. + */ + switch (ssr) { + case 0x19: /* -> PHASE_DATAIN */ + case 0x89: /* -> PHASE_DATAIN */ + acornscsi_abortcmd(host, host->SCpnt->tag); + return INTR_IDLE; + + case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ + /* DATA IN -> STATUS */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* DATA IN -> MESSAGE OUT */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_sendmessage(host); + break; + + case 0x1f: /* message in */ + case 0x4f: /* message in */ + case 0x8f: /* message in */ + /* DATA IN -> MESSAGE IN */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_DATAIN, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DATAOUT: /* STATE: transferred data out */ + /* + * This is more complicated - if we disconnect, the DMA could be 12 + * bytes ahead of us. We need to correct this. + */ + switch (ssr) { + case 0x18: /* -> PHASE_DATAOUT */ + case 0x88: /* -> PHASE_DATAOUT */ + acornscsi_abortcmd(host, host->SCpnt->tag); + return INTR_IDLE; + + case 0x1b: /* -> PHASE_STATUSIN */ + case 0x4b: /* -> PHASE_STATUSIN */ + case 0x8b: /* -> PHASE_STATUSIN */ + /* DATA OUT -> STATUS */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_readstatusbyte(host); + host->scsi.phase = PHASE_STATUSIN; + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* DATA OUT -> MESSAGE OUT */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_sendmessage(host); + break; + + case 0x1f: /* message in */ + case 0x4f: /* message in */ + case 0x8f: /* message in */ + /* DATA OUT -> MESSAGE IN */ + host->scsi.SCp.scsi_xferred = host->SCpnt->request_bufflen - + acornscsi_sbic_xfcount(host); + acornscsi_dma_stop(host); + acornscsi_dma_adjust(host); + acornscsi_message(host); /* -> PHASE_MSGIN, PHASE_DISCONNECT */ + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_DATAOUT, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_STATUSIN: /* STATE: status in complete */ + switch (ssr) { + case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + case 0x8f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + /* STATUS -> MESSAGE IN */ + acornscsi_message(host); + break; + + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* STATUS -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_STATUSIN, SSR %02X instead of MESSAGE_IN?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_MSGIN: /* STATE: message in */ + switch (ssr) { + case 0x1e: /* -> PHASE_MSGOUT */ + case 0x4e: /* -> PHASE_MSGOUT */ + case 0x8e: /* -> PHASE_MSGOUT */ + /* MESSAGE IN -> MESSAGE OUT */ + acornscsi_sendmessage(host); + break; + + case 0x1f: /* -> PHASE_MSGIN, PHASE_DONE, PHASE_DISCONNECT */ + case 0x2f: + case 0x4f: + case 0x8f: + acornscsi_message(host); + break; + + case 0x85: + printk("scsi%d.%c: strange message in disconnection\n", + host->host->host_no, acornscsi_target(host)); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + acornscsi_done(host, &host->SCpnt, DID_ERROR); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_MSGIN, SSR %02X after message in?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_DONE: /* STATE: received status & message */ + switch (ssr) { + case 0x85: /* -> PHASE_IDLE */ + acornscsi_done(host, &host->SCpnt, DID_OK); + return INTR_NEXT_COMMAND; + + case 0x1e: + case 0x8e: + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_DONE, SSR %02X instead of disconnect?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + case PHASE_ABORTED: + switch (ssr) { + case 0x85: + if (host->SCpnt) + acornscsi_done(host, &host->SCpnt, DID_ABORT); + else { + clear_bit(host->scsi.reconnected.target * 8 + host->scsi.reconnected.lun, + host->busyluns); + host->scsi.phase = PHASE_IDLE; + } + return INTR_NEXT_COMMAND; + + case 0x1e: + case 0x2e: + case 0x4e: + case 0x8e: + acornscsi_sendmessage(host); + break; + + default: + printk(KERN_ERR "scsi%d.%c: PHASE_ABORTED, SSR %02X?\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; + + default: + printk(KERN_ERR "scsi%d.%c: unknown driver phase %d\n", + host->host->host_no, acornscsi_target(host), ssr); + acornscsi_dumplog(host, host->SCpnt ? host->SCpnt->device->id : 8); + } + return INTR_PROCESSING; +} + +/* + * Prototype: void acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) + * Purpose : handle interrupts from Acorn SCSI card + * Params : irq - interrupt number + * dev_id - device specific data (AS_Host structure) + * regs - processor registers when interrupt occurred + */ +static irqreturn_t +acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + AS_Host *host = (AS_Host *)dev_id; + intr_ret_t ret; + int iostatus; + int in_irq = 0; + + do { + ret = INTR_IDLE; + + iostatus = inb(host->card.io_intr); + + if (iostatus & 2) { + acornscsi_dma_intr(host); + iostatus = inb(host->card.io_intr); + } + + if (iostatus & 8) + ret = acornscsi_sbicintr(host, in_irq); + + /* + * If we have a transfer pending, start it. + * Only start it if the interface has already started transferring + * it's data + */ + if (host->dma.xfer_required) + acornscsi_dma_xfer(host); + + if (ret == INTR_NEXT_COMMAND) + ret = acornscsi_kick(host); + + in_irq = 1; + } while (ret != INTR_IDLE); + + return IRQ_HANDLED; +} + +/*============================================================================================= + * Interfaces between interrupt handler and rest of scsi code + */ + +/* + * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) + * Purpose : queues a SCSI command + * Params : cmd - SCSI command + * done - function called on completion, with pointer to command descriptor + * Returns : 0, or < 0 on error. + */ +int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; + + if (!done) { + /* there should be some way of rejecting errors like this without panicing... */ + panic("scsi%d: queuecommand called with NULL done function [cmd=%p]", + host->host->host_no, SCpnt); + return -EINVAL; + } + +#if (DEBUG & DEBUG_NO_WRITE) + if (acornscsi_cmdtype(SCpnt->cmnd[0]) == CMD_WRITE && (NO_WRITE & (1 << SCpnt->device->id))) { + printk(KERN_CRIT "scsi%d.%c: WRITE attempted with NO_WRITE flag set\n", + host->host->host_no, '0' + SCpnt->device->id); + SCpnt->result = DID_NO_CONNECT << 16; + done(SCpnt); + return 0; + } +#endif + + SCpnt->scsi_done = done; + SCpnt->host_scribble = NULL; + SCpnt->result = 0; + SCpnt->tag = 0; + SCpnt->SCp.phase = (int)acornscsi_datadirection(SCpnt->cmnd[0]); + SCpnt->SCp.sent_command = 0; + SCpnt->SCp.scsi_xferred = 0; + + init_SCp(SCpnt); + + host->stats.queues += 1; + + { + unsigned long flags; + + if (!queue_add_cmd_ordered(&host->queues.issue, SCpnt)) { + SCpnt->result = DID_ERROR << 16; + done(SCpnt); + return 0; + } + local_irq_save(flags); + if (host->scsi.phase == PHASE_IDLE) + acornscsi_kick(host); + local_irq_restore(flags); + } + return 0; +} + +/* + * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) + * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 + * Params : SCpntp1 - pointer to command to return + * SCpntp2 - pointer to command to check + * result - result to pass back to mid-level done function + * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. + */ +static inline +void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) +{ + Scsi_Cmnd *SCpnt = *SCpntp1; + + if (SCpnt) { + *SCpntp1 = NULL; + + SCpnt->result = result; + SCpnt->scsi_done(SCpnt); + } + + if (SCpnt == *SCpntp2) + *SCpntp2 = NULL; +} + +enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; + +/* + * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : our abort status + */ +static enum res_abort +acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) +{ + enum res_abort res = res_not_running; + + if (queue_remove_cmd(&host->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the devices nor the + * interface know about the command. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on issue queue "); +//#endif + res = res_success; + } else if (queue_remove_cmd(&host->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. Simply + * acknowledge the abort condition, and when the target + * reconnects, we will give it an ABORT message. The + * target should then disconnect, and we will clear + * the busylun bit. + */ +//#if (DEBUG & DEBUG_ABORT) + printk("on disconnected queue "); +//#endif + res = res_success; + } else if (host->SCpnt == SCpnt) { + unsigned long flags; + +//#if (DEBUG & DEBUG_ABORT) + printk("executing "); +//#endif + + local_irq_save(flags); + switch (host->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. We simply + * remove all traces of the command. When the target reconnects, + * we will give it an ABORT message since the command could not + * be found. When the target finally disconnects, we will clear + * the busylun bit. + */ + case PHASE_IDLE: + if (host->scsi.disconnectable) { + host->scsi.disconnectable = 0; + host->SCpnt = NULL; + res = res_success; + } + break; + + /* + * If the command has connected and done nothing further, + * simply force a disconnect. We also need to clear the + * busylun bit. + */ + case PHASE_CONNECTED: + sbic_arm_write(host->scsi.io_port, CMND, CMND_DISCONNECT); + host->SCpnt = NULL; + res = res_success_clear; + break; + + default: + acornscsi_abortcmd(host, host->SCpnt->tag); + res = res_snooze; + } + local_irq_restore(flags); + } else if (host->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + host->origSCpnt = NULL; +//#if (DEBUG & DEBUG_ABORT) + printk("waiting for execution "); +//#endif + res = res_success_clear; + } else + printk("unknown "); + + return res; +} + +/* + * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort a command on this host + * Params : SCpnt - command to abort + * Returns : one of SCSI_ABORT_ macros + */ +int acornscsi_abort(Scsi_Cmnd *SCpnt) +{ + AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata; + int result; + + host->stats.aborts += 1; + +#if (DEBUG & DEBUG_ABORT) + { + int asr, ssr; + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); + + printk(KERN_WARNING "acornscsi_abort: "); + print_sbic_status(asr, ssr, host->scsi.phase); + acornscsi_dumplog(host, SCpnt->device->id); + } +#endif + + printk("scsi%d: ", host->host->host_no); + + switch (acornscsi_do_abort(host, SCpnt)) { + /* + * We managed to find the command and cleared it out. + * We do not expect the command to be executing on the + * target, but we have set the busylun bit. + */ + case res_success_clear: +//#if (DEBUG & DEBUG_ABORT) + printk("clear "); +//#endif + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, host->busyluns); + + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: +//#if (DEBUG & DEBUG_ABORT) + printk("success\n"); +//#endif + SCpnt->result = DID_ABORT << 16; + SCpnt->scsi_done(SCpnt); + result = SCSI_ABORT_SUCCESS; + break; + + /* + * We did find the command, but unfortunately we couldn't + * unhook it from ourselves. Wait some more, and if it + * still doesn't complete, reset the interface. + */ + case res_snooze: +//#if (DEBUG & DEBUG_ABORT) + printk("snooze\n"); +//#endif + result = SCSI_ABORT_SNOOZE; + break; + + /* + * The command could not be found (either because it completed, + * or it got dropped. + */ + default: + case res_not_running: + acornscsi_dumplog(host, SCpnt->device->id); +#if (DEBUG & DEBUG_ABORT) + result = SCSI_ABORT_SNOOZE; +#else + result = SCSI_ABORT_NOT_RUNNING; +#endif +//#if (DEBUG & DEBUG_ABORT) + printk("not running\n"); +//#endif + break; + } + + return result; +} + +/* + * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) + * Purpose : reset a command on this host/reset this host + * Params : SCpnt - command causing reset + * result - what type of reset to perform + * Returns : one of SCSI_RESET_ macros + */ +int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +{ + AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; + Scsi_Cmnd *SCptr; + + host->stats.resets += 1; + +#if (DEBUG & DEBUG_RESET) + { + int asr, ssr; + + asr = sbic_arm_read(host->scsi.io_port, ASR); + ssr = sbic_arm_read(host->scsi.io_port, SSR); + + printk(KERN_WARNING "acornscsi_reset: "); + print_sbic_status(asr, ssr, host->scsi.phase); + acornscsi_dumplog(host, SCpnt->device->id); + } +#endif + + acornscsi_dma_stop(host); + + SCptr = host->SCpnt; + + /* + * do hard reset. This resets all devices on this host, and so we + * must set the reset status on all commands. + */ + acornscsi_resetcard(host); + + /* + * report reset on commands current connected/disconnected + */ + acornscsi_reportstatus(&host->SCpnt, &SCptr, DID_RESET); + + while ((SCptr = queue_remove(&host->queues.disconnected)) != NULL) + acornscsi_reportstatus(&SCptr, &SCpnt, DID_RESET); + + if (SCpnt) { + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + } + + return SCSI_RESET_BUS_RESET | SCSI_RESET_HOST_RESET | SCSI_RESET_SUCCESS; +} + +/*============================================================================================== + * initialisation & miscellaneous support + */ + +/* + * Function: char *acornscsi_info(struct Scsi_Host *host) + * Purpose : return a string describing this interface + * Params : host - host to give information on + * Returns : a constant string + */ +const +char *acornscsi_info(struct Scsi_Host *host) +{ + static char string[100], *p; + + p = string; + + p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d" +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + " SYNC" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + " TAG" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_LINK + " LINK" +#endif +#if (DEBUG & DEBUG_NO_WRITE) + " NOWRITE ("NO_WRITE_STR")" +#endif + , host->hostt->name, host->io_port, host->irq, + VER_MAJOR, VER_MINOR, VER_PATCH); + return string; +} + +int acornscsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + int pos, begin = 0, devidx; + struct Scsi_Host *instance; + Scsi_Device *scd; + AS_Host *host; + char *p = buffer; + + instance = scsi_host_hn_get(host_no); + + if (inout == 1 || !instance) + return -EINVAL; + + host = (AS_Host *)instance->hostdata; + + p += sprintf(p, "AcornSCSI driver v%d.%d.%d" +#ifdef CONFIG_SCSI_ACORNSCSI_SYNC + " SYNC" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE + " TAG" +#endif +#ifdef CONFIG_SCSI_ACORNSCSI_LINK + " LINK" +#endif +#if (DEBUG & DEBUG_NO_WRITE) + " NOWRITE ("NO_WRITE_STR")" +#endif + "\n\n", VER_MAJOR, VER_MINOR, VER_PATCH); + + p += sprintf(p, "SBIC: WD33C93A Address: %08X IRQ : %d\n", + host->scsi.io_port, host->scsi.irq); +#ifdef USE_DMAC + p += sprintf(p, "DMAC: uPC71071 Address: %08X IRQ : %d\n\n", + host->dma.io_port, host->scsi.irq); +#endif + + p += sprintf(p, "Statistics:\n" + "Queued commands: %-10u Issued commands: %-10u\n" + "Done commands : %-10u Reads : %-10u\n" + "Writes : %-10u Others : %-10u\n" + "Disconnects : %-10u Aborts : %-10u\n" + "Resets : %-10u\n\nLast phases:", + host->stats.queues, host->stats.removes, + host->stats.fins, host->stats.reads, + host->stats.writes, host->stats.miscs, + host->stats.disconnects, host->stats.aborts, + host->stats.resets); + + for (devidx = 0; devidx < 9; devidx ++) { + unsigned int statptr, prev; + + p += sprintf(p, "\n%c:", devidx == 8 ? 'H' : ('0' + devidx)); + statptr = host->status_ptr[devidx] - 10; + + if ((signed int)statptr < 0) + statptr += STATUS_BUFFER_SIZE; + + prev = host->status[devidx][statptr].when; + + for (; statptr != host->status_ptr[devidx]; statptr = (statptr + 1) & (STATUS_BUFFER_SIZE - 1)) { + if (host->status[devidx][statptr].when) { + p += sprintf(p, "%c%02X:%02X+%2ld", + host->status[devidx][statptr].irq ? '-' : ' ', + host->status[devidx][statptr].ph, + host->status[devidx][statptr].ssr, + (host->status[devidx][statptr].when - prev) < 100 ? + (host->status[devidx][statptr].when - prev) : 99); + prev = host->status[devidx][statptr].when; + } + } + } + + p += sprintf(p, "\nAttached devices:\n"); + + list_for_each_entry(scd, &instance->my_devices, siblings) { + p += sprintf(p, "Device/Lun TaggedQ Sync\n"); + p += sprintf(p, " %d/%d ", scd->id, scd->lun); + if (scd->tagged_supported) + p += sprintf(p, "%3sabled(%3d) ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + else + p += sprintf(p, "unsupported "); + + if (host->device[scd->id].sync_xfer & 15) + p += sprintf(p, "offset %d, %d ns\n", + host->device[scd->id].sync_xfer & 15, + acornscsi_getperiod(host->device[scd->id].sync_xfer)); + else + p += sprintf(p, "async\n"); + + pos = p - buffer; + if (pos + begin < offset) { + begin += pos; + p = buffer; + } + pos = p - buffer; + if (pos + begin > offset + length) + break; + } + + pos = p - buffer; + + *start = buffer + (offset - begin); + pos -= offset - begin; + + if (pos > length) + pos = length; + + return pos; +} + +static Scsi_Host_Template acornscsi_template = { + .module = THIS_MODULE, + .proc_info = acornscsi_proc_info, + .name = "AcornSCSI", + .info = acornscsi_info, + .queuecommand = acornscsi_queuecmd, +#warning fixme + .abort = acornscsi_abort, + .reset = acornscsi_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .unchecked_isa_dma = 0, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "acornscsi", +}; + +static int __devinit +acornscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + AS_Host *ashost; + int ret = -ENOMEM; + + host = scsi_register(&acornscsi_template, sizeof(AS_Host)); + if (!host) + goto out; + + ashost = (AS_Host *)host->hostdata; + + host->io_port = ecard_address(ec, ECARD_MEMC, 0); + host->irq = ec->irq; + + ashost->host = host; + ashost->scsi.io_port = ioaddr(host->io_port + 0x800); + ashost->scsi.irq = host->irq; + ashost->card.io_intr = POD_SPACE(host->io_port) + 0x800; + ashost->card.io_page = POD_SPACE(host->io_port) + 0xc00; + ashost->card.io_ram = ioaddr(host->io_port); + ashost->dma.io_port = host->io_port + 0xc00; + ashost->dma.io_intr_clear = POD_SPACE(host->io_port) + 0x800; + + ec->irqaddr = (char *)ioaddr(ashost->card.io_intr); + ec->irqmask = 0x0a; + + ret = -EBUSY; + if (!request_region(host->io_port + 0x800, 2, "acornscsi(sbic)")) + goto err_1; + if (!request_region(ashost->card.io_intr, 1, "acornscsi(intr)")) + goto err_2; + if (!request_region(ashost->card.io_page, 1, "acornscsi(page)")) + goto err_3; +#ifdef USE_DMAC + if (!request_region(ashost->dma.io_port, 256, "acornscsi(dmac)")) + goto err_4; +#endif + if (!request_region(host->io_port, 2048, "acornscsi(ram)")) + goto err_5; + + ret = request_irq(host->irq, acornscsi_intr, SA_INTERRUPT, "acornscsi", ashost); + if (ret) { + printk(KERN_CRIT "scsi%d: IRQ%d not free: %d\n", + host->host_no, ashost->scsi.irq, ret); + goto err_6; + } + + memset(&ashost->stats, 0, sizeof (ashost->stats)); + queue_initialise(&ashost->queues.issue); + queue_initialise(&ashost->queues.disconnected); + msgqueue_initialise(&ashost->scsi.msgs); + + acornscsi_resetcard(ashost); + + ret = scsi_add_host(host, &ec->dev); + if (ret == 0) + goto out; + + free_irq(host->irq, ashost); + err_6: + release_region(host->io_port, 2048); + err_5: +#ifdef USE_DMAC + release_region(ashost->dma.io_port, 256); +#endif + err_4: + release_region(ashost->card.io_page, 1); + err_3: + release_region(ashost->card.io_intr, 1); + err_2: + release_region(host->io_port + 0x800, 2); + err_1: + scsi_unregister(host); + out: + return ret; +} + +static void __devexit acornscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + AS_Host *ashost = (AS_Host *)host->hostdata; + + ecard_set_drvdata(ec, NULL); + scsi_remove_host(host); + + /* + * Put card into RESET state + */ + outb(0x80, ashost->card.io_page); + + free_irq(host->irq, ashost); + + release_region(host->io_port + 0x800, 2); + release_region(ashost->card.io_intr, 1); + release_region(ashost->card.io_page, 1); + release_region(ashost->dma.io_port, 256); + release_region(host->io_port, 2048); + + msgqueue_free(&ashost->scsi.msgs); + queue_free(&ashost->queues.disconnected); + queue_free(&ashost->queues.issue); +} + +static const struct ecard_id acornscsi_cids[] = { + { MANU_ACORN, PROD_ACORN_SCSI }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver acornscsi_driver = { + .probe = acornscsi_probe, + .remove = __devexit_p(acornscsi_remove), + .id_table = acornscsi_cids, + .drv = { + .name = "acornscsi", + }, +}; + +static int __init acornscsi_init(void) +{ + return ecard_register_driver(&acornscsi_driver); +} + +static void __exit acornscsi_exit(void) +{ + ecard_remove_driver(&acornscsi_driver); +} + +module_init(acornscsi_init); +module_exit(acornscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("AcornSCSI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/acornscsi.h b/drivers/scsi/arm/acornscsi.h new file mode 100644 index 000000000000..8e4d88d0aba3 --- /dev/null +++ b/drivers/scsi/arm/acornscsi.h @@ -0,0 +1,358 @@ +/* + * linux/drivers/acorn/scsi/acornscsi.h + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Acorn SCSI driver + */ +#ifndef ACORNSCSI_H +#define ACORNSCSI_H + +/* SBIC registers */ +#define OWNID 0 +#define OWNID_FS1 (1<<7) +#define OWNID_FS2 (1<<6) +#define OWNID_EHP (1<<4) +#define OWNID_EAF (1<<3) + +#define CTRL 1 +#define CTRL_DMAMODE (1<<7) +#define CTRL_DMADBAMODE (1<<6) +#define CTRL_DMABURST (1<<5) +#define CTRL_DMAPOLLED 0 +#define CTRL_HHP (1<<4) +#define CTRL_EDI (1<<3) +#define CTRL_IDI (1<<2) +#define CTRL_HA (1<<1) +#define CTRL_HSP (1<<0) + +#define TIMEOUT 2 +#define TOTSECTS 3 +#define TOTHEADS 4 +#define TOTCYLH 5 +#define TOTCYLL 6 +#define LOGADDRH 7 +#define LOGADDRM2 8 +#define LOGADDRM1 9 +#define LOGADDRL 10 +#define SECTORNUM 11 +#define HEADNUM 12 +#define CYLH 13 +#define CYLL 14 +#define TARGETLUN 15 +#define TARGETLUN_TLV (1<<7) +#define TARGETLUN_DOK (1<<6) + +#define CMNDPHASE 16 +#define SYNCHTRANSFER 17 +#define SYNCHTRANSFER_OF0 0x00 +#define SYNCHTRANSFER_OF1 0x01 +#define SYNCHTRANSFER_OF2 0x02 +#define SYNCHTRANSFER_OF3 0x03 +#define SYNCHTRANSFER_OF4 0x04 +#define SYNCHTRANSFER_OF5 0x05 +#define SYNCHTRANSFER_OF6 0x06 +#define SYNCHTRANSFER_OF7 0x07 +#define SYNCHTRANSFER_OF8 0x08 +#define SYNCHTRANSFER_OF9 0x09 +#define SYNCHTRANSFER_OF10 0x0A +#define SYNCHTRANSFER_OF11 0x0B +#define SYNCHTRANSFER_OF12 0x0C +#define SYNCHTRANSFER_8DBA 0x00 +#define SYNCHTRANSFER_2DBA 0x20 +#define SYNCHTRANSFER_3DBA 0x30 +#define SYNCHTRANSFER_4DBA 0x40 +#define SYNCHTRANSFER_5DBA 0x50 +#define SYNCHTRANSFER_6DBA 0x60 +#define SYNCHTRANSFER_7DBA 0x70 + +#define TRANSCNTH 18 +#define TRANSCNTM 19 +#define TRANSCNTL 20 +#define DESTID 21 +#define DESTID_SCC (1<<7) +#define DESTID_DPD (1<<6) + +#define SOURCEID 22 +#define SOURCEID_ER (1<<7) +#define SOURCEID_ES (1<<6) +#define SOURCEID_DSP (1<<5) +#define SOURCEID_SIV (1<<4) + +#define SSR 23 +#define CMND 24 +#define CMND_RESET 0x00 +#define CMND_ABORT 0x01 +#define CMND_ASSERTATN 0x02 +#define CMND_NEGATEACK 0x03 +#define CMND_DISCONNECT 0x04 +#define CMND_RESELECT 0x05 +#define CMND_SELWITHATN 0x06 +#define CMND_SELECT 0x07 +#define CMND_SELECTATNTRANSFER 0x08 +#define CMND_SELECTTRANSFER 0x09 +#define CMND_RESELECTRXDATA 0x0A +#define CMND_RESELECTTXDATA 0x0B +#define CMND_WAITFORSELRECV 0x0C +#define CMND_SENDSTATCMD 0x0D +#define CMND_SENDDISCONNECT 0x0E +#define CMND_SETIDI 0x0F +#define CMND_RECEIVECMD 0x10 +#define CMND_RECEIVEDTA 0x11 +#define CMND_RECEIVEMSG 0x12 +#define CMND_RECEIVEUSP 0x13 +#define CMND_SENDCMD 0x14 +#define CMND_SENDDATA 0x15 +#define CMND_SENDMSG 0x16 +#define CMND_SENDUSP 0x17 +#define CMND_TRANSLATEADDR 0x18 +#define CMND_XFERINFO 0x20 +#define CMND_SBT (1<<7) + +#define DATA 25 +#define ASR 26 +#define ASR_INT (1<<7) +#define ASR_LCI (1<<6) +#define ASR_BSY (1<<5) +#define ASR_CIP (1<<4) +#define ASR_PE (1<<1) +#define ASR_DBR (1<<0) + +/* DMAC registers */ +#define INIT 0x00 +#define INIT_8BIT (1) + +#define CHANNEL 0x80 +#define CHANNEL_0 0x00 +#define CHANNEL_1 0x01 +#define CHANNEL_2 0x02 +#define CHANNEL_3 0x03 + +#define TXCNTLO 0x01 +#define TXCNTHI 0x81 +#define TXADRLO 0x02 +#define TXADRMD 0x82 +#define TXADRHI 0x03 + +#define DEVCON0 0x04 +#define DEVCON0_AKL (1<<7) +#define DEVCON0_RQL (1<<6) +#define DEVCON0_EXW (1<<5) +#define DEVCON0_ROT (1<<4) +#define DEVCON0_CMP (1<<3) +#define DEVCON0_DDMA (1<<2) +#define DEVCON0_AHLD (1<<1) +#define DEVCON0_MTM (1<<0) + +#define DEVCON1 0x84 +#define DEVCON1_WEV (1<<1) +#define DEVCON1_BHLD (1<<0) + +#define MODECON 0x05 +#define MODECON_WOED 0x01 +#define MODECON_VERIFY 0x00 +#define MODECON_READ 0x04 +#define MODECON_WRITE 0x08 +#define MODECON_AUTOINIT 0x10 +#define MODECON_ADDRDIR 0x20 +#define MODECON_DEMAND 0x00 +#define MODECON_SINGLE 0x40 +#define MODECON_BLOCK 0x80 +#define MODECON_CASCADE 0xC0 + +#define STATUS 0x85 +#define STATUS_TC0 (1<<0) +#define STATUS_RQ0 (1<<4) + +#define TEMPLO 0x06 +#define TEMPHI 0x86 +#define REQREG 0x07 +#define MASKREG 0x87 +#define MASKREG_M0 0x01 +#define MASKREG_M1 0x02 +#define MASKREG_M2 0x04 +#define MASKREG_M3 0x08 + +/* miscellaneous internal variables */ + +#define POD_SPACE(x) ((x) + 0xd0000) +#define MASK_ON (MASKREG_M3|MASKREG_M2|MASKREG_M1|MASKREG_M0) +#define MASK_OFF (MASKREG_M3|MASKREG_M2|MASKREG_M1) + +/* + * SCSI driver phases + */ +typedef enum { + PHASE_IDLE, /* we're not planning on doing anything */ + PHASE_CONNECTING, /* connecting to a target */ + PHASE_CONNECTED, /* connected to a target */ + PHASE_MSGOUT, /* message out to device */ + PHASE_RECONNECTED, /* reconnected */ + PHASE_COMMANDPAUSED, /* command partly sent */ + PHASE_COMMAND, /* command all sent */ + PHASE_DATAOUT, /* data out to device */ + PHASE_DATAIN, /* data in from device */ + PHASE_STATUSIN, /* status in from device */ + PHASE_MSGIN, /* message in from device */ + PHASE_DONE, /* finished */ + PHASE_ABORTED, /* aborted */ + PHASE_DISCONNECT, /* disconnecting */ +} phase_t; + +/* + * After interrupt, what to do now + */ +typedef enum { + INTR_IDLE, /* not expecting another IRQ */ + INTR_NEXT_COMMAND, /* start next command */ + INTR_PROCESSING, /* interrupt routine still processing */ +} intr_ret_t; + +/* + * DMA direction + */ +typedef enum { + DMA_OUT, /* DMA from memory to chip */ + DMA_IN /* DMA from chip to memory */ +} dmadir_t; + +/* + * Synchronous transfer state + */ +typedef enum { /* Synchronous transfer state */ + SYNC_ASYNCHRONOUS, /* don't negociate synchronous transfers*/ + SYNC_NEGOCIATE, /* start negociation */ + SYNC_SENT_REQUEST, /* sent SDTR message */ + SYNC_COMPLETED, /* received SDTR reply */ +} syncxfer_t; + +/* + * Command type + */ +typedef enum { /* command type */ + CMD_READ, /* READ_6, READ_10, READ_12 */ + CMD_WRITE, /* WRITE_6, WRITE_10, WRITE_12 */ + CMD_MISC, /* Others */ +} cmdtype_t; + +/* + * Data phase direction + */ +typedef enum { /* Data direction */ + DATADIR_IN, /* Data in phase expected */ + DATADIR_OUT /* Data out phase expected */ +} datadir_t; + +#include "queue.h" +#include "msgqueue.h" + +#define STATUS_BUFFER_SIZE 32 +/* + * This is used to dump the previous states of the SBIC + */ +struct status_entry { + unsigned long when; + unsigned char ssr; + unsigned char ph; + unsigned char irq; + unsigned char unused; +}; + +#define ADD_STATUS(_q,_ssr,_ph,_irq) \ +({ \ + host->status[(_q)][host->status_ptr[(_q)]].when = jiffies; \ + host->status[(_q)][host->status_ptr[(_q)]].ssr = (_ssr); \ + host->status[(_q)][host->status_ptr[(_q)]].ph = (_ph); \ + host->status[(_q)][host->status_ptr[(_q)]].irq = (_irq); \ + host->status_ptr[(_q)] = (host->status_ptr[(_q)] + 1) & (STATUS_BUFFER_SIZE - 1); \ +}) + +/* + * AcornSCSI host specific data + */ +typedef struct acornscsi_hostdata { + /* miscellaneous */ + struct Scsi_Host *host; /* host */ + Scsi_Cmnd *SCpnt; /* currently processing command */ + Scsi_Cmnd *origSCpnt; /* original connecting command */ + + /* driver information */ + struct { + unsigned int io_port; /* base address of WD33C93 */ + unsigned int irq; /* interrupt */ + phase_t phase; /* current phase */ + + struct { + unsigned char target; /* reconnected target */ + unsigned char lun; /* reconnected lun */ + unsigned char tag; /* reconnected tag */ + } reconnected; + + Scsi_Pointer SCp; /* current commands data pointer */ + + MsgQueue_t msgs; + + unsigned short last_message; /* last message to be sent */ + unsigned char disconnectable:1; /* this command can be disconnected */ + } scsi; + + /* statistics information */ + struct { + unsigned int queues; + unsigned int removes; + unsigned int fins; + unsigned int reads; + unsigned int writes; + unsigned int miscs; + unsigned int disconnects; + unsigned int aborts; + unsigned int resets; + } stats; + + /* queue handling */ + struct { + Queue_t issue; /* issue queue */ + Queue_t disconnected; /* disconnected command queue */ + } queues; + + /* per-device info */ + struct { + unsigned char sync_xfer; /* synchronous transfer (SBIC value) */ + syncxfer_t sync_state; /* sync xfer negociation state */ + unsigned char disconnect_ok:1; /* device can disconnect */ + } device[8]; + unsigned long busyluns[64 / sizeof(unsigned long)];/* array of bits indicating LUNs busy */ + + /* DMA info */ + struct { + unsigned int io_port; /* base address of DMA controller */ + unsigned int io_intr_clear; /* address of DMA interrupt clear */ + unsigned int free_addr; /* next free address */ + unsigned int start_addr; /* start address of current transfer */ + dmadir_t direction; /* dma direction */ + unsigned int transferred; /* number of bytes transferred */ + unsigned int xfer_start; /* scheduled DMA transfer start */ + unsigned int xfer_length; /* scheduled DMA transfer length */ + char *xfer_ptr; /* pointer to area */ + unsigned char xfer_required:1; /* set if we need to transfer something */ + unsigned char xfer_setup:1; /* set if DMA is setup */ + unsigned char xfer_done:1; /* set if DMA reached end of BH list */ + } dma; + + /* card info */ + struct { + unsigned int io_intr; /* base address of interrupt id reg */ + unsigned int io_page; /* base address of page reg */ + unsigned int io_ram; /* base address of RAM access */ + unsigned char page_reg; /* current setting of page reg */ + } card; + + unsigned char status_ptr[9]; + struct status_entry status[9][STATUS_BUFFER_SIZE]; +} AS_Host; + +#endif /* ACORNSCSI_H */ diff --git a/drivers/scsi/arm/arxescsi.c b/drivers/scsi/arm/arxescsi.c new file mode 100644 index 000000000000..8e0b75f52140 --- /dev/null +++ b/drivers/scsi/arm/arxescsi.c @@ -0,0 +1,408 @@ +/* + * linux/arch/arm/drivers/scsi/arxescsi.c + * + * Copyright (C) 1997-2000 Russell King, Stefan Hanske + * + * This driver is based on experimentation. Hence, it may have made + * assumptions about the particular card that I have available, and + * may not be reliable! + * + * Changelog: + * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 + * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. + * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card + * enabled writing + * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing + * (arxescsi_pseudo_dma_write) + * 02-04-2000 RMK 0.1.1 Updated for new error handling code. + * 22-10-2000 SH Updated for new registering scheme. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" + +struct arxescsi_info { + FAS216_Info info; + struct expansion_card *ec; +}; + +#define DMADATA_OFFSET (0x200) + +#define DMASTAT_OFFSET (0x600) +#define DMASTAT_DRQ (1 << 0) + +#define CSTATUS_IRQ (1 << 0) + +#define VERSION "1.10 (23/01/2003 2.5.57)" + +/* + * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : 0 if we should not set CMD_WITHDMA for transfer info command + */ +static fasdmatype_t +arxescsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + /* + * We don't do real DMA + */ + return fasdma_pseudo; +} + +static void arxescsi_pseudo_dma_write(unsigned char *addr, unsigned char *base) +{ + __asm__ __volatile__( + " stmdb sp!, {r0-r12}\n" + " mov r3, %0\n" + " mov r1, %1\n" + " add r2, r1, #512\n" + " mov r4, #256\n" + ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n" + " mov r5, r6, lsl #16\n" + " mov r7, r8, lsl #16\n" + ".loop_2: ldrb r0, [r1, #1536]\n" + " tst r0, #1\n" + " beq .loop_2\n" + " stmia r2, {r5-r8}\n\t" + " mov r9, r10, lsl #16\n" + " mov r11, r12, lsl #16\n" + ".loop_3: ldrb r0, [r1, #1536]\n" + " tst r0, #1\n" + " beq .loop_3\n" + " stmia r2, {r9-r12}\n" + " subs r4, r4, #16\n" + " bne .loop_1\n" + " ldmia sp!, {r0-r12}\n" + : + : "r" (addr), "r" (base)); +} + +/* + * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) + * Purpose : handles pseudo DMA + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * transfer - minimum number of bytes we expect to transfer + */ +static void +arxescsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, int transfer) +{ + struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; + unsigned int length, error = 0; + unsigned char *base = info->info.scsi.io_base; + unsigned char *addr; + + length = SCp->this_residual; + addr = SCp->ptr; + + if (direction == DMA_OUT) { + unsigned int word; + while (length > 256) { + if (readb(base + 0x80) & STAT_INT) { + error = 1; + break; + } + arxescsi_pseudo_dma_write(addr, base); + addr += 256; + length -= 256; + } + + if (!error) + while (length > 0) { + if (readb(base + 0x80) & STAT_INT) + break; + + if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) + continue; + + word = *addr | *(addr + 1) << 8; + + writew(word, base + DMADATA_OFFSET); + if (length > 1) { + addr += 2; + length -= 2; + } else { + addr += 1; + length -= 1; + } + } + } + else { + if (transfer && (transfer & 255)) { + while (length >= 256) { + if (readb(base + 0x80) & STAT_INT) { + error = 1; + break; + } + + if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) + continue; + + readsw(base + DMADATA_OFFSET, addr, 256 >> 1); + addr += 256; + length -= 256; + } + } + + if (!(error)) + while (length > 0) { + unsigned long word; + + if (readb(base + 0x80) & STAT_INT) + break; + + if (!(readb(base + DMASTAT_OFFSET) & DMASTAT_DRQ)) + continue; + + word = readw(base + DMADATA_OFFSET); + *addr++ = word; + if (--length > 0) { + *addr++ = word >> 8; + length --; + } + } + } +} + +/* + * Function: int arxescsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void arxescsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + /* + * no DMA to stop + */ +} + +/* + * Function: const char *arxescsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +static const char *arxescsi_info(struct Scsi_Host *host) +{ + struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION); + + return string; +} + +/* + * Function: int arxescsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +static int +arxescsi_proc_info(char *buffer, char **start, off_t offset, int length, + int host_no, int inout) +{ + struct Scsi_Host *host; + struct arxescsi_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + info = (struct arxescsi_info *)host->hostdata; + if (inout == 1) + return -EINVAL; + + p += sprintf(p, "ARXE 16-bit SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + *start = buffer + offset; + pos = p - buffer - offset; + if (pos > length) + pos = length; + + return pos; +} + +static Scsi_Host_Template arxescsi_template = { + .proc_info = arxescsi_proc_info, + .name = "ARXE SCSI card", + .info = arxescsi_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + .can_queue = 0, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "arxescsi", +}; + +static int __devinit +arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct arxescsi_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + if (!request_mem_region(resbase, reslen, "arxescsi")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&arxescsi_template, sizeof(struct arxescsi_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = NO_IRQ; + host->dma_channel = NO_DMA; + + info = (struct arxescsi_info *)host->hostdata; + info->ec = ec; + + info->info.scsi.io_base = base + 0x2000; + info->info.scsi.irq = host->irq; + info->info.scsi.io_shift = 5; + info->info.ifcfg.clockrate = 24; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 0; + info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 0; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; + info->info.dma.setup = arxescsi_dma_setup; + info->info.dma.pseudo = arxescsi_dma_pseudo; + info->info.dma.stop = arxescsi_dma_stop; + + ec->irqaddr = base; + ec->irqmask = CSTATUS_IRQ; + + ret = fas216_init(host); + if (ret) + goto out_unregister; + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + fas216_release(host); + out_unregister: + scsi_unregister(host); + out_unmap: + iounmap(base); + out_region: + release_mem_region(resbase, reslen); + out: + return ret; +} + +static void __devexit arxescsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id arxescsi_cids[] = { + { MANU_ARXE, PROD_ARXE_SCSI }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver arxescsi_driver = { + .probe = arxescsi_probe, + .remove = __devexit_p(arxescsi_remove), + .id_table = arxescsi_cids, + .drv = { + .name = "arxescsi", + }, +}; + +static int __init init_arxe_scsi_driver(void) +{ + return ecard_register_driver(&arxescsi_driver); +} + +static void __exit exit_arxe_scsi_driver(void) +{ + ecard_remove_driver(&arxescsi_driver); +} + +module_init(init_arxe_scsi_driver); +module_exit(exit_arxe_scsi_driver); + +MODULE_AUTHOR("Stefan Hanske"); +MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/scsi/arm/cumana_1.c b/drivers/scsi/arm/cumana_1.c new file mode 100644 index 000000000000..8a953eff5756 --- /dev/null +++ b/drivers/scsi/arm/cumana_1.c @@ -0,0 +1,355 @@ +/* + * Generic Generic NCR5380 driver + * + * Copyright 1995-2002, Russell King + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" + +#include + +#define AUTOSENSE +#define PSEUDO_DMA + +#define CUMANASCSI_PUBLIC_RELEASE 1 + +#define NCR5380_implementation_fields int port, ctrl +#define NCR5380_local_declare() struct Scsi_Host *_instance +#define NCR5380_setup(instance) _instance = instance +#define NCR5380_read(reg) cumanascsi_read(_instance, reg) +#define NCR5380_write(reg, value) cumanascsi_write(_instance, reg, value) +#define NCR5380_intr cumanascsi_intr +#define NCR5380_queue_command cumanascsi_queue_command +#define NCR5380_proc_info cumanascsi_proc_info + +int NCR5380_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#define BOARD_NORMAL 0 +#define BOARD_NCR53C400 1 + +#include "../NCR5380.h" + +void cumanascsi_setup(char *str, int *ints) +{ +} + +const char *cumanascsi_info(struct Scsi_Host *spnt) +{ + return ""; +} + +#ifdef NOT_EFFICIENT +#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) +#define STAT(p) inb((p)+1) +#define IN(p) inb((p)) +#define OUT(v,p) outb((v), (p)) +#else +#define CTRL(p,v) (p[-2308] = (*ctrl = (v))) +#define STAT(p) (p[4]) +#define IN(p) (*(p)) +#define IN2(p) ((unsigned short)(*(volatile unsigned long *)(p))) +#define OUT(v,p) (*(p) = (v)) +#define OUT2(v,p) (*((volatile unsigned long *)(p)) = (v)) +#endif +#define L(v) (((v)<<16)|((v) & 0x0000ffff)) +#define H(v) (((v)>>16)|((v) & 0xffff0000)) + +static inline int +NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, int len) +{ + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + int oldctrl = *ctrl; + unsigned long *laddr; +#ifdef NOT_EFFICIENT + int iobase = instance->io_port; + int dma_io = iobase & ~(0x3C0000>>2); +#else + volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); + volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); +#endif + + if(!len) return 0; + + CTRL(iobase, 0x02); + laddr = (unsigned long *)addr; + while(len >= 32) + { + int status; + unsigned long v; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(!(status & 0x40)) + continue; + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + v=*laddr++; OUT2(L(v),dma_io); OUT2(H(v),dma_io); + len -= 32; + if(len == 0) + break; + } + + addr = (unsigned char *)laddr; + CTRL(iobase, 0x12); + while(len > 0) + { + int status; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + OUT(*addr++, dma_io); + if(--len == 0) + break; + } + + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + OUT(*addr++, dma_io); + if(--len == 0) + break; + } + } +end: + CTRL(iobase, oldctrl|0x40); + return len; +} + +static inline int +NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, int len) +{ + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + int oldctrl = *ctrl; + unsigned long *laddr; +#ifdef NOT_EFFICIENT + int iobase = instance->io_port; + int dma_io = iobase & ~(0x3C0000>>2); +#else + volatile unsigned char *iobase = (unsigned char *)ioaddr(instance->io_port); + volatile unsigned char *dma_io = (unsigned char *)((int)iobase & ~0x3C0000); +#endif + + if(!len) return 0; + + CTRL(iobase, 0x00); + laddr = (unsigned long *)addr; + while(len >= 32) + { + int status; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(!(status & 0x40)) + continue; + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + *laddr++ = IN2(dma_io)|(IN2(dma_io)<<16); + len -= 32; + if(len == 0) + break; + } + + addr = (unsigned char *)laddr; + CTRL(iobase, 0x10); + while(len > 0) + { + int status; + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + *addr++ = IN(dma_io); + if(--len == 0) + break; + } + + status = STAT(iobase); + if(status & 0x80) + goto end; + if(status & 0x40) + { + *addr++ = IN(dma_io); + if(--len == 0) + break; + } + } +end: + CTRL(iobase, oldctrl|0x40); + return len; +} + +#undef STAT +#undef CTRL +#undef IN +#undef OUT + +#define CTRL(p,v) outb(*ctrl = (v), (p) - 577) + +static char cumanascsi_read(struct Scsi_Host *instance, int reg) +{ + unsigned int iobase = instance->io_port; + int i; + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + + CTRL(iobase, 0); + i = inb(iobase + 64 + reg); + CTRL(iobase, 0x40); + + return i; +} + +static void cumanascsi_write(struct Scsi_Host *instance, int reg, int value) +{ + int iobase = instance->io_port; + int *ctrl = &((struct NCR5380_hostdata *)instance->hostdata)->ctrl; + + CTRL(iobase, 0); + outb(value, iobase + 64 + reg); + CTRL(iobase, 0x40); +} + +#undef CTRL + +#include "../NCR5380.c" + +static Scsi_Host_Template cumanascsi_template = { + .module = THIS_MODULE, + .name = "Cumana 16-bit SCSI", + .info = cumanascsi_info, + .queuecommand = cumanascsi_queue_command, + .eh_abort_handler = NCR5380_abort, + .eh_device_reset_handler= NCR5380_device_reset, + .eh_bus_reset_handler = NCR5380_bus_reset, + .eh_host_reset_handler = NCR5380_host_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .unchecked_isa_dma = 0, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "CumanaSCSI-1", +}; + +static int __devinit +cumanascsi1_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + int ret = -ENOMEM; + + host = scsi_register(&cumanascsi_template, sizeof(struct NCR5380_hostdata)); + if (!host) + goto out; + + host->io_port = ecard_address(ec, ECARD_IOC, ECARD_SLOW) + 0x800; + host->irq = ec->irq; + + NCR5380_init(host, 0); + + host->n_io_port = 255; + if (!(request_region(host->io_port, host->n_io_port, "CumanaSCSI-1"))) { + ret = -EBUSY; + goto out_free; + } + + ((struct NCR5380_hostdata *)host->hostdata)->ctrl = 0; + outb(0x00, host->io_port - 577); + + ret = request_irq(host->irq, cumanascsi_intr, SA_INTERRUPT, + "CumanaSCSI-1", host); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_release; + } + + printk("scsi%d: at port 0x%08lx irq %d", + host->host_no, host->io_port, host->irq); + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", + host->can_queue, host->cmd_per_lun, CUMANASCSI_PUBLIC_RELEASE); + printk("\nscsi%d:", host->host_no); + NCR5380_print_options(host); + printk("\n"); + + ret = scsi_add_host(host, &ec->dev); + if (ret == 0) + goto out; + + free_irq(host->irq, host); + out_release: + release_region(host->io_port, host->n_io_port); + out_free: + scsi_unregister(host); + out: + return ret; +} + +static void __devexit cumanascsi1_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + scsi_remove_host(host); + free_irq(host->irq, host); + release_region(host->io_port, host->n_io_port); + scsi_unregister(host); +} + +static const struct ecard_id cumanascsi1_cids[] = { + { MANU_CUMANA, PROD_CUMANA_SCSI_1 }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver cumanascsi1_driver = { + .probe = cumanascsi1_probe, + .remove = __devexit_p(cumanascsi1_remove), + .id_table = cumanascsi1_cids, + .drv = { + .name = "cumanascsi1", + }, +}; + +static int __init cumanascsi_init(void) +{ + return ecard_register_driver(&cumanascsi1_driver); +} + +static void __exit cumanascsi_exit(void) +{ + ecard_remove_driver(&cumanascsi1_driver); +} + +module_init(cumanascsi_init); +module_exit(cumanascsi_exit); + +MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/cumana_2.c b/drivers/scsi/arm/cumana_2.c new file mode 100644 index 000000000000..3cd4a6389398 --- /dev/null +++ b/drivers/scsi/arm/cumana_2.c @@ -0,0 +1,574 @@ +/* + * linux/drivers/acorn/scsi/cumana_2.c + * + * Copyright (C) 1997-2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 30-08-1997 RMK 0.0.0 Created, READONLY version. + * 22-01-1998 RMK 0.0.1 Updated to 2.1.80. + * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. + * 02-05-1998 RMK 0.0.2 Updated & added DMA support. + * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 18-08-1998 RMK 0.0.3 Fixed synchronous transfer depth. + * 02-04-2000 RMK 0.0.4 Updated for new error handling code. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +#include + +#define CUMANASCSI2_STATUS (0x0000) +#define STATUS_INT (1 << 0) +#define STATUS_DRQ (1 << 1) +#define STATUS_LATCHED (1 << 3) + +#define CUMANASCSI2_ALATCH (0x0014) +#define ALATCH_ENA_INT (3) +#define ALATCH_DIS_INT (2) +#define ALATCH_ENA_TERM (5) +#define ALATCH_DIS_TERM (4) +#define ALATCH_ENA_BIT32 (11) +#define ALATCH_DIS_BIT32 (10) +#define ALATCH_ENA_DMA (13) +#define ALATCH_DIS_DMA (12) +#define ALATCH_DMA_OUT (15) +#define ALATCH_DMA_IN (14) + +#define CUMANASCSI2_PSEUDODMA (0x0200) + +#define CUMANASCSI2_FAS216_OFFSET (0x0300) +#define CUMANASCSI2_FAS216_SHIFT 2 + +/* + * Version + */ +#define VERSION "1.00 (13/11/2002 2.5.47)" + +/* + * Use term=0,1,0,0,0 to turn terminators on/off + */ +static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +#define NR_SG 256 + +struct cumanascsi2_info { + FAS216_Info info; + struct expansion_card *ec; + + void *status; /* card status register */ + void *alatch; /* Control register */ + unsigned int terms; /* Terminator state */ + void *dmaarea; /* Pseudo DMA area */ + struct scatterlist sg[NR_SG]; /* Scatter DMA list */ +}; + +#define CSTATUS_IRQ (1 << 0) +#define CSTATUS_DRQ (1 << 1) + +/* Prototype: void cumanascsi_2_irqenable(ec, irqnr) + * Purpose : Enable interrupts on Cumana SCSI 2 card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +cumanascsi_2_irqenable(struct expansion_card *ec, int irqnr) +{ + writeb(ALATCH_ENA_INT, ec->irq_data); +} + +/* Prototype: void cumanascsi_2_irqdisable(ec, irqnr) + * Purpose : Disable interrupts on Cumana SCSI 2 card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +cumanascsi_2_irqdisable(struct expansion_card *ec, int irqnr) +{ + writeb(ALATCH_DIS_INT, ec->irq_data); +} + +static const expansioncard_ops_t cumanascsi_2_ops = { + .irqenable = cumanascsi_2_irqenable, + .irqdisable = cumanascsi_2_irqdisable, +}; + +/* Prototype: void cumanascsi_2_terminator_ctl(host, on_off) + * Purpose : Turn the Cumana SCSI 2 terminators on or off + * Params : host - card to turn on/off + * : on_off - !0 to turn on, 0 to turn off + */ +static void +cumanascsi_2_terminator_ctl(struct Scsi_Host *host, int on_off) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + + if (on_off) { + info->terms = 1; + writeb(ALATCH_ENA_TERM, info->alatch); + } else { + info->terms = 0; + writeb(ALATCH_DIS_TERM, info->alatch); + } +} + +/* Prototype: void cumanascsi_2_intr(irq, *dev_id, *regs) + * Purpose : handle interrupts from Cumana SCSI 2 card + * Params : irq - interrupt number + * dev_id - user-defined (Scsi_Host structure) + * regs - processor registers at interrupt + */ +static irqreturn_t +cumanascsi_2_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cumanascsi2_info *info = dev_id; + + return fas216_intr(&info->info); +} + +/* Prototype: fasdmatype_t cumanascsi_2_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : type of transfer to be performed + */ +static fasdmatype_t +cumanascsi_2_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + struct device *dev = scsi_get_device(host); + int dmach = host->dma_channel; + + writeb(ALATCH_DIS_DMA, info->alatch); + + if (dmach != NO_DMA && + (min_type == fasdma_real_all || SCp->this_residual >= 512)) { + int bufs, map_dir, dma_dir, alatch_dir; + + bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); + + if (direction == DMA_OUT) + map_dir = DMA_TO_DEVICE, + dma_dir = DMA_MODE_WRITE, + alatch_dir = ALATCH_DMA_OUT; + else + map_dir = DMA_FROM_DEVICE, + dma_dir = DMA_MODE_READ, + alatch_dir = ALATCH_DMA_IN; + + dma_map_sg(dev, info->sg, bufs + 1, map_dir); + + disable_dma(dmach); + set_dma_sg(dmach, info->sg, bufs + 1); + writeb(alatch_dir, info->alatch); + set_dma_mode(dmach, dma_dir); + enable_dma(dmach); + writeb(ALATCH_ENA_DMA, info->alatch); + writeb(ALATCH_DIS_BIT32, info->alatch); + return fasdma_real_all; + } + + /* + * If we're not doing DMA, + * we'll do pseudo DMA + */ + return fasdma_pio; +} + +/* + * Prototype: void cumanascsi_2_dma_pseudo(host, SCpnt, direction, transfer) + * Purpose : handles pseudo DMA + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * transfer - minimum number of bytes we expect to transfer + */ +static void +cumanascsi_2_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, int transfer) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + unsigned int length; + unsigned char *addr; + + length = SCp->this_residual; + addr = SCp->ptr; + + if (direction == DMA_OUT) +#if 0 + while (length > 1) { + unsigned long word; + unsigned int status = readb(info->status); + + if (status & STATUS_INT) + goto end; + + if (!(status & STATUS_DRQ)) + continue; + + word = *addr | *(addr + 1) << 8; + writew(word, info->dmaarea); + addr += 2; + length -= 2; + } +#else + printk ("PSEUDO_OUT???\n"); +#endif + else { + if (transfer && (transfer & 255)) { + while (length >= 256) { + unsigned int status = readb(info->status); + + if (status & STATUS_INT) + return; + + if (!(status & STATUS_DRQ)) + continue; + + readsw(info->dmaarea, addr, 256 >> 1); + addr += 256; + length -= 256; + } + } + + while (length > 0) { + unsigned long word; + unsigned int status = readb(info->status); + + if (status & STATUS_INT) + return; + + if (!(status & STATUS_DRQ)) + continue; + + word = readw(info->dmaarea); + *addr++ = word; + if (--length > 0) { + *addr++ = word >> 8; + length --; + } + } + } +} + +/* Prototype: int cumanascsi_2_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void +cumanascsi_2_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + if (host->dma_channel != NO_DMA) { + writeb(ALATCH_DIS_DMA, info->alatch); + disable_dma(host->dma_channel); + } +} + +/* Prototype: const char *cumanascsi_2_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *cumanascsi_2_info(struct Scsi_Host *host) +{ + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s terminators o%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION, info->terms ? "n" : "ff"); + + return string; +} + +/* Prototype: int cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) + * Purpose : Set a driver specific function + * Params : host - host to setup + * : buffer - buffer containing string describing operation + * : length - length of string + * Returns : -EINVAL, or 0 + */ +static int +cumanascsi_2_set_proc_info(struct Scsi_Host *host, char *buffer, int length) +{ + int ret = length; + + if (length >= 11 && strcmp(buffer, "CUMANASCSI2") == 0) { + buffer += 11; + length -= 11; + + if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { + if (buffer[5] == '1') + cumanascsi_2_terminator_ctl(host, 1); + else if (buffer[5] == '0') + cumanascsi_2_terminator_ctl(host, 0); + else + ret = -EINVAL; + } else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Prototype: int cumanascsi_2_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int cumanascsi_2_proc_info (char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + struct Scsi_Host *host; + struct cumanascsi2_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + if (inout == 1) + return cumanascsi_2_set_proc_info(host, buffer, length); + + info = (struct cumanascsi2_info *)host->hostdata; + + p += sprintf(p, "Cumana SCSI II driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", + info->terms ? "n" : "ff"); + + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + *start = buffer + offset; + pos = p - buffer - offset; + if (pos > length) + pos = length; + + return pos; +} + +static Scsi_Host_Template cumanascsi2_template = { + .module = THIS_MODULE, + .proc_info = cumanascsi_2_proc_info, + .name = "Cumana SCSI II", + .info = cumanascsi_2_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + .can_queue = 1, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "cumanascsi2", +}; + +static int __devinit +cumanascsi2_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct cumanascsi2_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + if (!request_mem_region(resbase, reslen, "cumanascsi2")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&cumanascsi2_template, + sizeof(struct cumanascsi2_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = ec->irq; + host->dma_channel = ec->dma; + + ecard_set_drvdata(ec, host); + + info = (struct cumanascsi2_info *)host->hostdata; + info->ec = ec; + info->dmaarea = base + CUMANASCSI2_PSEUDODMA; + info->status = base + CUMANASCSI2_STATUS; + info->alatch = base + CUMANASCSI2_ALATCH; + + ec->irqaddr = info->status; + ec->irqmask = STATUS_INT; + ec->irq_data = base + CUMANASCSI2_ALATCH; + ec->ops = &cumanascsi_2_ops; + + cumanascsi_2_terminator_ctl(host, term[ec->slot_no]); + + info->info.scsi.io_base = base + CUMANASCSI2_FAS216_OFFSET; + info->info.scsi.io_shift = CUMANASCSI2_FAS216_SHIFT; + info->info.scsi.irq = host->irq; + info->info.ifcfg.clockrate = 40; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 7; + info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; + info->info.dma.setup = cumanascsi_2_dma_setup; + info->info.dma.pseudo = cumanascsi_2_dma_pseudo; + info->info.dma.stop = cumanascsi_2_dma_stop; + + ret = fas216_init(host); + if (ret) + goto out_free; + + ret = request_irq(host->irq, cumanascsi_2_intr, + SA_INTERRUPT, "cumanascsi2", info); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_release; + } + + if (host->dma_channel != NO_DMA) { + if (request_dma(host->dma_channel, "cumanascsi2")) { + printk("scsi%d: DMA%d not free, using PIO\n", + host->host_no, host->dma_channel); + host->dma_channel = NO_DMA; + } else { + set_dma_speed(host->dma_channel, 180); + info->info.ifcfg.capabilities |= FASCAP_DMA; + } + } + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, host); + + out_release: + fas216_release(host); + + out_free: + scsi_unregister(host); + + out_unmap: + iounmap(base); + + out_region: + release_mem_region(resbase, reslen); + + out: + return ret; +} + +static void __devexit cumanascsi2_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct cumanascsi2_info *info = (struct cumanascsi2_info *)host->hostdata; + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, info); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_MEMC); + reslen = ecard_resource_len(ec, ECARD_RES_MEMC); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id cumanascsi2_cids[] = { + { MANU_CUMANA, PROD_CUMANA_SCSI_2 }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver cumanascsi2_driver = { + .probe = cumanascsi2_probe, + .remove = __devexit_p(cumanascsi2_remove), + .id_table = cumanascsi2_cids, + .drv = { + .name = "cumanascsi2", + }, +}; + +static int __init cumanascsi2_init(void) +{ + return ecard_register_driver(&cumanascsi2_driver); +} + +static void __exit cumanascsi2_exit(void) +{ + ecard_remove_driver(&cumanascsi2_driver); +} + +module_init(cumanascsi2_init); +module_exit(cumanascsi2_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Cumana SCSI-2 driver for Acorn machines"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/ecoscsi.c b/drivers/scsi/arm/ecoscsi.c new file mode 100644 index 000000000000..5c3fcc6030d0 --- /dev/null +++ b/drivers/scsi/arm/ecoscsi.c @@ -0,0 +1,283 @@ +#define AUTOSENSE +/* #define PSEUDO_DMA */ + +/* + * EcoSCSI Generic NCR5380 driver + * + * Copyright 1995, Russell King + * + * ALPHA RELEASE 1. + * + * For more information, please consult + * + * NCR 5380 Family + * SCSI Protocol Controller + * Databook + * + * NCR Microelectronics + * 1635 Aeroplaza Drive + * Colorado Springs, CO 80916 + * 1+ (719) 578-3400 + * 1+ (800) 334-5454 + */ + +/* + * Options : + * + * PARITY - enable parity checking. Not supported. + * + * SCSI2 - enable support for SCSI-II tagged queueing. Untested. + * + * USLEEP - enable support for devices that don't disconnect. Untested. + */ + +/* + * $Log: ecoscsi.c,v $ + * Revision 1.2 1998/03/08 05:49:47 davem + * Merge to 2.1.89 + * + * Revision 1.1 1998/02/23 02:45:24 davem + * Merge to 2.1.88 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../scsi.h" +#include "../hosts.h" + +#define NCR5380_implementation_fields int port, ctrl +#define NCR5380_local_declare() struct Scsi_Host *_instance +#define NCR5380_setup(instance) _instance = instance + +#define NCR5380_read(reg) ecoscsi_read(_instance, reg) +#define NCR5380_write(reg, value) ecoscsi_write(_instance, reg, value) + +#define NCR5380_intr ecoscsi_intr +#define NCR5380_queue_command ecoscsi_queue_command +#define NCR5380_proc_info ecoscsi_proc_info + +#include "../NCR5380.h" + +#define ECOSCSI_PUBLIC_RELEASE 1 + +static char ecoscsi_read(struct Scsi_Host *instance, int reg) +{ + int iobase = instance->io_port; + outb(reg | 8, iobase); + return inb(iobase + 1); +} + +static void ecoscsi_write(struct Scsi_Host *instance, int reg, int value) +{ + int iobase = instance->io_port; + outb(reg | 8, iobase); + outb(value, iobase + 1); +} + +/* + * Function : ecoscsi_setup(char *str, int *ints) + * + * Purpose : LILO command line initialization of the overrides array, + * + * Inputs : str - unused, ints - array of integer parameters with ints[0] + * equal to the number of ints. + * + */ + +void ecoscsi_setup(char *str, int *ints) { +} + +/* + * Function : int ecoscsi_detect(Scsi_Host_Template * tpnt) + * + * Purpose : initializes ecoscsi NCR5380 driver based on the + * command line / compile time port and irq definitions. + * + * Inputs : tpnt - template for this SCSI adapter. + * + * Returns : 1 if a host adapter was found, 0 if not. + * + */ + +int ecoscsi_detect(Scsi_Host_Template * tpnt) +{ + struct Scsi_Host *host; + + tpnt->proc_name = "ecoscsi"; + + host = scsi_register (tpnt, sizeof(struct NCR5380_hostdata)); + if (!host) + return 0; + + host->io_port = 0x80ce8000; + host->n_io_port = 144; + host->irq = IRQ_NONE; + + if ( !(request_region(host->io_port, host->n_io_port, "ecoscsi")) ) + goto unregister_scsi; + + ecoscsi_write (host, MODE_REG, 0x20); /* Is it really SCSI? */ + if (ecoscsi_read (host, MODE_REG) != 0x20) /* Write to a reg. */ + goto release_reg; + + ecoscsi_write( host, MODE_REG, 0x00 ); /* it back. */ + if (ecoscsi_read (host, MODE_REG) != 0x00) + goto release_reg; + + NCR5380_init(host, 0); + + printk("scsi%d: at port 0x%08lx irqs disabled", host->host_no, host->io_port); + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", + host->can_queue, host->cmd_per_lun, ECOSCSI_PUBLIC_RELEASE); + printk("\nscsi%d:", host->host_no); + NCR5380_print_options(host); + printk("\n"); + + return 1; + +release_reg: + release_region(host->io_port, host->n_io_port); +unregister_scsi: + scsi_unregister(host); + return 0; +} + +int ecoscsi_release (struct Scsi_Host *shpnt) +{ + if (shpnt->irq != IRQ_NONE) + free_irq (shpnt->irq, NULL); + if (shpnt->io_port) + release_region (shpnt->io_port, shpnt->n_io_port); + return 0; +} + +const char * ecoscsi_info (struct Scsi_Host *spnt) +{ + return ""; +} + +#if 0 +#define STAT(p) inw(p + 144) + +static inline int NCR5380_pwrite(struct Scsi_Host *host, unsigned char *addr, + int len) +{ + int iobase = host->io_port; +printk("writing %p len %d\n",addr, len); + if(!len) return -1; + + while(1) + { + int status; + while(((status = STAT(iobase)) & 0x100)==0); + } +} + +static inline int NCR5380_pread(struct Scsi_Host *host, unsigned char *addr, + int len) +{ + int iobase = host->io_port; + int iobase2= host->io_port + 0x100; + unsigned char *start = addr; + int s; +printk("reading %p len %d\n",addr, len); + outb(inb(iobase + 128), iobase + 135); + while(len > 0) + { + int status,b,i, timeout; + timeout = 0x07FFFFFF; + while(((status = STAT(iobase)) & 0x100)==0) + { + timeout--; + if(status & 0x200 || !timeout) + { + printk("status = %p\n",status); + outb(0, iobase + 135); + return 1; + } + } + if(len >= 128) + { + for(i=0; i<64; i++) + { + b = inw(iobase + 136); + *addr++ = b; + *addr++ = b>>8; + } + len -= 128; + } + else + { + b = inw(iobase + 136); + *addr ++ = b; + len -= 1; + if(len) + *addr ++ = b>>8; + len -= 1; + } + } + outb(0, iobase + 135); + printk("first bytes = %02X %02X %02X %20X %02X %02X %02X\n",*start, start[1], start[2], start[3], start[4], start[5], start[6]); + return 1; +} +#endif +#undef STAT + +int NCR5380_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#define BOARD_NORMAL 0 +#define BOARD_NCR53C400 1 + +#include "../NCR5380.c" + +static Scsi_Host_Template ecoscsi_template = { + .module = THIS_MODULE, + .name = "Serial Port EcoSCSI NCR5380", + .detect = ecoscsi_detect, + .release = ecoscsi_release, + .info = ecoscsi_info, + .queuecommand = ecoscsi_queue_command, + .eh_abort_handler = NCR5380_abort, + .eh_device_reset_handler= NCR5380_device_reset, + .eh_bus_reset_handler = NCR5380_bus_reset, + .eh_host_reset_handler = NCR5380_host_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .use_clustering = DISABLE_CLUSTERING +}; + +static int __init ecoscsi_init(void) +{ + scsi_register_host(&ecoscsi_template); + if (ecoscsi_template.present) + return 0; + + scsi_unregister_host(&ecoscsi_template); + return -ENODEV; +} + +static void __exit ecoscsi_exit(void) +{ + scsi_unregister_host(&ecoscsi_template); +} + +module_init(ecoscsi_init); +module_exit(ecoscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Econet-SCSI driver for Acorn machines"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c new file mode 100644 index 000000000000..6a6136fbd46c --- /dev/null +++ b/drivers/scsi/arm/eesox.c @@ -0,0 +1,685 @@ +/* + * linux/drivers/acorn/scsi/eesox.c + * + * Copyright (C) 1997-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This driver is based on experimentation. Hence, it may have made + * assumptions about the particular card that I have available, and + * may not be reliable! + * + * Changelog: + * 01-10-1997 RMK Created, READONLY version + * 15-02-1998 RMK READ/WRITE version + * added DMA support and hardware definitions + * 14-03-1998 RMK Updated DMA support + * Added terminator control + * 15-04-1998 RMK Only do PIO if FAS216 will allow it. + * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new + * error handling code. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +#include + +#define EESOX_FAS216_OFFSET 0x3000 +#define EESOX_FAS216_SHIFT 5 + +#define EESOX_DMASTAT 0x2800 +#define EESOX_STAT_INTR 0x01 +#define EESOX_STAT_DMA 0x02 + +#define EESOX_CONTROL 0x2800 +#define EESOX_INTR_ENABLE 0x04 +#define EESOX_TERM_ENABLE 0x02 +#define EESOX_RESET 0x01 + +#define EESOX_DMADATA 0x3800 + +#define VERSION "1.10 (17/01/2003 2.5.59)" + +/* + * Use term=0,1,0,0,0 to turn terminators on/off + */ +static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +#define NR_SG 256 + +struct eesoxscsi_info { + FAS216_Info info; + struct expansion_card *ec; + + void *ctl_port; + unsigned int control; + struct scatterlist sg[NR_SG]; /* Scatter DMA list */ +}; + +/* Prototype: void eesoxscsi_irqenable(ec, irqnr) + * Purpose : Enable interrupts on EESOX SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +eesoxscsi_irqenable(struct expansion_card *ec, int irqnr) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; + + info->control |= EESOX_INTR_ENABLE; + + writeb(info->control, info->ctl_port); +} + +/* Prototype: void eesoxscsi_irqdisable(ec, irqnr) + * Purpose : Disable interrupts on EESOX SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; + + info->control &= ~EESOX_INTR_ENABLE; + + writeb(info->control, info->ctl_port); +} + +static const expansioncard_ops_t eesoxscsi_ops = { + .irqenable = eesoxscsi_irqenable, + .irqdisable = eesoxscsi_irqdisable, +}; + +/* Prototype: void eesoxscsi_terminator_ctl(*host, on_off) + * Purpose : Turn the EESOX SCSI terminators on or off + * Params : host - card to turn on/off + * : on_off - !0 to turn on, 0 to turn off + */ +static void +eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + unsigned long flags; + + spin_lock_irqsave(host->host_lock, flags); + if (on_off) + info->control |= EESOX_TERM_ENABLE; + else + info->control &= ~EESOX_TERM_ENABLE; + + writeb(info->control, info->ctl_port); + spin_unlock_irqrestore(host->host_lock, flags); +} + +/* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs) + * Purpose : handle interrupts from EESOX SCSI card + * Params : irq - interrupt number + * dev_id - user-defined (Scsi_Host structure) + * regs - processor registers at interrupt + */ +static irqreturn_t +eesoxscsi_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct eesoxscsi_info *info = dev_id; + + return fas216_intr(&info->info); +} + +/* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : type of transfer to be performed + */ +static fasdmatype_t +eesoxscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + struct device *dev = scsi_get_device(host); + int dmach = host->dma_channel; + + if (dmach != NO_DMA && + (min_type == fasdma_real_all || SCp->this_residual >= 512)) { + int bufs, map_dir, dma_dir; + + bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); + + if (direction == DMA_OUT) + map_dir = DMA_TO_DEVICE, + dma_dir = DMA_MODE_WRITE; + else + map_dir = DMA_FROM_DEVICE, + dma_dir = DMA_MODE_READ; + + dma_map_sg(dev, info->sg, bufs + 1, map_dir); + + disable_dma(dmach); + set_dma_sg(dmach, info->sg, bufs + 1); + set_dma_mode(dmach, dma_dir); + enable_dma(dmach); + return fasdma_real_all; + } + /* + * We don't do DMA, we only do slow PIO + * + * Some day, we will do Pseudo DMA + */ + return fasdma_pseudo; +} + +static void eesoxscsi_buffer_in(void *buf, int length, void *base) +{ + const void *reg_fas = base + EESOX_FAS216_OFFSET; + const void *reg_dmastat = base + EESOX_DMASTAT; + const void *reg_dmadata = base + EESOX_DMADATA; + const register unsigned long mask = 0xffff; + + do { + unsigned int status; + + /* + * Interrupt request? + */ + status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); + if (status & STAT_INT) + break; + + /* + * DMA request active? + */ + status = readb(reg_dmastat); + if (!(status & EESOX_STAT_DMA)) + continue; + + /* + * Get number of bytes in FIFO + */ + status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; + if (status > 16) + status = 16; + if (status > length) + status = length; + + /* + * Align buffer. + */ + if (((u32)buf) & 2 && status >= 2) { + *((u16 *)buf)++ = readl(reg_dmadata); + status -= 2; + length -= 2; + } + + if (status >= 8) { + unsigned long l1, l2; + + l1 = readl(reg_dmadata) & mask; + l1 |= readl(reg_dmadata) << 16; + l2 = readl(reg_dmadata) & mask; + l2 |= readl(reg_dmadata) << 16; + *((u32 *)buf)++ = l1; + *((u32 *)buf)++ = l2; + length -= 8; + continue; + } + + if (status >= 4) { + unsigned long l1; + + l1 = readl(reg_dmadata) & mask; + l1 |= readl(reg_dmadata) << 16; + + *((u32 *)buf)++ = l1; + length -= 4; + continue; + } + + if (status >= 2) { + *((u16 *)buf)++ = readl(reg_dmadata); + length -= 2; + } + } while (length); +} + +static void eesoxscsi_buffer_out(void *buf, int length, void *base) +{ + const void *reg_fas = base + EESOX_FAS216_OFFSET; + const void *reg_dmastat = base + EESOX_DMASTAT; + const void *reg_dmadata = base + EESOX_DMADATA; + + do { + unsigned int status; + + /* + * Interrupt request? + */ + status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); + if (status & STAT_INT) + break; + + /* + * DMA request active? + */ + status = readb(reg_dmastat); + if (!(status & EESOX_STAT_DMA)) + continue; + + /* + * Get number of bytes in FIFO + */ + status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; + if (status > 16) + status = 16; + status = 16 - status; + if (status > length) + status = length; + status &= ~1; + + /* + * Align buffer. + */ + if (((u32)buf) & 2 && status >= 2) { + writel(*((u16 *)buf)++ << 16, reg_dmadata); + status -= 2; + length -= 2; + } + + if (status >= 8) { + unsigned long l1, l2; + + l1 = *((u32 *)buf)++; + l2 = *((u32 *)buf)++; + + writel(l1 << 16, reg_dmadata); + writel(l1, reg_dmadata); + writel(l2 << 16, reg_dmadata); + writel(l2, reg_dmadata); + length -= 8; + continue; + } + + if (status >= 4) { + unsigned long l1; + + l1 = *((u32 *)buf)++; + + writel(l1 << 16, reg_dmadata); + writel(l1, reg_dmadata); + length -= 4; + continue; + } + + if (status >= 2) { + writel(*((u16 *)buf)++ << 16, reg_dmadata); + length -= 2; + } + } while (length); +} + +static void +eesoxscsi_dma_pseudo(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t dir, int transfer_size) +{ + void *base = (void *)host->base; + if (dir == DMA_IN) { + eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, base); + } else { + eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, base); + } +} + +/* Prototype: int eesoxscsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void +eesoxscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + if (host->dma_channel != NO_DMA) + disable_dma(host->dma_channel); +} + +/* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *eesoxscsi_info(struct Scsi_Host *host) +{ + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s terminators o%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff"); + + return string; +} + +/* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) + * Purpose : Set a driver specific function + * Params : host - host to setup + * : buffer - buffer containing string describing operation + * : length - length of string + * Returns : -EINVAL, or 0 + */ +static int +eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) +{ + int ret = length; + + if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) { + buffer += 9; + length -= 9; + + if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { + if (buffer[5] == '1') + eesoxscsi_terminator_ctl(host, 1); + else if (buffer[5] == '0') + eesoxscsi_terminator_ctl(host, 0); + else + ret = -EINVAL; + } else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Prototype: int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int eesoxscsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + struct Scsi_Host *host; + struct eesoxscsi_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + if (inout == 1) + return eesoxscsi_set_proc_info(host, buffer, length); + + info = (struct eesoxscsi_info *)host->hostdata; + + p += sprintf(p, "EESOX SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", + info->control & EESOX_TERM_ENABLE ? "n" : "ff"); + + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + *start = buffer + offset; + pos = p - buffer - offset; + if (pos > length) + pos = length; + + return pos; +} + +static ssize_t eesoxscsi_show_term(struct device *dev, char *buf) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + + return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0); +} + +static ssize_t eesoxscsi_store_term(struct device *dev, const char *buf, size_t len) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + unsigned long flags; + + if (len > 1) { + spin_lock_irqsave(host->host_lock, flags); + if (buf[0] != '0') { + info->control |= EESOX_TERM_ENABLE; + } else { + info->control &= ~EESOX_TERM_ENABLE; + } + writeb(info->control, info->ctl_port); + spin_unlock_irqrestore(host->host_lock, flags); + } + + return len; +} + +static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, + eesoxscsi_show_term, eesoxscsi_store_term); + +static Scsi_Host_Template eesox_template = { + .module = THIS_MODULE, + .proc_info = eesoxscsi_proc_info, + .name = "EESOX SCSI", + .info = eesoxscsi_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + .can_queue = 1, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 1, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "eesox", +}; + +static int __devinit +eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct eesoxscsi_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + if (!request_mem_region(resbase, reslen, "eesoxscsi")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&eesox_template, + sizeof(struct eesoxscsi_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = ec->irq; + host->dma_channel = ec->dma; + + ecard_set_drvdata(ec, host); + + info = (struct eesoxscsi_info *)host->hostdata; + info->ec = ec; + info->ctl_port = base + EESOX_CONTROL; + info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0; + writeb(info->control, info->ctl_port); + + ec->irqaddr = base + EESOX_DMASTAT; + ec->irqmask = EESOX_STAT_INTR; + ec->irq_data = info; + ec->ops = &eesoxscsi_ops; + + info->info.scsi.io_base = base + EESOX_FAS216_OFFSET; + info->info.scsi.io_shift = EESOX_FAS216_SHIFT; + info->info.scsi.irq = host->irq; + info->info.ifcfg.clockrate = 40; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 7; + info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; + info->info.dma.setup = eesoxscsi_dma_setup; + info->info.dma.pseudo = eesoxscsi_dma_pseudo; + info->info.dma.stop = eesoxscsi_dma_stop; + + device_create_file(&ec->dev, &dev_attr_bus_term); + + ret = fas216_init(host); + if (ret) + goto out_free; + + ret = request_irq(host->irq, eesoxscsi_intr, 0, "eesoxscsi", info); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_remove; + } + + if (host->dma_channel != NO_DMA) { + if (request_dma(host->dma_channel, "eesox")) { + printk("scsi%d: DMA%d not free, DMA disabled\n", + host->host_no, host->dma_channel); + host->dma_channel = NO_DMA; + } else { + set_dma_speed(host->dma_channel, 180); + info->info.ifcfg.capabilities |= FASCAP_DMA; + info->info.ifcfg.cntl3 |= CNTL3_BS8; + } + } + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, host); + + out_remove: + fas216_remove(host); + + out_free: + device_remove_file(&ec->dev, &dev_attr_bus_term); + scsi_unregister(host); + + out_unmap: + iounmap(base); + + out_region: + release_mem_region(resbase, reslen); + + out: + return ret; +} + +static void __devexit eesoxscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, info); + + device_remove_file(&ec->dev, &dev_attr_bus_term); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id eesoxscsi_cids[] = { + { MANU_EESOX, PROD_EESOX_SCSI2 }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver eesoxscsi_driver = { + .probe = eesoxscsi_probe, + .remove = __devexit_p(eesoxscsi_remove), + .id_table = eesoxscsi_cids, + .drv = { + .name = "eesoxscsi", + }, +}; + +static int __init eesox_init(void) +{ + return ecard_register_driver(&eesoxscsi_driver); +} + +static void __exit eesox_exit(void) +{ + ecard_remove_driver(&eesoxscsi_driver); +} + +module_init(eesox_init); +module_exit(eesox_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c new file mode 100644 index 000000000000..5b5d93ace30f --- /dev/null +++ b/drivers/scsi/arm/fas216.c @@ -0,0 +1,3045 @@ +/* + * linux/drivers/acorn/scsi/fas216.c + * + * Copyright (C) 1997-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and + * other sources, including: + * the AMD Am53CF94 data sheet + * the AMD Am53C94 data sheet + * + * This is a generic driver. To use it, have a look at cumana_2.c. You + * should define your own structure that overlays FAS216_Info, eg: + * struct my_host_data { + * FAS216_Info info; + * ... my host specific data ... + * }; + * + * Changelog: + * 30-08-1997 RMK Created + * 14-09-1997 RMK Started disconnect support + * 08-02-1998 RMK Corrected real DMA support + * 15-02-1998 RMK Started sync xfer support + * 06-04-1998 RMK Tightened conditions for printing incomplete + * transfers + * 02-05-1998 RMK Added extra checks in fas216_reset + * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns + * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h + * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT + * 02-04-2000 RMK Converted to use the new error handling, and + * automatically request sense data upon check + * condition status from targets. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +/* NOTE: SCSI2 Synchronous transfers *require* DMA according to + * the data sheet. This restriction is crazy, especially when + * you only want to send 16 bytes! What were the guys who + * designed this chip on at that time? Did they read the SCSI2 + * spec at all? The following sections are taken from the SCSI2 + * standard (s2r10) concerning this: + * + * > IMPLEMENTORS NOTES: + * > (1) Re-negotiation at every selection is not recommended, since a + * > significant performance impact is likely. + * + * > The implied synchronous agreement shall remain in effect until a BUS DEVICE + * > RESET message is received, until a hard reset condition occurs, or until one + * > of the two SCSI devices elects to modify the agreement. The default data + * > transfer mode is asynchronous data transfer mode. The default data transfer + * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard + * > reset condition. + * + * In total, this means that once you have elected to use synchronous + * transfers, you must always use DMA. + * + * I was thinking that this was a good chip until I found this restriction ;( + */ +#define SCSI2_SYNC +#undef SCSI2_TAG + +#undef DEBUG_CONNECT +#undef DEBUG_MESSAGES + +#undef CHECK_STRUCTURE + +#define LOG_CONNECT (1 << 0) +#define LOG_BUSSERVICE (1 << 1) +#define LOG_FUNCTIONDONE (1 << 2) +#define LOG_MESSAGES (1 << 3) +#define LOG_BUFFER (1 << 4) +#define LOG_ERROR (1 << 8) + +static int level_mask = LOG_ERROR; + +MODULE_PARM(level_mask, "i"); + +static int __init fas216_log_setup(char *str) +{ + char *s; + + level_mask = 0; + + while ((s = strsep(&str, ",")) != NULL) { + switch (s[0]) { + case 'a': + if (strcmp(s, "all") == 0) + level_mask |= -1; + break; + case 'b': + if (strncmp(s, "bus", 3) == 0) + level_mask |= LOG_BUSSERVICE; + if (strncmp(s, "buf", 3) == 0) + level_mask |= LOG_BUFFER; + break; + case 'c': + level_mask |= LOG_CONNECT; + break; + case 'e': + level_mask |= LOG_ERROR; + break; + case 'm': + level_mask |= LOG_MESSAGES; + break; + case 'n': + if (strcmp(s, "none") == 0) + level_mask = 0; + break; + case 's': + level_mask |= LOG_FUNCTIONDONE; + break; + } + } + return 1; +} + +__setup("fas216_logging=", fas216_log_setup); + +static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg) +{ + unsigned int off = reg << info->scsi.io_shift; + if (info->scsi.io_base) + return readb(info->scsi.io_base + off); + else + return inb(info->scsi.io_port + off); +} + +static inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val) +{ + unsigned int off = reg << info->scsi.io_shift; + if (info->scsi.io_base) + writeb(val, info->scsi.io_base + off); + else + outb(val, info->scsi.io_port + off); +} + +static void fas216_dumpstate(FAS216_Info *info) +{ + unsigned char is, stat, inst; + + is = fas216_readb(info, REG_IS); + stat = fas216_readb(info, REG_STAT); + inst = fas216_readb(info, REG_INST); + + printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X" + " INST=%02X IS=%02X CFIS=%02X", + fas216_readb(info, REG_CTCL), + fas216_readb(info, REG_CTCM), + fas216_readb(info, REG_CMD), stat, inst, is, + fas216_readb(info, REG_CFIS)); + printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n", + fas216_readb(info, REG_CNTL1), + fas216_readb(info, REG_CNTL2), + fas216_readb(info, REG_CNTL3), + fas216_readb(info, REG_CTCH)); +} + +static void print_SCp(Scsi_Pointer *SCp, const char *prefix, const char *suffix) +{ + printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s", + prefix, SCp->ptr, SCp->this_residual, SCp->buffer, + SCp->buffers_residual, suffix); +} + +static void fas216_dumpinfo(FAS216_Info *info) +{ + static int used = 0; + int i; + + if (used++) + return; + + printk("FAS216_Info=\n"); + printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n", + info->magic_start, info->host, info->SCpnt, + info->origSCpnt); + printk(" scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n", + info->scsi.io_port, info->scsi.io_shift, info->scsi.irq, + info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2], + info->scsi.cfg[3]); + printk(" type=%p phase=%X\n", + info->scsi.type, info->scsi.phase); + print_SCp(&info->scsi.SCp, " SCp={ ", " }\n"); + printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", + info->scsi.async_stp, + info->scsi.disconnectable, info->scsi.aborting); + printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" + " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n", + info->stats.queues, info->stats.removes, info->stats.fins, + info->stats.reads, info->stats.writes, info->stats.miscs, + info->stats.disconnects, info->stats.aborts, info->stats.bus_resets, + info->stats.host_resets); + printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n", + info->ifcfg.clockrate, info->ifcfg.select_timeout, + info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); + for (i = 0; i < 8; i++) { + printk(" busyluns[%d]=%08lx dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", + i, info->busyluns[i], i, + info->device[i].disconnect_ok, info->device[i].stp, + info->device[i].sof, info->device[i].sync_state); + } + printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", + info->dma.transfer_type, info->dma.setup, + info->dma.pseudo, info->dma.stop); + printk(" internal_done=%X magic_end=%lX }\n", + info->internal_done, info->magic_end); +} + +#ifdef CHECK_STRUCTURE +static void __fas216_checkmagic(FAS216_Info *info, const char *func) +{ + int corruption = 0; + if (info->magic_start != MAGIC) { + printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n"); + corruption++; + } + if (info->magic_end != MAGIC) { + printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n"); + corruption++; + } + if (corruption) { + fas216_dumpinfo(info); + panic("scsi memory space corrupted in %s", func); + } +} +#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__) +#else +#define fas216_checkmagic(info) +#endif + +static const char *fas216_bus_phase(int stat) +{ + static const char *phases[] = { + "DATA OUT", "DATA IN", + "COMMAND", "STATUS", + "MISC OUT", "MISC IN", + "MESG OUT", "MESG IN" + }; + + return phases[stat & STAT_BUSMASK]; +} + +static const char *fas216_drv_phase(FAS216_Info *info) +{ + static const char *phases[] = { + [PHASE_IDLE] = "idle", + [PHASE_SELECTION] = "selection", + [PHASE_COMMAND] = "command", + [PHASE_DATAOUT] = "data out", + [PHASE_DATAIN] = "data in", + [PHASE_MSGIN] = "message in", + [PHASE_MSGIN_DISCONNECT]= "disconnect", + [PHASE_MSGOUT_EXPECT] = "expect message out", + [PHASE_MSGOUT] = "message out", + [PHASE_STATUS] = "status", + [PHASE_DONE] = "done", + }; + + if (info->scsi.phase < ARRAY_SIZE(phases) && + phases[info->scsi.phase]) + return phases[info->scsi.phase]; + return "???"; +} + +static char fas216_target(FAS216_Info *info) +{ + if (info->SCpnt) + return '0' + info->SCpnt->device->id; + else + return 'H'; +} + +static void +fas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap) +{ + static char buf[1024]; + + vsnprintf(buf, sizeof(buf), fmt, ap); + printk("scsi%d.%c: %s", info->host->host_no, target, buf); +} + +static void +fas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...) +{ + va_list args; + + if (level != 0 && !(level & level_mask)) + return; + + va_start(args, fmt); + fas216_do_log(info, '0' + SCpnt->device->id, fmt, args); + va_end(args); + + printk(" CDB: "); + print_command(SCpnt->cmnd); +} + +static void +fas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...) +{ + va_list args; + + if (level != 0 && !(level & level_mask)) + return; + + if (target < 0) + target = 'H'; + else + target += '0'; + + va_start(args, fmt); + fas216_do_log(info, target, fmt, args); + va_end(args); + + printk("\n"); +} + +static void fas216_log(FAS216_Info *info, int level, char *fmt, ...) +{ + va_list args; + + if (level != 0 && !(level & level_mask)) + return; + + va_start(args, fmt); + fas216_do_log(info, fas216_target(info), fmt, args); + va_end(args); + + printk("\n"); +} + +#define PH_SIZE 32 + +static struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE]; +static int ph_ptr; + +static void add_debug_list(int stat, int ssr, int isr, int ph) +{ + ph_list[ph_ptr].stat = stat; + ph_list[ph_ptr].ssr = ssr; + ph_list[ph_ptr].isr = isr; + ph_list[ph_ptr].ph = ph; + + ph_ptr = (ph_ptr + 1) & (PH_SIZE-1); +} + +static struct { int command; void *from; } cmd_list[8]; +static int cmd_ptr; + +static void fas216_cmd(FAS216_Info *info, unsigned int command) +{ + cmd_list[cmd_ptr].command = command; + cmd_list[cmd_ptr].from = __builtin_return_address(0); + + cmd_ptr = (cmd_ptr + 1) & 7; + + fas216_writeb(info, REG_CMD, command); +} + +static void print_debug_list(void) +{ + int i; + + i = ph_ptr; + + printk(KERN_ERR "SCSI IRQ trail\n"); + do { + printk(" %02x:%02x:%02x:%1x", + ph_list[i].stat, ph_list[i].ssr, + ph_list[i].isr, ph_list[i].ph); + i = (i + 1) & (PH_SIZE - 1); + if (((i ^ ph_ptr) & 7) == 0) + printk("\n"); + } while (i != ph_ptr); + if ((i ^ ph_ptr) & 7) + printk("\n"); + + i = cmd_ptr; + printk(KERN_ERR "FAS216 commands: "); + do { + printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from); + i = (i + 1) & 7; + } while (i != cmd_ptr); + printk("\n"); +} + +static void fas216_done(FAS216_Info *info, unsigned int result); + +/** + * fas216_get_last_msg - retrive last message from the list + * @info: interface to search + * @pos: current fifo position + * + * Retrieve a last message from the list, using position in fifo. + */ +static inline unsigned short +fas216_get_last_msg(FAS216_Info *info, int pos) +{ + unsigned short packed_msg = NOP; + struct message *msg; + int msgnr = 0; + + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + if (pos >= msg->fifo) + break; + } + + if (msg) { + if (msg->msg[0] == EXTENDED_MESSAGE) + packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; + else + packed_msg = msg->msg[0]; + } + + fas216_log(info, LOG_MESSAGES, + "Message: %04x found at position %02x\n", packed_msg, pos); + + return packed_msg; +} + +/** + * fas216_syncperiod - calculate STP register value + * @info: state structure for interface connected to device + * @ns: period in ns (between subsequent bytes) + * + * Calculate value to be loaded into the STP register for a given period + * in ns. Returns a value suitable for REG_STP. + */ +static int fas216_syncperiod(FAS216_Info *info, int ns) +{ + int value = (info->ifcfg.clockrate * ns) / 1000; + + fas216_checkmagic(info); + + if (value < 4) + value = 4; + else if (value > 35) + value = 35; + + return value & 31; +} + +/** + * fas216_set_sync - setup FAS216 chip for specified transfer period. + * @info: state structure for interface connected to device + * @target: target + * + * Correctly setup FAS216 chip for specified transfer period. + * Notes : we need to switch the chip out of FASTSCSI mode if we have + * a transfer period >= 200ns - otherwise the chip will violate + * the SCSI timings. + */ +static void fas216_set_sync(FAS216_Info *info, int target) +{ + unsigned int cntl3; + + fas216_writeb(info, REG_SOF, info->device[target].sof); + fas216_writeb(info, REG_STP, info->device[target].stp); + + cntl3 = info->scsi.cfg[2]; + if (info->device[target].period >= (200 / 4)) + cntl3 = cntl3 & ~CNTL3_FASTSCSI; + + fas216_writeb(info, REG_CNTL3, cntl3); +} + +/* Synchronous transfer support + * + * Note: The SCSI II r10 spec says (5.6.12): + * + * (2) Due to historical problems with early host adapters that could + * not accept an SDTR message, some targets may not initiate synchronous + * negotiation after a power cycle as required by this standard. Host + * adapters that support synchronous mode may avoid the ensuing failure + * modes when the target is independently power cycled by initiating a + * synchronous negotiation on each REQUEST SENSE and INQUIRY command. + * This approach increases the SCSI bus overhead and is not recommended + * for new implementations. The correct method is to respond to an + * SDTR message with a MESSAGE REJECT message if the either the + * initiator or target devices does not support synchronous transfers + * or does not want to negotiate for synchronous transfers at the time. + * Using the correct method assures compatibility with wide data + * transfers and future enhancements. + * + * We will always initiate a synchronous transfer negotiation request on + * every INQUIRY or REQUEST SENSE message, unless the target itself has + * at some point performed a synchronous transfer negotiation request, or + * we have synchronous transfers disabled for this device. + */ + +/** + * fas216_handlesync - Handle a synchronous transfer message + * @info: state structure for interface + * @msg: message from target + * + * Handle a synchronous transfer message from the target + */ +static void fas216_handlesync(FAS216_Info *info, char *msg) +{ + struct fas216_device *dev = &info->device[info->SCpnt->device->id]; + enum { sync, async, none, reject } res = none; + +#ifdef SCSI2_SYNC + switch (msg[0]) { + case MESSAGE_REJECT: + /* Synchronous transfer request failed. + * Note: SCSI II r10: + * + * SCSI devices that are capable of synchronous + * data transfers shall not respond to an SDTR + * message with a MESSAGE REJECT message. + * + * Hence, if we get this condition, we disable + * negotiation for this device. + */ + if (dev->sync_state == neg_inprogress) { + dev->sync_state = neg_invalid; + res = async; + } + break; + + case EXTENDED_MESSAGE: + switch (dev->sync_state) { + /* We don't accept synchronous transfer requests. + * Respond with a MESSAGE_REJECT to prevent a + * synchronous transfer agreement from being reached. + */ + case neg_invalid: + res = reject; + break; + + /* We were not negotiating a synchronous transfer, + * but the device sent us a negotiation request. + * Honour the request by sending back a SDTR + * message containing our capability, limited by + * the targets capability. + */ + default: + fas216_cmd(info, CMD_SETATN); + if (msg[4] > info->ifcfg.sync_max_depth) + msg[4] = info->ifcfg.sync_max_depth; + if (msg[3] < 1000 / info->ifcfg.clockrate) + msg[3] = 1000 / info->ifcfg.clockrate; + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + msg[3], msg[4]); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + /* This is wrong. The agreement is not in effect + * until this message is accepted by the device + */ + dev->sync_state = neg_targcomplete; + res = sync; + break; + + /* We initiated the synchronous transfer negotiation, + * and have successfully received a response from the + * target. The synchronous transfer agreement has been + * reached. Note: if the values returned are out of our + * bounds, we must reject the message. + */ + case neg_inprogress: + res = reject; + if (msg[4] <= info->ifcfg.sync_max_depth && + msg[3] >= 1000 / info->ifcfg.clockrate) { + dev->sync_state = neg_complete; + res = sync; + } + break; + } + } +#else + res = reject; +#endif + + switch (res) { + case sync: + dev->period = msg[3]; + dev->sof = msg[4]; + dev->stp = fas216_syncperiod(info, msg[3] * 4); + fas216_set_sync(info, info->SCpnt->device->id); + break; + + case reject: + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + + case async: + dev->period = info->ifcfg.asyncperiod / 4; + dev->sof = 0; + dev->stp = info->scsi.async_stp; + fas216_set_sync(info, info->SCpnt->device->id); + break; + + case none: + break; + } +} + +/** + * fas216_updateptrs - update data pointers after transfer suspended/paused + * @info: interface's local pointer to update + * @bytes_transferred: number of bytes transferred + * + * Update data pointers after transfer suspended/paused + */ +static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) +{ + Scsi_Pointer *SCp = &info->scsi.SCp; + + fas216_checkmagic(info); + + BUG_ON(bytes_transferred < 0); + + info->SCpnt->request_bufflen -= bytes_transferred; + + while (bytes_transferred != 0) { + if (SCp->this_residual > bytes_transferred) + break; + /* + * We have used up this buffer. Move on to the + * next buffer. + */ + bytes_transferred -= SCp->this_residual; + if (!next_SCp(SCp) && bytes_transferred) { + printk(KERN_WARNING "scsi%d.%c: out of buffers\n", + info->host->host_no, '0' + info->SCpnt->device->id); + return; + } + } + + SCp->this_residual -= bytes_transferred; + if (SCp->this_residual) + SCp->ptr += bytes_transferred; + else + SCp->ptr = NULL; +} + +/** + * fas216_pio - transfer data off of/on to card using programmed IO + * @info: interface to transfer data to/from + * @direction: direction to transfer data (DMA_OUT/DMA_IN) + * + * Transfer data off of/on to card using programmed IO. + * Notes: this is incredibly slow. + */ +static void fas216_pio(FAS216_Info *info, fasdmadir_t direction) +{ + Scsi_Pointer *SCp = &info->scsi.SCp; + + fas216_checkmagic(info); + + if (direction == DMA_OUT) + fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp)); + else + put_next_SCp_byte(SCp, fas216_readb(info, REG_FF)); + + if (SCp->this_residual == 0) + next_SCp(SCp); +} + +static void fas216_set_stc(FAS216_Info *info, unsigned int length) +{ + fas216_writeb(info, REG_STCL, length); + fas216_writeb(info, REG_STCM, length >> 8); + fas216_writeb(info, REG_STCH, length >> 16); +} + +static unsigned int fas216_get_ctc(FAS216_Info *info) +{ + return fas216_readb(info, REG_CTCL) + + (fas216_readb(info, REG_CTCM) << 8) + + (fas216_readb(info, REG_CTCH) << 16); +} + +/** + * fas216_cleanuptransfer - clean up after a transfer has completed. + * @info: interface to clean up + * + * Update the data pointers according to the number of bytes transferred + * on the SCSI bus. + */ +static void fas216_cleanuptransfer(FAS216_Info *info) +{ + unsigned long total, residual, fifo; + fasdmatype_t dmatype = info->dma.transfer_type; + + info->dma.transfer_type = fasdma_none; + + /* + * PIO transfers do not need to be cleaned up. + */ + if (dmatype == fasdma_pio || dmatype == fasdma_none) + return; + + if (dmatype == fasdma_real_all) + total = info->SCpnt->request_bufflen; + else + total = info->scsi.SCp.this_residual; + + residual = fas216_get_ctc(info); + + fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + + fas216_log(info, LOG_BUFFER, "cleaning up from previous " + "transfer: length 0x%06x, residual 0x%x, fifo %d", + total, residual, fifo); + + /* + * If we were performing Data-Out, the transfer counter + * counts down each time a byte is transferred by the + * host to the FIFO. This means we must include the + * bytes left in the FIFO from the transfer counter. + */ + if (info->scsi.phase == PHASE_DATAOUT) + residual += fifo; + + fas216_updateptrs(info, total - residual); +} + +/** + * fas216_transfer - Perform a DMA/PIO transfer off of/on to card + * @info: interface from which device disconnected from + * + * Start a DMA/PIO transfer off of/on to card + */ +static void fas216_transfer(FAS216_Info *info) +{ + fasdmadir_t direction; + fasdmatype_t dmatype; + + fas216_log(info, LOG_BUFFER, + "starttransfer: buffer %p length 0x%06x reqlen 0x%06x", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual, + info->SCpnt->request_bufflen); + + if (!info->scsi.SCp.ptr) { + fas216_log(info, LOG_ERROR, "null buffer passed to " + "fas216_starttransfer"); + print_SCp(&info->scsi.SCp, "SCp: ", "\n"); + print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n"); + return; + } + + /* + * If we have a synchronous transfer agreement in effect, we must + * use DMA mode. If we are using asynchronous transfers, we may + * use DMA mode or PIO mode. + */ + if (info->device[info->SCpnt->device->id].sof) + dmatype = fasdma_real_all; + else + dmatype = fasdma_pio; + + if (info->scsi.phase == PHASE_DATAOUT) + direction = DMA_OUT; + else + direction = DMA_IN; + + if (info->dma.setup) + dmatype = info->dma.setup(info->host, &info->scsi.SCp, + direction, dmatype); + info->dma.transfer_type = dmatype; + + if (dmatype == fasdma_real_all) + fas216_set_stc(info, info->SCpnt->request_bufflen); + else + fas216_set_stc(info, info->scsi.SCp.this_residual); + + switch (dmatype) { + case fasdma_pio: + fas216_log(info, LOG_BUFFER, "PIO transfer"); + fas216_writeb(info, REG_SOF, 0); + fas216_writeb(info, REG_STP, info->scsi.async_stp); + fas216_cmd(info, CMD_TRANSFERINFO); + fas216_pio(info, direction); + break; + + case fasdma_pseudo: + fas216_log(info, LOG_BUFFER, "pseudo transfer"); + fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); + info->dma.pseudo(info->host, &info->scsi.SCp, + direction, info->SCpnt->transfersize); + break; + + case fasdma_real_block: + fas216_log(info, LOG_BUFFER, "block dma transfer"); + fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); + break; + + case fasdma_real_all: + fas216_log(info, LOG_BUFFER, "total dma transfer"); + fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA); + break; + + default: + fas216_log(info, LOG_BUFFER | LOG_ERROR, + "invalid FAS216 DMA type"); + break; + } +} + +/** + * fas216_stoptransfer - Stop a DMA transfer onto / off of the card + * @info: interface from which device disconnected from + * + * Called when we switch away from DATA IN or DATA OUT phases. + */ +static void fas216_stoptransfer(FAS216_Info *info) +{ + fas216_checkmagic(info); + + if (info->dma.transfer_type == fasdma_real_all || + info->dma.transfer_type == fasdma_real_block) + info->dma.stop(info->host, &info->scsi.SCp); + + fas216_cleanuptransfer(info); + + if (info->scsi.phase == PHASE_DATAIN) { + unsigned int fifo; + + /* + * If we were performing Data-In, then the FIFO counter + * contains the number of bytes not transferred via DMA + * from the on-board FIFO. Read them manually. + */ + fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + while (fifo && info->scsi.SCp.ptr) { + *info->scsi.SCp.ptr = fas216_readb(info, REG_FF); + fas216_updateptrs(info, 1); + fifo--; + } + } else { + /* + * After a Data-Out phase, there may be unsent + * bytes left in the FIFO. Flush them out. + */ + fas216_cmd(info, CMD_FLUSHFIFO); + } +} + +static void fas216_aborttransfer(FAS216_Info *info) +{ + fas216_checkmagic(info); + + if (info->dma.transfer_type == fasdma_real_all || + info->dma.transfer_type == fasdma_real_block) + info->dma.stop(info->host, &info->scsi.SCp); + + info->dma.transfer_type = fasdma_none; + fas216_cmd(info, CMD_FLUSHFIFO); +} + +static void fas216_kick(FAS216_Info *info); + +/** + * fas216_disconnected_intr - handle device disconnection + * @info: interface from which device disconnected from + * + * Handle device disconnection + */ +static void fas216_disconnect_intr(FAS216_Info *info) +{ + unsigned long flags; + + fas216_checkmagic(info); + + fas216_log(info, LOG_CONNECT, "disconnect phase=%02x", + info->scsi.phase); + + msgqueue_flush(&info->scsi.msgs); + + switch (info->scsi.phase) { + case PHASE_SELECTION: /* while selecting - no target */ + case PHASE_SELSTEPS: + fas216_done(info, DID_NO_CONNECT); + break; + + case PHASE_MSGIN_DISCONNECT: /* message in - disconnecting */ + info->scsi.disconnectable = 1; + info->scsi.phase = PHASE_IDLE; + info->stats.disconnects += 1; + spin_lock_irqsave(&info->host_lock, flags); + if (info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + spin_unlock_irqrestore(&info->host_lock, flags); + break; + + case PHASE_DONE: /* at end of command - complete */ + fas216_done(info, DID_OK); + break; + + case PHASE_MSGOUT: /* message out - possible ABORT message */ + if (fas216_get_last_msg(info, info->scsi.msgin_fifo) == ABORT) { + info->scsi.aborting = 0; + fas216_done(info, DID_ABORT); + break; + } + + default: /* huh? */ + printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n", + info->host->host_no, fas216_target(info), fas216_drv_phase(info)); + print_debug_list(); + fas216_stoptransfer(info); + fas216_done(info, DID_ERROR); + break; + } +} + +/** + * fas216_reselected_intr - start reconnection of a device + * @info: interface which was reselected + * + * Start reconnection of a device + */ +static void +fas216_reselected_intr(FAS216_Info *info) +{ + unsigned int cfis, i; + unsigned char msg[4]; + unsigned char target, lun, tag; + + fas216_checkmagic(info); + + WARN_ON(info->scsi.phase == PHASE_SELECTION || + info->scsi.phase == PHASE_SELSTEPS); + + cfis = fas216_readb(info, REG_CFIS); + + fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x", + info->scsi.phase, cfis); + + cfis &= CFIS_CF; + + if (cfis < 2 || cfis > 4) { + printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n", + info->host->host_no); + goto bad_message; + } + + for (i = 0; i < cfis; i++) + msg[i] = fas216_readb(info, REG_FF); + + if (!(msg[0] & (1 << info->host->this_id)) || + !(msg[1] & 0x80)) + goto initiator_error; + + target = msg[0] & ~(1 << info->host->this_id); + target = ffs(target) - 1; + lun = msg[1] & 7; + tag = 0; + + if (cfis >= 3) { + if (msg[2] != SIMPLE_QUEUE_TAG) + goto initiator_error; + + tag = msg[3]; + } + + /* set up for synchronous transfers */ + fas216_writeb(info, REG_SDID, target); + fas216_set_sync(info, target); + msgqueue_flush(&info->scsi.msgs); + + fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x", + target, lun, tag); + + if (info->scsi.disconnectable && info->SCpnt) { + info->scsi.disconnectable = 0; + if (info->SCpnt->device->id == target && + info->SCpnt->device->lun == lun && + info->SCpnt->tag == tag) { + fas216_log(info, LOG_CONNECT, "reconnected previously executing command"); + } else { + queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); + fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue"); + info->SCpnt = NULL; + } + } + if (!info->SCpnt) { + info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected, + target, lun, tag); + fas216_log(info, LOG_CONNECT, "had to get command"); + } + + if (info->SCpnt) { + /* + * Restore data pointer from SAVED data pointer + */ + info->scsi.SCp = info->SCpnt->SCp; + + fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + info->scsi.phase = PHASE_MSGIN; + } else { + /* + * Our command structure not found - abort the + * command on the target. Since we have no + * record of this command, we can't send + * an INITIATOR DETECTED ERROR message. + */ + fas216_cmd(info, CMD_SETATN); + +#if 0 + if (tag) + msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, tag); + else +#endif + msgqueue_addmsg(&info->scsi.msgs, 1, ABORT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + info->scsi.aborting = 1; + } + + fas216_cmd(info, CMD_MSGACCEPTED); + return; + + initiator_error: + printk(KERN_ERR "scsi%d.H: error during reselection: bytes", + info->host->host_no); + for (i = 0; i < cfis; i++) + printk(" %02x", msg[i]); + printk("\n"); + bad_message: + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + fas216_cmd(info, CMD_MSGACCEPTED); +} + +static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen) +{ + int i; + + switch (message[0]) { + case COMMAND_COMPLETE: + if (msglen != 1) + goto unrecognised; + + printk(KERN_ERR "scsi%d.%c: command complete with no " + "status in MESSAGE_IN?\n", + info->host->host_no, fas216_target(info)); + break; + + case SAVE_POINTERS: + if (msglen != 1) + goto unrecognised; + + /* + * Save current data pointer to SAVED data pointer + * SCSI II standard says that we must not acknowledge + * this until we have really saved pointers. + * NOTE: we DO NOT save the command nor status pointers + * as required by the SCSI II standard. These always + * point to the start of their respective areas. + */ + info->SCpnt->SCp = info->scsi.SCp; + info->SCpnt->SCp.sent_command = 0; + fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, + "save data pointers: [%p, %X]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + break; + + case RESTORE_POINTERS: + if (msglen != 1) + goto unrecognised; + + /* + * Restore current data pointer from SAVED data pointer + */ + info->scsi.SCp = info->SCpnt->SCp; + fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER, + "restore data pointers: [%p, 0x%x]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + break; + + case DISCONNECT: + if (msglen != 1) + goto unrecognised; + + info->scsi.phase = PHASE_MSGIN_DISCONNECT; + break; + + case MESSAGE_REJECT: + if (msglen != 1) + goto unrecognised; + + switch (fas216_get_last_msg(info, info->scsi.msgin_fifo)) { + case EXTENDED_MESSAGE | EXTENDED_SDTR << 8: + fas216_handlesync(info, message); + break; + + default: + fas216_log(info, 0, "reject, last message 0x%04x", + fas216_get_last_msg(info, info->scsi.msgin_fifo)); + } + break; + + case NOP: + break; + + case EXTENDED_MESSAGE: + if (msglen < 3) + goto unrecognised; + + switch (message[2]) { + case EXTENDED_SDTR: /* Sync transfer negotiation request/reply */ + fas216_handlesync(info, message); + break; + + default: + goto unrecognised; + } + break; + + default: + goto unrecognised; + } + return; + +unrecognised: + fas216_log(info, 0, "unrecognised message, rejecting"); + printk("scsi%d.%c: message was", info->host->host_no, fas216_target(info)); + for (i = 0; i < msglen; i++) + printk("%s%02X", i & 31 ? " " : "\n ", message[i]); + printk("\n"); + + /* + * Something strange seems to be happening here - + * I can't use SETATN since the chip gives me an + * invalid command interrupt when I do. Weird. + */ +fas216_cmd(info, CMD_NOP); +fas216_dumpstate(info); + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT); + info->scsi.phase = PHASE_MSGOUT_EXPECT; +fas216_dumpstate(info); +} + +static int fas216_wait_cmd(FAS216_Info *info, int cmd) +{ + int tout; + int stat; + + fas216_cmd(info, cmd); + + for (tout = 1000; tout; tout -= 1) { + stat = fas216_readb(info, REG_STAT); + if (stat & (STAT_INT|STAT_PARITYERROR)) + break; + udelay(1); + } + + return stat; +} + +static int fas216_get_msg_byte(FAS216_Info *info) +{ + unsigned int stat = fas216_wait_cmd(info, CMD_MSGACCEPTED); + + if ((stat & STAT_INT) == 0) + goto timedout; + + if ((stat & STAT_BUSMASK) != STAT_MESGIN) + goto unexpected_phase_change; + + fas216_readb(info, REG_INST); + + stat = fas216_wait_cmd(info, CMD_TRANSFERINFO); + + if ((stat & STAT_INT) == 0) + goto timedout; + + if (stat & STAT_PARITYERROR) + goto parity_error; + + if ((stat & STAT_BUSMASK) != STAT_MESGIN) + goto unexpected_phase_change; + + fas216_readb(info, REG_INST); + + return fas216_readb(info, REG_FF); + +timedout: + fas216_log(info, LOG_ERROR, "timed out waiting for message byte"); + return -1; + +unexpected_phase_change: + fas216_log(info, LOG_ERROR, "unexpected phase change: status = %02x", stat); + return -2; + +parity_error: + fas216_log(info, LOG_ERROR, "parity error during message in phase"); + return -3; +} + +/** + * fas216_message - handle a function done interrupt from FAS216 chip + * @info: interface which caused function done interrupt + * + * Handle a function done interrupt from FAS216 chip + */ +static void fas216_message(FAS216_Info *info) +{ + unsigned char *message = info->scsi.message; + unsigned int msglen = 1; + int msgbyte = 0; + + fas216_checkmagic(info); + + message[0] = fas216_readb(info, REG_FF); + + if (message[0] == EXTENDED_MESSAGE) { + msgbyte = fas216_get_msg_byte(info); + + if (msgbyte >= 0) { + message[1] = msgbyte; + + for (msglen = 2; msglen < message[1] + 2; msglen++) { + msgbyte = fas216_get_msg_byte(info); + + if (msgbyte >= 0) + message[msglen] = msgbyte; + else + break; + } + } + } + + if (msgbyte == -3) + goto parity_error; + +#ifdef DEBUG_MESSAGES + { + int i; + + printk("scsi%d.%c: message in: ", + info->host->host_no, fas216_target(info)); + for (i = 0; i < msglen; i++) + printk("%02X ", message[i]); + printk("\n"); + } +#endif + + fas216_parse_message(info, message, msglen); + fas216_cmd(info, CMD_MSGACCEPTED); + return; + +parity_error: + fas216_cmd(info, CMD_SETATN); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, MSG_PARITY_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + fas216_cmd(info, CMD_MSGACCEPTED); + return; +} + +/** + * fas216_send_command - send command after all message bytes have been sent + * @info: interface which caused bus service + * + * Send a command to a target after all message bytes have been sent + */ +static void fas216_send_command(FAS216_Info *info) +{ + int i; + + fas216_checkmagic(info); + + fas216_cmd(info, CMD_NOP|CMD_WITHDMA); + fas216_cmd(info, CMD_FLUSHFIFO); + + /* load command */ + for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++) + fas216_writeb(info, REG_FF, info->SCpnt->cmnd[i]); + + fas216_cmd(info, CMD_TRANSFERINFO); + + info->scsi.phase = PHASE_COMMAND; +} + +/** + * fas216_send_messageout - handle bus service to send a message + * @info: interface which caused bus service + * + * Handle bus service to send a message. + * Note: We do not allow the device to change the data direction! + */ +static void fas216_send_messageout(FAS216_Info *info, int start) +{ + unsigned int tot_msglen = msgqueue_msglength(&info->scsi.msgs); + + fas216_checkmagic(info); + + fas216_cmd(info, CMD_FLUSHFIFO); + + if (tot_msglen) { + struct message *msg; + int msgnr = 0; + + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + int i; + + for (i = start; i < msg->length; i++) + fas216_writeb(info, REG_FF, msg->msg[i]); + + msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); + start = 0; + } + } else + fas216_writeb(info, REG_FF, NOP); + + fas216_cmd(info, CMD_TRANSFERINFO); + + info->scsi.phase = PHASE_MSGOUT; +} + +/** + * fas216_busservice_intr - handle bus service interrupt from FAS216 chip + * @info: interface which caused bus service interrupt + * @stat: Status register contents + * @is: SCSI Status register contents + * + * Handle a bus service interrupt from FAS216 chip + */ +static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int is) +{ + fas216_checkmagic(info); + + fas216_log(info, LOG_BUSSERVICE, + "bus service: stat=%02x is=%02x phase=%02x", + stat, is, info->scsi.phase); + + switch (info->scsi.phase) { + case PHASE_SELECTION: + if ((is & IS_BITS) != IS_MSGBYTESENT) + goto bad_is; + break; + + case PHASE_SELSTEPS: + switch (is & IS_BITS) { + case IS_SELARB: + case IS_MSGBYTESENT: + goto bad_is; + + case IS_NOTCOMMAND: + case IS_EARLYPHASE: + if ((stat & STAT_BUSMASK) == STAT_MESGIN) + break; + goto bad_is; + + case IS_COMPLETE: + break; + } + + default: + break; + } + + fas216_cmd(info, CMD_NOP); + +#define STATE(st,ph) ((ph) << 3 | (st)) + /* This table describes the legal SCSI state transitions, + * as described by the SCSI II spec. + */ + switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) { + case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGOUT): /* Message Out -> Data In */ + case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command -> Data In */ + case STATE(STAT_DATAIN, PHASE_MSGIN): /* Message In -> Data In */ + info->scsi.phase = PHASE_DATAIN; + fas216_transfer(info); + return; + + case STATE(STAT_DATAIN, PHASE_DATAIN): /* Data In -> Data In */ + case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out -> Data Out */ + fas216_cleanuptransfer(info); + fas216_transfer(info); + return; + + case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command -> Data Out */ + case STATE(STAT_DATAOUT, PHASE_MSGIN): /* Message In -> Data Out */ + fas216_cmd(info, CMD_FLUSHFIFO); + info->scsi.phase = PHASE_DATAOUT; + fas216_transfer(info); + return; + + case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out -> Status */ + case STATE(STAT_STATUS, PHASE_DATAIN): /* Data In -> Status */ + fas216_stoptransfer(info); + case STATE(STAT_STATUS, PHASE_SELSTEPS):/* Sel w/ steps -> Status */ + case STATE(STAT_STATUS, PHASE_MSGOUT): /* Message Out -> Status */ + case STATE(STAT_STATUS, PHASE_COMMAND): /* Command -> Status */ + case STATE(STAT_STATUS, PHASE_MSGIN): /* Message In -> Status */ + fas216_cmd(info, CMD_INITCMDCOMPLETE); + info->scsi.phase = PHASE_STATUS; + return; + + case STATE(STAT_MESGIN, PHASE_DATAOUT): /* Data Out -> Message In */ + case STATE(STAT_MESGIN, PHASE_DATAIN): /* Data In -> Message In */ + fas216_stoptransfer(info); + case STATE(STAT_MESGIN, PHASE_COMMAND): /* Command -> Message In */ + case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In */ + case STATE(STAT_MESGIN, PHASE_MSGOUT): /* Message Out -> Message In */ + info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + fas216_cmd(info, CMD_FLUSHFIFO); + fas216_cmd(info, CMD_TRANSFERINFO); + info->scsi.phase = PHASE_MSGIN; + return; + + case STATE(STAT_MESGIN, PHASE_MSGIN): + info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF; + fas216_cmd(info, CMD_TRANSFERINFO); + return; + + case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out -> Command */ + case STATE(STAT_COMMAND, PHASE_MSGIN): /* Message In -> Command */ + fas216_send_command(info); + info->scsi.phase = PHASE_COMMAND; + return; + + + /* + * Selection -> Message Out + */ + case STATE(STAT_MESGOUT, PHASE_SELECTION): + fas216_send_messageout(info, 1); + return; + + /* + * Message Out -> Message Out + */ + case STATE(STAT_MESGOUT, PHASE_SELSTEPS): + case STATE(STAT_MESGOUT, PHASE_MSGOUT): + /* + * If we get another message out phase, this usually + * means some parity error occurred. Resend complete + * set of messages. If we have more than one byte to + * send, we need to assert ATN again. + */ + if (info->device[info->SCpnt->device->id].parity_check) { + /* + * We were testing... good, the device + * supports parity checking. + */ + info->device[info->SCpnt->device->id].parity_check = 0; + info->device[info->SCpnt->device->id].parity_enabled = 1; + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + } + + if (msgqueue_msglength(&info->scsi.msgs) > 1) + fas216_cmd(info, CMD_SETATN); + /*FALLTHROUGH*/ + + /* + * Any -> Message Out + */ + case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT): + fas216_send_messageout(info, 0); + return; + + /* Error recovery rules. + * These either attempt to abort or retry the operation. + * TODO: we need more of these + */ + case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ + /* error - we've sent out all the command bytes + * we have. + * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS + * to include the command bytes sent for this to work + * correctly. + */ + printk(KERN_ERR "scsi%d.%c: " + "target trying to receive more command bytes\n", + info->host->host_no, fas216_target(info)); + fas216_cmd(info, CMD_SETATN); + fas216_set_stc(info, 15); + fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA); + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + return; + } + + if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { + printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", + info->host->host_no, fas216_target(info), + fas216_bus_phase(stat)); + msgqueue_flush(&info->scsi.msgs); + fas216_cmd(info, CMD_SETATN); + msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); + info->scsi.phase = PHASE_MSGOUT_EXPECT; + info->scsi.aborting = 1; + fas216_cmd(info, CMD_TRANSFERINFO); + return; + } + printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", + info->host->host_no, fas216_target(info), + fas216_bus_phase(stat), + fas216_drv_phase(info)); + print_debug_list(); + return; + +bad_is: + fas216_log(info, 0, "bus service at step %d?", is & IS_BITS); + fas216_dumpstate(info); + print_debug_list(); + + fas216_done(info, DID_ERROR); +} + +/** + * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip + * @info: interface which caused function done interrupt + * @stat: Status register contents + * @is: SCSI Status register contents + * + * Handle a function done interrupt from FAS216 chip + */ +static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is) +{ + unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF; + + fas216_checkmagic(info); + + fas216_log(info, LOG_FUNCTIONDONE, + "function done: stat=%02x is=%02x phase=%02x", + stat, is, info->scsi.phase); + + switch (info->scsi.phase) { + case PHASE_STATUS: /* status phase - read status and msg */ + if (fifo_len != 2) { + fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len); + } + /* + * Read status then message byte. + */ + info->scsi.SCp.Status = fas216_readb(info, REG_FF); + info->scsi.SCp.Message = fas216_readb(info, REG_FF); + info->scsi.phase = PHASE_DONE; + fas216_cmd(info, CMD_MSGACCEPTED); + break; + + case PHASE_IDLE: + case PHASE_SELECTION: + case PHASE_SELSTEPS: + break; + + case PHASE_MSGIN: /* message in phase */ + if ((stat & STAT_BUSMASK) == STAT_MESGIN) { + info->scsi.msgin_fifo = fifo_len; + fas216_message(info); + break; + } + + default: + fas216_log(info, 0, "internal phase %s for function done?" + " What do I do with this?", + fas216_target(info), fas216_drv_phase(info)); + } +} + +static void fas216_bus_reset(FAS216_Info *info) +{ + neg_t sync_state; + int i; + + msgqueue_flush(&info->scsi.msgs); + + sync_state = neg_invalid; + +#ifdef SCSI2_SYNC + if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA)) + sync_state = neg_wait; +#endif + + info->scsi.phase = PHASE_IDLE; + info->SCpnt = NULL; /* bug! */ + memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp)); + + for (i = 0; i < 8; i++) { + info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; + info->device[i].sync_state = sync_state; + info->device[i].period = info->ifcfg.asyncperiod / 4; + info->device[i].stp = info->scsi.async_stp; + info->device[i].sof = 0; + info->device[i].wide_xfer = 0; + } + + info->rst_bus_status = 1; + wake_up(&info->eh_wait); +} + +/** + * fas216_intr - handle interrupts to progress a command + * @info: interface to service + * + * Handle interrupts from the interface to progress a command + */ +irqreturn_t fas216_intr(FAS216_Info *info) +{ + unsigned char inst, is, stat; + int handled = IRQ_NONE; + + fas216_checkmagic(info); + + stat = fas216_readb(info, REG_STAT); + is = fas216_readb(info, REG_IS); + inst = fas216_readb(info, REG_INST); + + add_debug_list(stat, is, inst, info->scsi.phase); + + if (stat & STAT_INT) { + if (inst & INST_BUSRESET) { + fas216_log(info, 0, "bus reset detected"); + fas216_bus_reset(info); + scsi_report_bus_reset(info->host, 0); + } else if (inst & INST_ILLEGALCMD) { + fas216_log(info, LOG_ERROR, "illegal command given\n"); + fas216_dumpstate(info); + print_debug_list(); + } else if (inst & INST_DISCONNECT) + fas216_disconnect_intr(info); + else if (inst & INST_RESELECTED) /* reselected */ + fas216_reselected_intr(info); + else if (inst & INST_BUSSERVICE) /* bus service request */ + fas216_busservice_intr(info, stat, is); + else if (inst & INST_FUNCDONE) /* function done */ + fas216_funcdone_intr(info, stat, is); + else + fas216_log(info, 0, "unknown interrupt received:" + " phase %s inst %02X is %02X stat %02X", + fas216_drv_phase(info), inst, is, stat); + handled = IRQ_HANDLED; + } + return handled; +} + +static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + int tot_msglen; + + /* following what the ESP driver says */ + fas216_set_stc(info, 0); + fas216_cmd(info, CMD_NOP | CMD_WITHDMA); + + /* flush FIFO */ + fas216_cmd(info, CMD_FLUSHFIFO); + + /* load bus-id and timeout */ + fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); + fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); + + /* synchronous transfers */ + fas216_set_sync(info, SCpnt->device->id); + + tot_msglen = msgqueue_msglength(&info->scsi.msgs); + +#ifdef DEBUG_MESSAGES + { + struct message *msg; + int msgnr = 0, i; + + printk("scsi%d.%c: message out: ", + info->host->host_no, '0' + SCpnt->device->id); + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + printk("{ "); + for (i = 0; i < msg->length; i++) + printk("%02x ", msg->msg[i]); + printk("} "); + } + printk("\n"); + } +#endif + + if (tot_msglen == 1 || tot_msglen == 3) { + /* + * We have an easy message length to send... + */ + struct message *msg; + int msgnr = 0, i; + + info->scsi.phase = PHASE_SELSTEPS; + + /* load message bytes */ + while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { + for (i = 0; i < msg->length; i++) + fas216_writeb(info, REG_FF, msg->msg[i]); + msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); + } + + /* load command */ + for (i = 0; i < SCpnt->cmd_len; i++) + fas216_writeb(info, REG_FF, SCpnt->cmnd[i]); + + if (tot_msglen == 1) + fas216_cmd(info, CMD_SELECTATN); + else + fas216_cmd(info, CMD_SELECTATN3); + } else { + /* + * We have an unusual number of message bytes to send. + * Load first byte into fifo, and issue SELECT with ATN and + * stop steps. + */ + struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); + + fas216_writeb(info, REG_FF, msg->msg[0]); + msg->fifo = 1; + + fas216_cmd(info, CMD_SELECTATNSTOP); + } +} + +/* + * Decide whether we need to perform a parity test on this device. + * Can also be used to force parity error conditions during initial + * information transfer phase (message out) for test purposes. + */ +static int parity_test(FAS216_Info *info, int target) +{ +#if 0 + if (target == 3) { + info->device[target].parity_check = 0; + return 1; + } +#endif + return info->device[target].parity_check; +} + +static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + int disconnect_ok; + + /* + * claim host busy + */ + info->scsi.phase = PHASE_SELECTION; + info->scsi.SCp = SCpnt->SCp; + info->SCpnt = SCpnt; + info->dma.transfer_type = fasdma_none; + + if (parity_test(info, SCpnt->device->id)) + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE); + else + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + + /* + * Don't allow request sense commands to disconnect. + */ + disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE && + info->device[SCpnt->device->id].disconnect_ok; + + /* + * build outgoing message bytes + */ + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->device->lun)); + + /* + * add tag message if required + */ + if (SCpnt->tag) + msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); + + do { +#ifdef SCSI2_SYNC + if ((info->device[SCpnt->device->id].sync_state == neg_wait || + info->device[SCpnt->device->id].sync_state == neg_complete) && + (SCpnt->cmnd[0] == REQUEST_SENSE || + SCpnt->cmnd[0] == INQUIRY)) { + info->device[SCpnt->device->id].sync_state = neg_inprogress; + msgqueue_addmsg(&info->scsi.msgs, 5, + EXTENDED_MESSAGE, 3, EXTENDED_SDTR, + 1000 / info->ifcfg.clockrate, + info->ifcfg.sync_max_depth); + break; + } +#endif + } while (0); + + __fas216_start_command(info, SCpnt); +} + +static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ +#ifdef SCSI2_TAG + /* + * tagged queuing - allocate a new tag to this command + */ + if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && + SCpnt->cmnd[0] != INQUIRY) { + SCpnt->device->current_tag += 1; + if (SCpnt->device->current_tag == 0) + SCpnt->device->current_tag = 1; + SCpnt->tag = SCpnt->device->current_tag; + } else +#endif + set_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + + info->stats.removes += 1; + switch (SCpnt->cmnd[0]) { + case WRITE_6: + case WRITE_10: + case WRITE_12: + info->stats.writes += 1; + break; + case READ_6: + case READ_10: + case READ_12: + info->stats.reads += 1; + break; + default: + info->stats.miscs += 1; + break; + } +} + +static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + struct message *msg; + + /* + * claim host busy + */ + info->scsi.phase = PHASE_SELECTION; + info->scsi.SCp = SCpnt->SCp; + info->SCpnt = SCpnt; + info->dma.transfer_type = fasdma_none; + + fas216_log(info, LOG_ERROR, "sending bus device reset"); + + msgqueue_flush(&info->scsi.msgs); + msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET); + + /* following what the ESP driver says */ + fas216_set_stc(info, 0); + fas216_cmd(info, CMD_NOP | CMD_WITHDMA); + + /* flush FIFO */ + fas216_cmd(info, CMD_FLUSHFIFO); + + /* load bus-id and timeout */ + fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); + fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); + + /* synchronous transfers */ + fas216_set_sync(info, SCpnt->device->id); + + msg = msgqueue_getmsg(&info->scsi.msgs, 0); + + fas216_writeb(info, REG_FF, BUS_DEVICE_RESET); + msg->fifo = 1; + + fas216_cmd(info, CMD_SELECTATNSTOP); +} + +/** + * fas216_kick - kick a command to the interface + * @info: our host interface to kick + * + * Kick a command to the interface, interface should be idle. + * Notes: Interrupts are always disabled! + */ +static void fas216_kick(FAS216_Info *info) +{ + Scsi_Cmnd *SCpnt = NULL; +#define TYPE_OTHER 0 +#define TYPE_RESET 1 +#define TYPE_QUEUE 2 + int where_from = TYPE_OTHER; + + fas216_checkmagic(info); + + /* + * Obtain the next command to process. + */ + do { + if (info->rstSCpnt) { + SCpnt = info->rstSCpnt; + /* don't remove it */ + where_from = TYPE_RESET; + break; + } + + if (info->reqSCpnt) { + SCpnt = info->reqSCpnt; + info->reqSCpnt = NULL; + break; + } + + if (info->origSCpnt) { + SCpnt = info->origSCpnt; + info->origSCpnt = NULL; + break; + } + + /* retrieve next command */ + if (!SCpnt) { + SCpnt = queue_remove_exclude(&info->queues.issue, + info->busyluns); + where_from = TYPE_QUEUE; + break; + } + } while (0); + + if (!SCpnt) { + /* + * no command pending, so enable reselection. + */ + fas216_cmd(info, CMD_ENABLESEL); + return; + } + + /* + * We're going to start a command, so disable reselection + */ + fas216_cmd(info, CMD_DISABLESEL); + + if (info->scsi.disconnectable && info->SCpnt) { + fas216_log(info, LOG_CONNECT, + "moved command for %d to disconnected queue", + info->SCpnt->device->id); + queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); + info->scsi.disconnectable = 0; + info->SCpnt = NULL; + } + + fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt, + "starting"); + + switch (where_from) { + case TYPE_QUEUE: + fas216_allocate_tag(info, SCpnt); + case TYPE_OTHER: + fas216_start_command(info, SCpnt); + break; + case TYPE_RESET: + fas216_do_bus_device_reset(info, SCpnt); + break; + } + + fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]", + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + + /* + * should now get either DISCONNECT or + * (FUNCTION DONE with BUS SERVICE) interrupt + */ +} + +/* + * Clean up from issuing a BUS DEVICE RESET message to a device. + */ +static void +fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ + fas216_log(info, LOG_ERROR, "fas216 device reset complete"); + + info->rstSCpnt = NULL; + info->rst_dev_status = 1; + wake_up(&info->eh_wait); +} + +/** + * fas216_rq_sns_done - Finish processing automatic request sense command + * @info: interface that completed + * @SCpnt: command that completed + * @result: driver byte of result + * + * Finish processing automatic request sense command + */ +static void +fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ + fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, + "request sense complete, result=0x%04x%02x%02x", + result, SCpnt->SCp.Message, SCpnt->SCp.Status); + + if (result != DID_OK || SCpnt->SCp.Status != GOOD) + /* + * Something went wrong. Make sure that we don't + * have valid data in the sense buffer that could + * confuse the higher levels. + */ + memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); +//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->device->id); +//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); } + /* + * Note that we don't set SCpnt->result, since that should + * reflect the status of the command that we were asked by + * the upper layers to process. This would have been set + * correctly by fas216_std_done. + */ + SCpnt->scsi_done(SCpnt); +} + +/** + * fas216_std_done - finish processing of standard command + * @info: interface that completed + * @SCpnt: command that completed + * @result: driver byte of result + * + * Finish processing of standard command + */ +static void +fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +{ + info->stats.fins += 1; + + SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | + info->scsi.SCp.Status; + + fas216_log_command(info, LOG_CONNECT, SCpnt, + "command complete, result=0x%08x", SCpnt->result); + + /* + * If the driver detected an error, we're all done. + */ + if (host_byte(SCpnt->result) != DID_OK || + msg_byte(SCpnt->result) != COMMAND_COMPLETE) + goto done; + + /* + * If the command returned CHECK_CONDITION or COMMAND_TERMINATED + * status, request the sense information. + */ + if (status_byte(SCpnt->result) == CHECK_CONDITION || + status_byte(SCpnt->result) == COMMAND_TERMINATED) + goto request_sense; + + /* + * If the command did not complete with GOOD status, + * we are all done here. + */ + if (status_byte(SCpnt->result) != GOOD) + goto done; + + /* + * We have successfully completed a command. Make sure that + * we do not have any buffers left to transfer. The world + * is not perfect, and we seem to occasionally hit this. + * It can be indicative of a buggy driver, target or the upper + * levels of the SCSI code. + */ + if (info->scsi.SCp.ptr) { + switch (SCpnt->cmnd[0]) { + case INQUIRY: + case START_STOP: + case MODE_SENSE: + break; + + default: + printk(KERN_ERR "scsi%d.%c: incomplete data transfer " + "detected: res=%08X ptr=%p len=%X CDB: ", + info->host->host_no, '0' + SCpnt->device->id, + SCpnt->result, info->scsi.SCp.ptr, + info->scsi.SCp.this_residual); + print_command(SCpnt->cmnd); + SCpnt->result &= ~(255 << 16); + SCpnt->result |= DID_BAD_TARGET << 16; + goto request_sense; + } + } + +done: + if (SCpnt->scsi_done) { + SCpnt->scsi_done(SCpnt); + return; + } + + panic("scsi%d.H: null scsi_done function in fas216_done", + info->host->host_no); + + +request_sense: + if (SCpnt->cmnd[0] == REQUEST_SENSE) + goto done; + + fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, + "requesting sense"); + memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd)); + SCpnt->cmnd[0] = REQUEST_SENSE; + SCpnt->cmnd[1] = SCpnt->device->lun << 5; + SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); + SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); + SCpnt->SCp.buffer = NULL; + SCpnt->SCp.buffers_residual = 0; + SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; + SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); + SCpnt->SCp.Message = 0; + SCpnt->SCp.Status = 0; + SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); + SCpnt->sc_data_direction = SCSI_DATA_READ; + SCpnt->use_sg = 0; + SCpnt->tag = 0; + SCpnt->host_scribble = (void *)fas216_rq_sns_done; + + /* + * Place this command into the high priority "request + * sense" slot. This will be the very next command + * executed, unless a target connects to us. + */ + if (info->reqSCpnt) + printk(KERN_WARNING "scsi%d.%c: loosing request command\n", + info->host->host_no, '0' + SCpnt->device->id); + info->reqSCpnt = SCpnt; +} + +/** + * fas216_done - complete processing for current command + * @info: interface that completed + * @result: driver byte of result + * + * Complete processing for current command + */ +static void fas216_done(FAS216_Info *info, unsigned int result) +{ + void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int); + Scsi_Cmnd *SCpnt; + unsigned long flags; + + fas216_checkmagic(info); + + if (!info->SCpnt) + goto no_command; + + SCpnt = info->SCpnt; + info->SCpnt = NULL; + info->scsi.phase = PHASE_IDLE; + + if (info->scsi.aborting) { + fas216_log(info, 0, "uncaught abort - returning DID_ABORT"); + result = DID_ABORT; + info->scsi.aborting = 0; + } + + /* + * Sanity check the completion - if we have zero bytes left + * to transfer, we should not have a valid pointer. + */ + if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { + printk("scsi%d.%c: zero bytes left to transfer, but " + "buffer pointer still valid: ptr=%p len=%08x CDB: ", + info->host->host_no, '0' + SCpnt->device->id, + info->scsi.SCp.ptr, info->scsi.SCp.this_residual); + info->scsi.SCp.ptr = NULL; + print_command(SCpnt->cmnd); + } + + /* + * Clear down this command as completed. If we need to request + * the sense information, fas216_kick will re-assert the busy + * status. + */ + info->device[SCpnt->device->id].parity_check = 0; + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + + fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble; + fn(info, SCpnt, result); + + if (info->scsi.irq != NO_IRQ) { + spin_lock_irqsave(&info->host_lock, flags); + if (info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + spin_unlock_irqrestore(&info->host_lock, flags); + } + return; + +no_command: + panic("scsi%d.H: null command in fas216_done", + info->host->host_no); +} + +/** + * fas216_queue_command - queue a command for adapter to process. + * @SCpnt: Command to queue + * @done: done function to call once command is complete + * + * Queue a command for adapter to process. + * Returns: 0 on success, else error. + * Notes: io_request_lock is held, interrupts are disabled. + */ +int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + int result; + + fas216_checkmagic(info); + + fas216_log_command(info, LOG_CONNECT, SCpnt, + "received command (%p)", SCpnt); + + SCpnt->scsi_done = done; + SCpnt->host_scribble = (void *)fas216_std_done; + SCpnt->result = 0; + + init_SCp(SCpnt); + + info->stats.queues += 1; + SCpnt->tag = 0; + + spin_lock(&info->host_lock); + + /* + * Add command into execute queue and let it complete under + * whatever scheme we're using. + */ + result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt); + + /* + * If we successfully added the command, + * kick the interface to get it moving. + */ + if (result == 0 && info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + spin_unlock(&info->host_lock); + + fas216_log_target(info, LOG_CONNECT, -1, "queue %s", + result ? "failure" : "success"); + + return result; +} + +/** + * fas216_internal_done - trigger restart of a waiting thread in fas216_command + * @SCpnt: Command to wake + * + * Trigger restart of a waiting thread in fas216_command + */ +static void fas216_internal_done(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + + fas216_checkmagic(info); + + info->internal_done = 1; +} + +/** + * fas216_command - queue a command for adapter to process. + * @SCpnt: Command to queue + * + * Queue a command for adapter to process. + * Returns: scsi result code. + * Notes: io_request_lock is held, interrupts are disabled. + */ +int fas216_command(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + + fas216_checkmagic(info); + + /* + * We should only be using this if we don't have an interrupt. + * Provide some "incentive" to use the queueing code. + */ + if (info->scsi.irq != NO_IRQ) + BUG(); + + info->internal_done = 0; + fas216_queue_command(SCpnt, fas216_internal_done); + + /* + * This wastes time, since we can't return until the command is + * complete. We can't sleep either since we may get re-entered! + * However, we must re-enable interrupts, or else we'll be + * waiting forever. + */ + spin_unlock_irq(info->host->host_lock); + + while (!info->internal_done) { + /* + * If we don't have an IRQ, then we must poll the card for + * it's interrupt, and use that to call this driver's + * interrupt routine. That way, we keep the command + * progressing. Maybe we can add some inteligence here + * and go to sleep if we know that the device is going + * to be some time (eg, disconnected). + */ + if (fas216_readb(info, REG_STAT) & STAT_INT) { + spin_lock_irq(info->host->host_lock); + fas216_intr(info); + spin_unlock_irq(info->host->host_lock); + } + } + + spin_lock_irq(info->host->host_lock); + + return SCpnt->result; +} + +/* + * Error handler timeout function. Indicate that we timed out, + * and wake up any error handler process so it can continue. + */ +static void fas216_eh_timer(unsigned long data) +{ + FAS216_Info *info = (FAS216_Info *)data; + + fas216_log(info, LOG_ERROR, "error handling timed out\n"); + + del_timer(&info->eh_timer); + + if (info->rst_bus_status == 0) + info->rst_bus_status = -1; + if (info->rst_dev_status == 0) + info->rst_dev_status = -1; + + wake_up(&info->eh_wait); +} + +enum res_find { + res_failed, /* not found */ + res_success, /* command on issue queue */ + res_hw_abort /* command on disconnected dev */ +}; + +/** + * fas216_do_abort - decide how to abort a command + * @SCpnt: command to abort + * + * Decide how to abort a command. + * Returns: abort status + */ +static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +{ + enum res_find res = res_failed; + + if (queue_remove_cmd(&info->queues.issue, SCpnt)) { + /* + * The command was on the issue queue, and has not been + * issued yet. We can remove the command from the queue, + * and acknowledge the abort. Neither the device nor the + * interface know about the command. + */ + printk("on issue queue "); + + res = res_success; + } else if (queue_remove_cmd(&info->queues.disconnected, SCpnt)) { + /* + * The command was on the disconnected queue. We must + * reconnect with the device if possible, and send it + * an abort message. + */ + printk("on disconnected queue "); + + res = res_hw_abort; + } else if (info->SCpnt == SCpnt) { + printk("executing "); + + switch (info->scsi.phase) { + /* + * If the interface is idle, and the command is 'disconnectable', + * then it is the same as on the disconnected queue. + */ + case PHASE_IDLE: + if (info->scsi.disconnectable) { + info->scsi.disconnectable = 0; + info->SCpnt = NULL; + res = res_hw_abort; + } + break; + + default: + break; + } + } else if (info->origSCpnt == SCpnt) { + /* + * The command will be executed next, but a command + * is currently using the interface. This is similar to + * being on the issue queue, except the busylun bit has + * been set. + */ + info->origSCpnt = NULL; + clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); + printk("waiting for execution "); + res = res_success; + } else + printk("unknown "); + + return res; +} + +/** + * fas216_eh_abort - abort this command + * @SCpnt: command to abort + * + * Abort this command. + * Returns: FAILED if unable to abort + * Notes: io_request_lock is taken, and irqs are disabled + */ +int fas216_eh_abort(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + int result = FAILED; + + fas216_checkmagic(info); + + info->stats.aborts += 1; + + printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no); + print_command(SCpnt->data_cmnd); + + print_debug_list(); + fas216_dumpstate(info); + + printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt); + + switch (fas216_find_command(info, SCpnt)) { + /* + * We found the command, and cleared it out. Either + * the command is still known to be executing on the + * target, or the busylun bit is not set. + */ + case res_success: + printk("success\n"); + result = SUCCESS; + break; + + /* + * We need to reconnect to the target and send it an + * ABORT or ABORT_TAG message. We can only do this + * if the bus is free. + */ + case res_hw_abort: + + + /* + * We are unable to abort the command for some reason. + */ + default: + case res_failed: + printk("failed\n"); + break; + } + + return result; +} + +/** + * fas216_eh_device_reset - Reset the device associated with this command + * @SCpnt: command specifing device to reset + * + * Reset the device associated with this command. + * Returns: FAILED if unable to reset. + * Notes: We won't be re-entered, so we'll only have one device + * reset on the go at one time. + */ +int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + unsigned long flags; + int i, res = FAILED, target = SCpnt->device->id; + + fas216_log(info, LOG_ERROR, "device reset for target %d", target); + + spin_lock_irqsave(&info->host_lock, flags); + + do { + /* + * If we are currently connected to a device, and + * it is the device we want to reset, there is + * nothing we can do here. Chances are it is stuck, + * and we need a bus reset. + */ + if (info->SCpnt && !info->scsi.disconnectable && + info->SCpnt->device->id == SCpnt->device->id) + break; + + /* + * We're going to be resetting this device. Remove + * all pending commands from the driver. By doing + * so, we guarantee that we won't touch the command + * structures except to process the reset request. + */ + queue_remove_all_target(&info->queues.issue, target); + queue_remove_all_target(&info->queues.disconnected, target); + if (info->origSCpnt && info->origSCpnt->device->id == target) + info->origSCpnt = NULL; + if (info->reqSCpnt && info->reqSCpnt->device->id == target) + info->reqSCpnt = NULL; + for (i = 0; i < 8; i++) + clear_bit(target * 8 + i, info->busyluns); + + /* + * Hijack this SCSI command structure to send + * a bus device reset message to this device. + */ + SCpnt->host_scribble = (void *)fas216_devicereset_done; + + info->rst_dev_status = 0; + info->rstSCpnt = SCpnt; + + if (info->scsi.phase == PHASE_IDLE) + fas216_kick(info); + + mod_timer(&info->eh_timer, 30 * HZ); + spin_unlock_irqrestore(&info->host_lock, flags); + + /* + * Wait up to 30 seconds for the reset to complete. + */ + wait_event(info->eh_wait, info->rst_dev_status); + + del_timer_sync(&info->eh_timer); + spin_lock_irqsave(&info->host_lock, flags); + info->rstSCpnt = NULL; + + if (info->rst_dev_status == 1) + res = SUCCESS; + } while (0); + + SCpnt->host_scribble = NULL; + spin_unlock_irqrestore(&info->host_lock, flags); + + fas216_log(info, LOG_ERROR, "device reset complete: %s\n", + res == SUCCESS ? "success" : "failed"); + + return res; +} + +/** + * fas216_eh_bus_reset - Reset the bus associated with the command + * @SCpnt: command specifing bus to reset + * + * Reset the bus associated with the command. + * Returns: FAILED if unable to reset. + * Notes: Further commands are blocked. + */ +int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + unsigned long flags; + Scsi_Device *SDpnt; + + fas216_checkmagic(info); + fas216_log(info, LOG_ERROR, "resetting bus"); + + info->stats.bus_resets += 1; + + spin_lock_irqsave(&info->host_lock, flags); + + /* + * Stop all activity on this interface. + */ + fas216_aborttransfer(info); + fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); + + /* + * Clear any pending interrupts. + */ + while (fas216_readb(info, REG_STAT) & STAT_INT) + fas216_readb(info, REG_INST); + + info->rst_bus_status = 0; + + /* + * For each attached hard-reset device, clear out + * all command structures. Leave the running + * command in place. + */ + list_for_each_entry(SDpnt, &info->host->my_devices, siblings) { + int i; + + if (SDpnt->soft_reset) + continue; + + queue_remove_all_target(&info->queues.issue, SDpnt->id); + queue_remove_all_target(&info->queues.disconnected, SDpnt->id); + if (info->origSCpnt && info->origSCpnt->device->id == SDpnt->id) + info->origSCpnt = NULL; + if (info->reqSCpnt && info->reqSCpnt->device->id == SDpnt->id) + info->reqSCpnt = NULL; + info->SCpnt = NULL; + + for (i = 0; i < 8; i++) + clear_bit(SDpnt->id * 8 + i, info->busyluns); + } + + info->scsi.phase = PHASE_IDLE; + + /* + * Reset the SCSI bus. Device cleanup happens in + * the interrupt handler. + */ + fas216_cmd(info, CMD_RESETSCSI); + + mod_timer(&info->eh_timer, jiffies + HZ); + spin_unlock_irqrestore(&info->host_lock, flags); + + /* + * Wait one second for the interrupt. + */ + wait_event(info->eh_wait, info->rst_bus_status); + del_timer_sync(&info->eh_timer); + + fas216_log(info, LOG_ERROR, "bus reset complete: %s\n", + info->rst_bus_status == 1 ? "success" : "failed"); + + return info->rst_bus_status == 1 ? SUCCESS : FAILED; +} + +/** + * fas216_init_chip - Initialise FAS216 state after reset + * @info: state structure for interface + * + * Initialise FAS216 state after reset + */ +static void fas216_init_chip(FAS216_Info *info) +{ + unsigned int clock = ((info->ifcfg.clockrate - 1) / 5 + 1) & 7; + fas216_writeb(info, REG_CLKF, clock); + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]); + fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]); + fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); + fas216_writeb(info, REG_SOF, 0); + fas216_writeb(info, REG_STP, info->scsi.async_stp); + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); +} + +/** + * fas216_eh_host_reset - Reset the host associated with this command + * @SCpnt: command specifing host to reset + * + * Reset the host associated with this command. + * Returns: FAILED if unable to reset. + * Notes: io_request_lock is taken, and irqs are disabled + */ +int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) +{ + FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; + + fas216_checkmagic(info); + + printk("scsi%d.%c: %s: resetting host\n", + info->host->host_no, '0' + SCpnt->device->id, __FUNCTION__); + + /* + * Reset the SCSI chip. + */ + fas216_cmd(info, CMD_RESETCHIP); + + /* + * Ugly ugly ugly! + * We need to release the host_lock and enable + * IRQs if we sleep, but we must relock and disable + * IRQs after the sleep. + */ + spin_unlock_irq(info->host->host_lock); + scsi_sleep(50 * HZ/100); + spin_lock_irq(info->host->host_lock); + + /* + * Release the SCSI reset. + */ + fas216_cmd(info, CMD_NOP); + + fas216_init_chip(info); + + return SUCCESS; +} + +#define TYPE_UNKNOWN 0 +#define TYPE_NCR53C90 1 +#define TYPE_NCR53C90A 2 +#define TYPE_NCR53C9x 3 +#define TYPE_Am53CF94 4 +#define TYPE_EmFAS216 5 +#define TYPE_QLFAS216 6 + +static char *chip_types[] = { + "unknown", + "NS NCR53C90", + "NS NCR53C90A", + "NS NCR53C9x", + "AMD Am53CF94", + "Emulex FAS216", + "QLogic FAS216" +}; + +static int fas216_detect_type(FAS216_Info *info) +{ + int family, rev; + + /* + * Reset the chip. + */ + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + udelay(50); + fas216_writeb(info, REG_CMD, CMD_NOP); + + /* + * Check to see if control reg 2 is present. + */ + fas216_writeb(info, REG_CNTL3, 0); + fas216_writeb(info, REG_CNTL2, CNTL2_S2FE); + + /* + * If we are unable to read back control reg 2 + * correctly, it is not present, and we have a + * NCR53C90. + */ + if ((fas216_readb(info, REG_CNTL2) & (~0xe0)) != CNTL2_S2FE) + return TYPE_NCR53C90; + + /* + * Now, check control register 3 + */ + fas216_writeb(info, REG_CNTL2, 0); + fas216_writeb(info, REG_CNTL3, 0); + fas216_writeb(info, REG_CNTL3, 5); + + /* + * If we are unable to read the register back + * correctly, we have a NCR53C90A + */ + if (fas216_readb(info, REG_CNTL3) != 5) + return TYPE_NCR53C90A; + + /* + * Now read the ID from the chip. + */ + fas216_writeb(info, REG_CNTL3, 0); + + fas216_writeb(info, REG_CNTL3, CNTL3_ADIDCHK); + fas216_writeb(info, REG_CNTL3, 0); + + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + udelay(50); + fas216_writeb(info, REG_CMD, CMD_WITHDMA | CMD_NOP); + + fas216_writeb(info, REG_CNTL2, CNTL2_ENF); + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + udelay(50); + fas216_writeb(info, REG_CMD, CMD_NOP); + + rev = fas216_readb(info, REG_ID); + family = rev >> 3; + rev &= 7; + + switch (family) { + case 0x01: + if (rev == 4) + return TYPE_Am53CF94; + break; + + case 0x02: + switch (rev) { + case 2: + return TYPE_EmFAS216; + case 3: + return TYPE_QLFAS216; + } + break; + + default: + break; + } + printk("family %x rev %x\n", family, rev); + return TYPE_NCR53C9x; +} + +/** + * fas216_reset_state - Initialise driver internal state + * @info: state to initialise + * + * Initialise driver internal state + */ +static void fas216_reset_state(FAS216_Info *info) +{ + int i; + + fas216_checkmagic(info); + + fas216_bus_reset(info); + + /* + * Clear out all stale info in our state structure + */ + memset(info->busyluns, 0, sizeof(info->busyluns)); + info->scsi.disconnectable = 0; + info->scsi.aborting = 0; + + for (i = 0; i < 8; i++) { + info->device[i].parity_enabled = 0; + info->device[i].parity_check = 1; + } + + /* + * Drain all commands on disconnected queue + */ + while (queue_remove(&info->queues.disconnected) != NULL); + + /* + * Remove executing commands. + */ + info->SCpnt = NULL; + info->reqSCpnt = NULL; + info->rstSCpnt = NULL; + info->origSCpnt = NULL; +} + +/** + * fas216_init - initialise FAS/NCR/AMD SCSI structures. + * @host: a driver-specific filled-out structure + * + * Initialise FAS/NCR/AMD SCSI structures. + * Returns: 0 on success + */ +int fas216_init(struct Scsi_Host *host) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + + info->magic_start = MAGIC; + info->magic_end = MAGIC; + info->host = host; + info->scsi.cfg[0] = host->this_id | CNTL1_PERE; + info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE; + info->scsi.cfg[2] = info->ifcfg.cntl3 | + CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM; + info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod); + + info->rst_dev_status = -1; + info->rst_bus_status = -1; + init_waitqueue_head(&info->eh_wait); + init_timer(&info->eh_timer); + info->eh_timer.data = (unsigned long)info; + info->eh_timer.function = fas216_eh_timer; + + spin_lock_init(&info->host_lock); + + memset(&info->stats, 0, sizeof(info->stats)); + + msgqueue_initialise(&info->scsi.msgs); + + if (!queue_initialise(&info->queues.issue)) + return -ENOMEM; + + if (!queue_initialise(&info->queues.disconnected)) { + queue_free(&info->queues.issue); + return -ENOMEM; + } + + return 0; +} + +/** + * fas216_add - initialise FAS/NCR/AMD SCSI ic. + * @host: a driver-specific filled-out structure + * @dev: parent device + * + * Initialise FAS/NCR/AMD SCSI ic. + * Returns: 0 on success + */ +int fas216_add(struct Scsi_Host *host, struct device *dev) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + int type, ret; + + if (info->ifcfg.clockrate <= 10 || info->ifcfg.clockrate > 40) { + printk(KERN_CRIT "fas216: invalid clock rate %u MHz\n", + info->ifcfg.clockrate); + return -EINVAL; + } + + fas216_reset_state(info); + type = fas216_detect_type(info); + info->scsi.type = chip_types[type]; + + udelay(300); + + /* + * Initialise the chip correctly. + */ + fas216_init_chip(info); + + /* + * Reset the SCSI bus. We don't want to see + * the resulting reset interrupt, so mask it + * out. + */ + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_DISR); + fas216_writeb(info, REG_CMD, CMD_RESETSCSI); + + /* + * scsi standard says wait 250ms + */ + spin_unlock_irq(info->host->host_lock); + scsi_sleep(100*HZ/100); + spin_lock_irq(info->host->host_lock); + + fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); + fas216_readb(info, REG_INST); + + fas216_checkmagic(info); + + ret = scsi_add_host(host, dev); + if (ret) + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); + + return ret; +} + +void fas216_remove(struct Scsi_Host *host) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + + fas216_checkmagic(info); + scsi_remove_host(host); + + fas216_writeb(info, REG_CMD, CMD_RESETCHIP); +} + +/** + * fas216_release - release all resources for FAS/NCR/AMD SCSI ic. + * @host: a driver-specific filled-out structure + * + * release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. + */ +void fas216_release(struct Scsi_Host *host) +{ + FAS216_Info *info = (FAS216_Info *)host->hostdata; + + queue_free(&info->queues.disconnected); + queue_free(&info->queues.issue); +} + +int fas216_print_host(FAS216_Info *info, char *buffer) +{ + return sprintf(buffer, + "\n" + "Chip : %s\n" + " Address: 0x%08lx\n" + " IRQ : %d\n" + " DMA : %d\n", + info->scsi.type, info->host->io_port, + info->host->irq, info->host->dma_channel); +} + +int fas216_print_stats(FAS216_Info *info, char *buffer) +{ + char *p = buffer; + + p += sprintf(p, "\n" + "Command Statistics:\n" + " Queued : %u\n" + " Issued : %u\n" + " Completed : %u\n" + " Reads : %u\n" + " Writes : %u\n" + " Others : %u\n" + " Disconnects: %u\n" + " Aborts : %u\n" + " Bus resets : %u\n" + " Host resets: %u\n", + info->stats.queues, info->stats.removes, + info->stats.fins, info->stats.reads, + info->stats.writes, info->stats.miscs, + info->stats.disconnects, info->stats.aborts, + info->stats.bus_resets, info->stats.host_resets); + + return p - buffer; +} + +int fas216_print_devices(FAS216_Info *info, char *buffer) +{ + struct fas216_device *dev; + Scsi_Device *scd; + char *p = buffer; + + p += sprintf(p, "Device/Lun TaggedQ Parity Sync\n"); + + list_for_each_entry(scd, &info->host->my_devices, siblings) { + dev = &info->device[scd->id]; + p += sprintf(p, " %d/%d ", scd->id, scd->lun); + if (scd->tagged_supported) + p += sprintf(p, "%3sabled(%3d) ", + scd->tagged_queue ? "en" : "dis", + scd->current_tag); + else + p += sprintf(p, "unsupported "); + + p += sprintf(p, "%3sabled ", dev->parity_enabled ? "en" : "dis"); + + if (dev->sof) + p += sprintf(p, "offset %d, %d ns\n", + dev->sof, dev->period * 4); + else + p += sprintf(p, "async\n"); + } + + return p - buffer; +} + +EXPORT_SYMBOL(fas216_init); +EXPORT_SYMBOL(fas216_add); +EXPORT_SYMBOL(fas216_queue_command); +EXPORT_SYMBOL(fas216_command); +EXPORT_SYMBOL(fas216_intr); +EXPORT_SYMBOL(fas216_remove); +EXPORT_SYMBOL(fas216_release); +EXPORT_SYMBOL(fas216_eh_abort); +EXPORT_SYMBOL(fas216_eh_device_reset); +EXPORT_SYMBOL(fas216_eh_bus_reset); +EXPORT_SYMBOL(fas216_eh_host_reset); +EXPORT_SYMBOL(fas216_print_host); +EXPORT_SYMBOL(fas216_print_stats); +EXPORT_SYMBOL(fas216_print_devices); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Generic FAS216/NCR53C9x driver core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/fas216.h b/drivers/scsi/arm/fas216.h new file mode 100644 index 000000000000..d6b6322f9d53 --- /dev/null +++ b/drivers/scsi/arm/fas216.h @@ -0,0 +1,394 @@ +/* + * linux/drivers/acorn/scsi/fas216.h + * + * Copyright (C) 1997-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * FAS216 generic driver + */ +#ifndef FAS216_H +#define FAS216_H + +#ifndef NO_IRQ +#define NO_IRQ 255 +#endif + +#include "queue.h" +#include "msgqueue.h" + +/* FAS register definitions */ + +/* transfer count low */ +#define REG_CTCL (0) +#define REG_STCL (0) + +/* transfer count medium */ +#define REG_CTCM (1) +#define REG_STCM (1) + +/* fifo data */ +#define REG_FF (2) + +/* command */ +#define REG_CMD (3) +#define CMD_NOP 0x00 +#define CMD_FLUSHFIFO 0x01 +#define CMD_RESETCHIP 0x02 +#define CMD_RESETSCSI 0x03 + +#define CMD_TRANSFERINFO 0x10 +#define CMD_INITCMDCOMPLETE 0x11 +#define CMD_MSGACCEPTED 0x12 +#define CMD_PADBYTES 0x18 +#define CMD_SETATN 0x1a +#define CMD_RSETATN 0x1b + +#define CMD_SELECTWOATN 0x41 +#define CMD_SELECTATN 0x42 +#define CMD_SELECTATNSTOP 0x43 +#define CMD_ENABLESEL 0x44 +#define CMD_DISABLESEL 0x45 +#define CMD_SELECTATN3 0x46 +#define CMD_RESEL3 0x47 + +#define CMD_WITHDMA 0x80 + +/* status register (read) */ +#define REG_STAT (4) +#define STAT_IO (1 << 0) /* IO phase */ +#define STAT_CD (1 << 1) /* CD phase */ +#define STAT_MSG (1 << 2) /* MSG phase */ +#define STAT_TRANSFERDONE (1 << 3) /* Transfer completed */ +#define STAT_TRANSFERCNTZ (1 << 4) /* Transfer counter is zero */ +#define STAT_PARITYERROR (1 << 5) /* Parity error */ +#define STAT_REALBAD (1 << 6) /* Something bad */ +#define STAT_INT (1 << 7) /* Interrupt */ + +#define STAT_BUSMASK (STAT_MSG|STAT_CD|STAT_IO) +#define STAT_DATAOUT (0) /* Data out */ +#define STAT_DATAIN (STAT_IO) /* Data in */ +#define STAT_COMMAND (STAT_CD) /* Command out */ +#define STAT_STATUS (STAT_CD|STAT_IO) /* Status In */ +#define STAT_MESGOUT (STAT_MSG|STAT_CD) /* Message out */ +#define STAT_MESGIN (STAT_MSG|STAT_CD|STAT_IO) /* Message In */ + +/* bus ID for select / reselect */ +#define REG_SDID (4) +#define BUSID(target) ((target) & 7) + +/* Interrupt status register (read) */ +#define REG_INST (5) +#define INST_SELWOATN (1 << 0) /* Select w/o ATN */ +#define INST_SELATN (1 << 1) /* Select w/ATN */ +#define INST_RESELECTED (1 << 2) /* Reselected */ +#define INST_FUNCDONE (1 << 3) /* Function done */ +#define INST_BUSSERVICE (1 << 4) /* Bus service */ +#define INST_DISCONNECT (1 << 5) /* Disconnect */ +#define INST_ILLEGALCMD (1 << 6) /* Illegal command */ +#define INST_BUSRESET (1 << 7) /* SCSI Bus reset */ + +/* Timeout register (write) */ +#define REG_STIM (5) + +/* Sequence step register (read) */ +#define REG_IS (6) +#define IS_BITS 0x07 +#define IS_SELARB 0x00 /* Select & Arb ok */ +#define IS_MSGBYTESENT 0x01 /* One byte message sent*/ +#define IS_NOTCOMMAND 0x02 /* Not in command state */ +#define IS_EARLYPHASE 0x03 /* Early phase change */ +#define IS_COMPLETE 0x04 /* Command ok */ +#define IS_SOF 0x08 /* Sync off flag */ + +/* Transfer period step (write) */ +#define REG_STP (6) + +/* Synchronous Offset (write) */ +#define REG_SOF (7) + +/* Fifo state register (read) */ +#define REG_CFIS (7) +#define CFIS_CF 0x1f /* Num bytes in FIFO */ +#define CFIS_IS 0xe0 /* Step */ + +/* config register 1 */ +#define REG_CNTL1 (8) +#define CNTL1_CID (7 << 0) /* Chip ID */ +#define CNTL1_STE (1 << 3) /* Self test enable */ +#define CNTL1_PERE (1 << 4) /* Parity enable reporting en. */ +#define CNTL1_PTE (1 << 5) /* Parity test enable */ +#define CNTL1_DISR (1 << 6) /* Disable Irq on SCSI reset */ +#define CNTL1_ETM (1 << 7) /* Extended Timing Mode */ + +/* Clock conversion factor (read) */ +#define REG_CLKF (9) +#define CLKF_F37MHZ 0x00 /* 35.01 - 40 MHz */ +#define CLKF_F10MHZ 0x02 /* 10 MHz */ +#define CLKF_F12MHZ 0x03 /* 10.01 - 15 MHz */ +#define CLKF_F17MHZ 0x04 /* 15.01 - 20 MHz */ +#define CLKF_F22MHZ 0x05 /* 20.01 - 25 MHz */ +#define CLKF_F27MHZ 0x06 /* 25.01 - 30 MHz */ +#define CLKF_F32MHZ 0x07 /* 30.01 - 35 MHz */ + +/* Chip test register (write) */ +#define REG_FTM (10) +#define TEST_FTM 0x01 /* Force target mode */ +#define TEST_FIM 0x02 /* Force initiator mode */ +#define TEST_FHI 0x04 /* Force high impedance mode */ + +/* Configuration register 2 (read/write) */ +#define REG_CNTL2 (11) +#define CNTL2_PGDP (1 << 0) /* Pass Th/Generate Data Parity */ +#define CNTL2_PGRP (1 << 1) /* Pass Th/Generate Reg Parity */ +#define CNTL2_ACDPE (1 << 2) /* Abort on Cmd/Data Parity Err */ +#define CNTL2_S2FE (1 << 3) /* SCSI2 Features Enable */ +#define CNTL2_TSDR (1 << 4) /* Tristate DREQ */ +#define CNTL2_SBO (1 << 5) /* Select Byte Order */ +#define CNTL2_ENF (1 << 6) /* Enable features */ +#define CNTL2_DAE (1 << 7) /* Data Alignment Enable */ + +/* Configuration register 3 (read/write) */ +#define REG_CNTL3 (12) +#define CNTL3_BS8 (1 << 0) /* Burst size 8 */ +#define CNTL3_MDM (1 << 1) /* Modify DMA mode */ +#define CNTL3_LBTM (1 << 2) /* Last Byte Transfer mode */ +#define CNTL3_FASTCLK (1 << 3) /* Fast SCSI clocking */ +#define CNTL3_FASTSCSI (1 << 4) /* Fast SCSI */ +#define CNTL3_G2CB (1 << 5) /* Group2 SCSI support */ +#define CNTL3_QTAG (1 << 6) /* Enable 3 byte msgs */ +#define CNTL3_ADIDCHK (1 << 7) /* Additional ID check */ + +/* High transfer count (read/write) */ +#define REG_CTCH (14) +#define REG_STCH (14) + +/* ID register (read only) */ +#define REG_ID (14) + +/* Data alignment */ +#define REG_DAL (15) + +typedef enum { + PHASE_IDLE, /* we're not planning on doing anything */ + PHASE_SELECTION, /* selecting a device */ + PHASE_SELSTEPS, /* selection with command steps */ + PHASE_COMMAND, /* command sent */ + PHASE_MESSAGESENT, /* selected, and we're sending cmd */ + PHASE_DATAOUT, /* data out to device */ + PHASE_DATAIN, /* data in from device */ + PHASE_MSGIN, /* message in from device */ + PHASE_MSGIN_DISCONNECT, /* disconnecting from bus */ + PHASE_MSGOUT, /* after message out phase */ + PHASE_MSGOUT_EXPECT, /* expecting message out */ + PHASE_STATUS, /* status from device */ + PHASE_DONE /* Command complete */ +} phase_t; + +typedef enum { + DMA_OUT, /* DMA from memory to chip */ + DMA_IN /* DMA from chip to memory */ +} fasdmadir_t; + +typedef enum { + fasdma_none, /* No dma */ + fasdma_pio, /* PIO mode */ + fasdma_pseudo, /* Pseudo DMA */ + fasdma_real_block, /* Real DMA, on block by block basis */ + fasdma_real_all /* Real DMA, on request by request */ +} fasdmatype_t; + +typedef enum { + neg_wait, /* Negociate with device */ + neg_inprogress, /* Negociation sent */ + neg_complete, /* Negociation complete */ + neg_targcomplete, /* Target completed negociation */ + neg_invalid /* Negociation not supported */ +} neg_t; + +#define MAGIC 0x441296bdUL +#define NR_MSGS 8 + +#define FASCAP_DMA (1 << 0) +#define FASCAP_PSEUDODMA (1 << 1) + +typedef struct { + unsigned long magic_start; + spinlock_t host_lock; + struct Scsi_Host *host; /* host */ + Scsi_Cmnd *SCpnt; /* currently processing command */ + Scsi_Cmnd *origSCpnt; /* original connecting command */ + Scsi_Cmnd *reqSCpnt; /* request sense command */ + Scsi_Cmnd *rstSCpnt; /* reset command */ + Scsi_Cmnd *pending_SCpnt[8]; /* per-device pending commands */ + int next_pending; /* next pending device */ + + /* + * Error recovery + */ + wait_queue_head_t eh_wait; + struct timer_list eh_timer; + unsigned int rst_dev_status; + unsigned int rst_bus_status; + + /* driver information */ + struct { + phase_t phase; /* current phase */ + void *io_base; /* iomem base of FAS216 */ + unsigned int io_port; /* base address of FAS216 */ + unsigned int io_shift; /* shift to adjust reg offsets by */ + unsigned char cfg[4]; /* configuration registers */ + const char *type; /* chip type */ + unsigned int irq; /* interrupt */ + + Scsi_Pointer SCp; /* current commands data pointer */ + + MsgQueue_t msgs; /* message queue for connected device */ + + unsigned int async_stp; /* Async transfer STP value */ + unsigned char msgin_fifo; /* bytes in fifo at time of message in */ + unsigned char message[256]; /* last message received from device */ + + unsigned char disconnectable:1; /* this command can be disconnected */ + unsigned char aborting:1; /* aborting command */ + } scsi; + + /* statistics information */ + struct { + unsigned int queues; + unsigned int removes; + unsigned int fins; + unsigned int reads; + unsigned int writes; + unsigned int miscs; + unsigned int disconnects; + unsigned int aborts; + unsigned int bus_resets; + unsigned int host_resets; + } stats; + + /* configuration information */ + struct { + unsigned char clockrate; /* clock rate of FAS device (MHz) */ + unsigned char select_timeout; /* timeout (R5) */ + unsigned char sync_max_depth; /* Synchronous xfer max fifo depth */ + unsigned char wide_max_size; /* Maximum wide transfer size */ + unsigned char cntl3; /* Control Reg 3 */ + unsigned int asyncperiod; /* Async transfer period (ns) */ + unsigned int capabilities; /* driver capabilities */ + unsigned int disconnect_ok:1; /* Disconnects allowed? */ + } ifcfg; + + /* queue handling */ + struct { + Queue_t issue; /* issue queue */ + Queue_t disconnected; /* disconnected command queue */ + } queues; + + /* per-device info */ + struct fas216_device { + unsigned char disconnect_ok:1; /* device can disconnect */ + unsigned char parity_enabled:1; /* parity checking enabled */ + unsigned char parity_check:1; /* need to check parity checking */ + unsigned char period; /* sync xfer period in (*4ns) */ + unsigned char stp; /* synchronous transfer period */ + unsigned char sof; /* synchronous offset register */ + unsigned char wide_xfer; /* currently negociated wide transfer */ + neg_t sync_state; /* synchronous transfer mode */ + neg_t wide_state; /* wide transfer mode */ + } device[8]; + unsigned long busyluns[64/sizeof(unsigned long)];/* array of bits indicating LUNs busy */ + + /* dma */ + struct { + fasdmatype_t transfer_type; /* current type of DMA transfer */ + fasdmatype_t (*setup) (struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, fasdmatype_t min_dma); + void (*pseudo)(struct Scsi_Host *host, Scsi_Pointer *SCp, fasdmadir_t direction, int transfer); + void (*stop) (struct Scsi_Host *host, Scsi_Pointer *SCp); + } dma; + + /* miscellaneous */ + int internal_done; /* flag to indicate request done */ + + unsigned long magic_end; +} FAS216_Info; + +/* Function: int fas216_init (struct Scsi_Host *instance) + * Purpose : initialise FAS/NCR/AMD SCSI structures. + * Params : instance - a driver-specific filled-out structure + * Returns : 0 on success + */ +extern int fas216_init (struct Scsi_Host *instance); + +/* Function: int fas216_add (struct Scsi_Host *instance, struct device *dev) + * Purpose : initialise FAS/NCR/AMD SCSI ic. + * Params : instance - a driver-specific filled-out structure + * Returns : 0 on success + */ +extern int fas216_add (struct Scsi_Host *instance, struct device *dev); + +/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) + * Purpose : queue a command for adapter to process. + * Params : SCpnt - Command to queue + * done - done function to call once command is complete + * Returns : 0 - success, else error + */ +extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); + +/* Function: int fas216_command (Scsi_Cmnd *SCpnt) + * Purpose : queue a command for adapter to process. + * Params : SCpnt - Command to queue + * Returns : scsi result code + */ +extern int fas216_command (Scsi_Cmnd *); + +/* Function: irqreturn_t fas216_intr (FAS216_Info *info) + * Purpose : handle interrupts from the interface to progress a command + * Params : info - interface to service + */ +extern irqreturn_t fas216_intr (FAS216_Info *info); + +extern void fas216_remove (struct Scsi_Host *instance); + +/* Function: void fas216_release (struct Scsi_Host *instance) + * Purpose : release all resources and put everything to bed for FAS/NCR/AMD SCSI ic. + * Params : instance - a driver-specific filled-out structure + * Returns : 0 on success + */ +extern void fas216_release (struct Scsi_Host *instance); + +extern int fas216_print_host(FAS216_Info *info, char *buffer); +extern int fas216_print_stats(FAS216_Info *info, char *buffer); +extern int fas216_print_devices(FAS216_Info *info, char *buffer); + +/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) + * Purpose : abort this command + * Params : SCpnt - command to abort + * Returns : FAILED if unable to abort + */ +extern int fas216_eh_abort(Scsi_Cmnd *SCpnt); + +/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the device associated with this command + * Params : SCpnt - command specifing device to reset + * Returns : FAILED if unable to reset + */ +extern int fas216_eh_device_reset(Scsi_Cmnd *SCpnt); + +/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the complete bus associated with this command + * Params : SCpnt - command specifing bus to reset + * Returns : FAILED if unable to reset + */ +extern int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt); + +/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) + * Purpose : Reset the host associated with this command + * Params : SCpnt - command specifing host to reset + * Returns : FAILED if unable to reset + */ +extern int fas216_eh_host_reset(Scsi_Cmnd *SCpnt); + +#endif /* FAS216_H */ diff --git a/drivers/scsi/arm/msgqueue.c b/drivers/scsi/arm/msgqueue.c new file mode 100644 index 000000000000..7c95c7582b29 --- /dev/null +++ b/drivers/scsi/arm/msgqueue.c @@ -0,0 +1,171 @@ +/* + * linux/drivers/acorn/scsi/msgqueue.c + * + * Copyright (C) 1997-1998 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * message queue handling + */ +#include +#include +#include +#include + +#include "msgqueue.h" + +/* + * Function: struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) + * Purpose : Allocate a message queue entry + * Params : msgq - message queue to claim entry for + * Returns : message queue entry or NULL. + */ +static struct msgqueue_entry *mqe_alloc(MsgQueue_t *msgq) +{ + struct msgqueue_entry *mq; + + if ((mq = msgq->free) != NULL) + msgq->free = mq->next; + + return mq; +} + +/* + * Function: void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) + * Purpose : free a message queue entry + * Params : msgq - message queue to free entry from + * mq - message queue entry to free + */ +static void mqe_free(MsgQueue_t *msgq, struct msgqueue_entry *mq) +{ + if (mq) { + mq->next = msgq->free; + msgq->free = mq; + } +} + +/* + * Function: void msgqueue_initialise(MsgQueue_t *msgq) + * Purpose : initialise a message queue + * Params : msgq - queue to initialise + */ +void msgqueue_initialise(MsgQueue_t *msgq) +{ + int i; + + msgq->qe = NULL; + msgq->free = &msgq->entries[0]; + + for (i = 0; i < NR_MESSAGES; i++) + msgq->entries[i].next = &msgq->entries[i + 1]; + + msgq->entries[NR_MESSAGES - 1].next = NULL; +} + + +/* + * Function: void msgqueue_free(MsgQueue_t *msgq) + * Purpose : free a queue + * Params : msgq - queue to free + */ +void msgqueue_free(MsgQueue_t *msgq) +{ +} + +/* + * Function: int msgqueue_msglength(MsgQueue_t *msgq) + * Purpose : calculate the total length of all messages on the message queue + * Params : msgq - queue to examine + * Returns : number of bytes of messages in queue + */ +int msgqueue_msglength(MsgQueue_t *msgq) +{ + struct msgqueue_entry *mq = msgq->qe; + int length = 0; + + for (mq = msgq->qe; mq; mq = mq->next) + length += mq->msg.length; + + return length; +} + +/* + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) + * Purpose : return a message + * Params : msgq - queue to obtain message from + * : msgno - message number + * Returns : pointer to message string, or NULL + */ +struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) +{ + struct msgqueue_entry *mq; + + for (mq = msgq->qe; mq && msgno; mq = mq->next, msgno--); + + return mq ? &mq->msg : NULL; +} + +/* + * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) + * Purpose : add a message onto a message queue + * Params : msgq - queue to add message on + * length - length of message + * ... - message bytes + * Returns : != 0 if successful + */ +int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) +{ + struct msgqueue_entry *mq = mqe_alloc(msgq); + va_list ap; + + if (mq) { + struct msgqueue_entry **mqp; + int i; + + va_start(ap, length); + for (i = 0; i < length; i++) + mq->msg.msg[i] = va_arg(ap, unsigned int); + va_end(ap); + + mq->msg.length = length; + mq->msg.fifo = 0; + mq->next = NULL; + + mqp = &msgq->qe; + while (*mqp) + mqp = &(*mqp)->next; + + *mqp = mq; + } + + return mq != NULL; +} + +/* + * Function: void msgqueue_flush(MsgQueue_t *msgq) + * Purpose : flush all messages from message queue + * Params : msgq - queue to flush + */ +void msgqueue_flush(MsgQueue_t *msgq) +{ + struct msgqueue_entry *mq, *mqnext; + + for (mq = msgq->qe; mq; mq = mqnext) { + mqnext = mq->next; + mqe_free(msgq, mq); + } + msgq->qe = NULL; +} + +EXPORT_SYMBOL(msgqueue_initialise); +EXPORT_SYMBOL(msgqueue_free); +EXPORT_SYMBOL(msgqueue_msglength); +EXPORT_SYMBOL(msgqueue_getmsg); +EXPORT_SYMBOL(msgqueue_addmsg); +EXPORT_SYMBOL(msgqueue_flush); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SCSI message queue handling"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/msgqueue.h b/drivers/scsi/arm/msgqueue.h new file mode 100644 index 000000000000..41c7333df3e3 --- /dev/null +++ b/drivers/scsi/arm/msgqueue.h @@ -0,0 +1,82 @@ +/* + * linux/drivers/acorn/scsi/msgqueue.h + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * message queue handling + */ +#ifndef MSGQUEUE_H +#define MSGQUEUE_H + +struct message { + char msg[8]; + int length; + int fifo; +}; + +struct msgqueue_entry { + struct message msg; + struct msgqueue_entry *next; +}; + +#define NR_MESSAGES 4 + +typedef struct { + struct msgqueue_entry *qe; + struct msgqueue_entry *free; + struct msgqueue_entry entries[NR_MESSAGES]; +} MsgQueue_t; + +/* + * Function: void msgqueue_initialise(MsgQueue_t *msgq) + * Purpose : initialise a message queue + * Params : msgq - queue to initialise + */ +extern void msgqueue_initialise(MsgQueue_t *msgq); + +/* + * Function: void msgqueue_free(MsgQueue_t *msgq) + * Purpose : free a queue + * Params : msgq - queue to free + */ +extern void msgqueue_free(MsgQueue_t *msgq); + +/* + * Function: int msgqueue_msglength(MsgQueue_t *msgq) + * Purpose : calculate the total length of all messages on the message queue + * Params : msgq - queue to examine + * Returns : number of bytes of messages in queue + */ +extern int msgqueue_msglength(MsgQueue_t *msgq); + +/* + * Function: struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno) + * Purpose : return a message & its length + * Params : msgq - queue to obtain message from + * : msgno - message number + * Returns : pointer to message string, or NULL + */ +extern struct message *msgqueue_getmsg(MsgQueue_t *msgq, int msgno); + +/* + * Function: int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...) + * Purpose : add a message onto a message queue + * Params : msgq - queue to add message on + * length - length of message + * ... - message bytes + * Returns : != 0 if successful + */ +extern int msgqueue_addmsg(MsgQueue_t *msgq, int length, ...); + +/* + * Function: void msgqueue_flush(MsgQueue_t *msgq) + * Purpose : flush all messages from message queue + * Params : msgq - queue to flush + */ +extern void msgqueue_flush(MsgQueue_t *msgq); + +#endif diff --git a/drivers/scsi/arm/oak.c b/drivers/scsi/arm/oak.c new file mode 100644 index 000000000000..f99eab756b74 --- /dev/null +++ b/drivers/scsi/arm/oak.c @@ -0,0 +1,215 @@ +/* + * Oak Generic NCR5380 driver + * + * Copyright 1995-2002, Russell King + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" + +#define AUTOSENSE +/*#define PSEUDO_DMA*/ + +#define OAKSCSI_PUBLIC_RELEASE 1 + +#define NCR5380_read(reg) oakscsi_read(_instance, reg) +#define NCR5380_write(reg, value) oakscsi_write(_instance, reg, value) +#define NCR5380_intr oakscsi_intr +#define NCR5380_queue_command oakscsi_queue_command +#define NCR5380_proc_info oakscsi_proc_info + +int NCR5380_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int inout); + +#define NCR5380_implementation_fields int port, ctrl +#define NCR5380_local_declare() struct Scsi_Host *_instance +#define NCR5380_setup(instance) _instance = instance + +#define BOARD_NORMAL 0 +#define BOARD_NCR53C400 1 + +#include "../NCR5380.h" + +#undef START_DMA_INITIATOR_RECEIVE_REG +#define START_DMA_INITIATOR_RECEIVE_REG (7 + 128) + +const char * oakscsi_info (struct Scsi_Host *spnt) +{ + return ""; +} + +#define STAT(p) inw(p + 144) +extern void inswb(int from, void *to, int len); + +static inline int NCR5380_pwrite(struct Scsi_Host *instance, unsigned char *addr, + int len) +{ + int iobase = instance->io_port; +printk("writing %p len %d\n",addr, len); + if(!len) return -1; + + while(1) + { + int status; + while(((status = STAT(iobase)) & 0x100)==0); + } +} + +static inline int NCR5380_pread(struct Scsi_Host *instance, unsigned char *addr, + int len) +{ + int iobase = instance->io_port; +printk("reading %p len %d\n", addr, len); + while(len > 0) + { + int status, timeout; + unsigned long b; + + timeout = 0x01FFFFFF; + + while(((status = STAT(iobase)) & 0x100)==0) + { + timeout--; + if(status & 0x200 || !timeout) + { + printk("status = %08X\n",status); + return 1; + } + } + if(len >= 128) + { + inswb(iobase + 136, addr, 128); + addr += 128; + len -= 128; + } + else + { + b = (unsigned long) inw(iobase + 136); + *addr ++ = b; + len -= 1; + if(len) + *addr ++ = b>>8; + len -= 1; + } + } + return 0; +} + +#define oakscsi_read(instance,reg) (inb((instance)->io_port + (reg))) +#define oakscsi_write(instance,reg,val) (outb((val), (instance)->io_port + (reg))) + +#undef STAT + +#include "../NCR5380.c" + +static Scsi_Host_Template oakscsi_template = { + .module = THIS_MODULE, + .proc_info = oakscsi_proc_info, + .name = "Oak 16-bit SCSI", + .info = oakscsi_info, + .queuecommand = oakscsi_queue_command, + .eh_abort_handler = NCR5380_abort, + .eh_device_reset_handler= NCR5380_device_reset, + .eh_bus_reset_handler = NCR5380_bus_reset, + .eh_host_reset_handler = NCR5380_host_reset, + .can_queue = 16, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .use_clustering = DISABLE_CLUSTERING, + .proc_name = "oakscsi", +}; + +static int __devinit +oakscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + int ret = -ENOMEM; + + host = scsi_register(&oakscsi_template, sizeof(struct NCR5380_hostdata)); + if (!host) + goto out; + + host->io_port = ecard_address(ec, ECARD_MEMC, 0); + host->irq = IRQ_NONE; + host->n_io_port = 255; + + ret = -EBUSY; + if (!request_region (host->io_port, host->n_io_port, "Oak SCSI")) + goto unreg; + + NCR5380_init(host, 0); + + printk("scsi%d: at port 0x%08lx irqs disabled", + host->host_no, host->io_port); + printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d", + host->can_queue, host->cmd_per_lun, OAKSCSI_PUBLIC_RELEASE); + printk("\nscsi%d:", host->host_no); + NCR5380_print_options(host); + printk("\n"); + + ret = scsi_add_host(host, &ec->dev); + if (ret == 0) + goto out; + + release_region(host->io_port, host->n_io_port); + unreg: + scsi_unregister(host); + out: + return ret; +} + +static void __devexit oakscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + scsi_remove_host(host); + + release_region(host->io_port, host->n_io_port); + scsi_unregister(host); +} + +static const struct ecard_id oakscsi_cids[] = { + { MANU_OAK, PROD_OAK_SCSI }, + { 0xffff, 0xffff } +}; + +static struct ecard_driver oakscsi_driver = { + .probe = oakscsi_probe, + .remove = __devexit_p(oakscsi_remove), + .id_table = oakscsi_cids, + .drv = { + .name = "oakscsi", + }, +}; + +static int __init oakscsi_init(void) +{ + return ecard_register_driver(&oakscsi_driver); +} + +static void __exit oakscsi_exit(void) +{ + ecard_remove_driver(&oakscsi_driver); +} + +module_init(oakscsi_init); +module_exit(oakscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Oak SCSI driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/scsi/arm/powertec.c b/drivers/scsi/arm/powertec.c new file mode 100644 index 000000000000..79fca5999b01 --- /dev/null +++ b/drivers/scsi/arm/powertec.c @@ -0,0 +1,486 @@ +/* + * linux/drivers/acorn/scsi/powertec.c + * + * Copyright (C) 1997-2003 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../scsi.h" +#include "../hosts.h" +#include "fas216.h" +#include "scsi.h" + +#include + +#define POWERTEC_FAS216_OFFSET 0x3000 +#define POWERTEC_FAS216_SHIFT 6 + +#define POWERTEC_INTR_STATUS 0x2000 +#define POWERTEC_INTR_BIT 0x80 + +#define POWERTEC_RESET_CONTROL 0x1018 +#define POWERTEC_RESET_BIT 1 + +#define POWERTEC_TERM_CONTROL 0x2018 +#define POWERTEC_TERM_ENABLE 1 + +#define POWERTEC_INTR_CONTROL 0x101c +#define POWERTEC_INTR_ENABLE 1 +#define POWERTEC_INTR_DISABLE 0 + +#define VERSION "1.10 (19/01/2003 2.5.59)" + +/* + * Use term=0,1,0,0,0 to turn terminators on/off. + * One entry per slot. + */ +static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; + +#define NR_SG 256 + +struct powertec_info { + FAS216_Info info; + struct expansion_card *ec; + void *term_port; + unsigned int term_ctl; + struct scatterlist sg[NR_SG]; +}; + +/* Prototype: void powertecscsi_irqenable(ec, irqnr) + * Purpose : Enable interrupts on Powertec SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +powertecscsi_irqenable(struct expansion_card *ec, int irqnr) +{ + writeb(POWERTEC_INTR_ENABLE, ec->irq_data); +} + +/* Prototype: void powertecscsi_irqdisable(ec, irqnr) + * Purpose : Disable interrupts on Powertec SCSI card + * Params : ec - expansion card structure + * : irqnr - interrupt number + */ +static void +powertecscsi_irqdisable(struct expansion_card *ec, int irqnr) +{ + writeb(POWERTEC_INTR_DISABLE, ec->irq_data); +} + +static const expansioncard_ops_t powertecscsi_ops = { + .irqenable = powertecscsi_irqenable, + .irqdisable = powertecscsi_irqdisable, +}; + +/* Prototype: void powertecscsi_terminator_ctl(host, on_off) + * Purpose : Turn the Powertec SCSI terminators on or off + * Params : host - card to turn on/off + * : on_off - !0 to turn on, 0 to turn off + */ +static void +powertecscsi_terminator_ctl(struct Scsi_Host *host, int on_off) +{ + struct powertec_info *info = (struct powertec_info *)host->hostdata; + + info->term_ctl = on_off ? POWERTEC_TERM_ENABLE : 0; + writeb(info->term_ctl, info->term_port); +} + +/* Prototype: void powertecscsi_intr(irq, *dev_id, *regs) + * Purpose : handle interrupts from Powertec SCSI card + * Params : irq - interrupt number + * dev_id - user-defined (Scsi_Host structure) + * regs - processor registers at interrupt + */ +static irqreturn_t +powertecscsi_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct powertec_info *info = dev_id; + + return fas216_intr(&info->info); +} + +/* Prototype: fasdmatype_t powertecscsi_dma_setup(host, SCpnt, direction, min_type) + * Purpose : initialises DMA/PIO + * Params : host - host + * SCpnt - command + * direction - DMA on to/off of card + * min_type - minimum DMA support that we must have for this transfer + * Returns : type of transfer to be performed + */ +static fasdmatype_t +powertecscsi_dma_setup(struct Scsi_Host *host, Scsi_Pointer *SCp, + fasdmadir_t direction, fasdmatype_t min_type) +{ + struct powertec_info *info = (struct powertec_info *)host->hostdata; + struct device *dev = scsi_get_device(host); + int dmach = host->dma_channel; + + if (info->info.ifcfg.capabilities & FASCAP_DMA && + min_type == fasdma_real_all) { + int bufs, map_dir, dma_dir; + + bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); + + if (direction == DMA_OUT) + map_dir = DMA_TO_DEVICE, + dma_dir = DMA_MODE_WRITE; + else + map_dir = DMA_FROM_DEVICE, + dma_dir = DMA_MODE_READ; + + dma_map_sg(dev, info->sg, bufs + 1, map_dir); + + disable_dma(dmach); + set_dma_sg(dmach, info->sg, bufs + 1); + set_dma_mode(dmach, dma_dir); + enable_dma(dmach); + return fasdma_real_all; + } + + /* + * If we're not doing DMA, + * we'll do slow PIO + */ + return fasdma_pio; +} + +/* Prototype: int powertecscsi_dma_stop(host, SCpnt) + * Purpose : stops DMA/PIO + * Params : host - host + * SCpnt - command + */ +static void +powertecscsi_dma_stop(struct Scsi_Host *host, Scsi_Pointer *SCp) +{ + if (host->dma_channel != NO_DMA) + disable_dma(host->dma_channel); +} + +/* Prototype: const char *powertecscsi_info(struct Scsi_Host * host) + * Purpose : returns a descriptive string about this interface, + * Params : host - driver host structure to return info for. + * Returns : pointer to a static buffer containing null terminated string. + */ +const char *powertecscsi_info(struct Scsi_Host *host) +{ + struct powertec_info *info = (struct powertec_info *)host->hostdata; + static char string[150]; + + sprintf(string, "%s (%s) in slot %d v%s terminators o%s", + host->hostt->name, info->info.scsi.type, info->ec->slot_no, + VERSION, info->term_ctl ? "n" : "ff"); + + return string; +} + +/* Prototype: int powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) + * Purpose : Set a driver specific function + * Params : host - host to setup + * : buffer - buffer containing string describing operation + * : length - length of string + * Returns : -EINVAL, or 0 + */ +static int +powertecscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) +{ + int ret = length; + + if (length >= 12 && strncmp(buffer, "POWERTECSCSI", 12) == 0) { + buffer += 12; + length -= 12; + + if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { + if (buffer[5] == '1') + powertecscsi_terminator_ctl(host, 1); + else if (buffer[5] == '0') + powertecscsi_terminator_ctl(host, 0); + else + ret = -EINVAL; + } else + ret = -EINVAL; + } else + ret = -EINVAL; + + return ret; +} + +/* Prototype: int powertecscsi_proc_info(char *buffer, char **start, off_t offset, + * int length, int host_no, int inout) + * Purpose : Return information about the driver to a user process accessing + * the /proc filesystem. + * Params : buffer - a buffer to write information to + * start - a pointer into this buffer set by this routine to the start + * of the required information. + * offset - offset into information that we have read upto. + * length - length of buffer + * host_no - host number to return information for + * inout - 0 for reading, 1 for writing. + * Returns : length of data written to buffer. + */ +int powertecscsi_proc_info(char *buffer, char **start, off_t offset, + int length, int host_no, int inout) +{ + struct Scsi_Host *host; + struct powertec_info *info; + char *p = buffer; + int pos; + + host = scsi_host_hn_get(host_no); + if (!host) + return 0; + + if (inout == 1) + return powertecscsi_set_proc_info(host, buffer, length); + + info = (struct powertec_info *)host->hostdata; + + p += sprintf(p, "PowerTec SCSI driver v%s\n", VERSION); + p += fas216_print_host(&info->info, p); + p += sprintf(p, "Term : o%s\n", + info->term_ctl ? "n" : "ff"); + + p += fas216_print_stats(&info->info, p); + p += fas216_print_devices(&info->info, p); + + *start = buffer + offset; + pos = p - buffer - offset; + if (pos > length) + pos = length; + + return pos; +} + +static ssize_t powertecscsi_show_term(struct device *dev, char *buf) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct powertec_info *info = (struct powertec_info *)host->hostdata; + + return sprintf(buf, "%d\n", info->term_ctl ? 1 : 0); +} + +static ssize_t +powertecscsi_store_term(struct device *dev, const char *buf, size_t len) +{ + struct expansion_card *ec = ECARD_DEV(dev); + struct Scsi_Host *host = ecard_get_drvdata(ec); + + if (len > 1) + powertecscsi_terminator_ctl(host, buf[0] != '0'); + + return len; +} + +static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, + powertecscsi_show_term, powertecscsi_store_term); + +static Scsi_Host_Template powertecscsi_template = { + .module = THIS_MODULE, + .proc_info = powertecscsi_proc_info, + .name = "PowerTec SCSI", + .info = powertecscsi_info, + .command = fas216_command, + .queuecommand = fas216_queue_command, + .eh_host_reset_handler = fas216_eh_host_reset, + .eh_bus_reset_handler = fas216_eh_bus_reset, + .eh_device_reset_handler = fas216_eh_device_reset, + .eh_abort_handler = fas216_eh_abort, + + .can_queue = 8, + .this_id = 7, + .sg_tablesize = SG_ALL, + .cmd_per_lun = 2, + .use_clustering = ENABLE_CLUSTERING, + .proc_name = "powertec", +}; + +static int __devinit +powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + struct Scsi_Host *host; + struct powertec_info *info; + unsigned long resbase, reslen; + unsigned char *base; + int ret; + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + if (!request_mem_region(resbase, reslen, "powertecscsi")) { + ret = -EBUSY; + goto out; + } + + base = ioremap(resbase, reslen); + if (!base) { + ret = -ENOMEM; + goto out_region; + } + + host = scsi_register(&powertecscsi_template, + sizeof (struct powertec_info)); + if (!host) { + ret = -ENOMEM; + goto out_unmap; + } + + host->base = (unsigned long)base; + host->irq = ec->irq; + host->dma_channel = ec->dma; + + ec->irqaddr = base + POWERTEC_INTR_STATUS; + ec->irqmask = POWERTEC_INTR_BIT; + ec->irq_data = base + POWERTEC_INTR_CONTROL; + ec->ops = &powertecscsi_ops; + + ecard_set_drvdata(ec, host); + + info = (struct powertec_info *)host->hostdata; + info->term_port = base + POWERTEC_TERM_CONTROL; + powertecscsi_terminator_ctl(host, term[ec->slot_no]); + + device_create_file(&ec->dev, &dev_attr_bus_term); + + info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET; + info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT; + info->info.scsi.irq = host->irq; + info->info.ifcfg.clockrate = 40; /* MHz */ + info->info.ifcfg.select_timeout = 255; + info->info.ifcfg.asyncperiod = 200; /* ns */ + info->info.ifcfg.sync_max_depth = 7; + info->info.ifcfg.cntl3 = CNTL3_BS8 | CNTL3_FASTSCSI | CNTL3_FASTCLK; + info->info.ifcfg.disconnect_ok = 1; + info->info.ifcfg.wide_max_size = 0; + info->info.ifcfg.capabilities = 0; + info->info.dma.setup = powertecscsi_dma_setup; + info->info.dma.pseudo = NULL; + info->info.dma.stop = powertecscsi_dma_stop; + + ret = fas216_init(host); + if (ret) + goto out_free; + + ret = request_irq(host->irq, powertecscsi_intr, + SA_INTERRUPT, "powertec", info); + if (ret) { + printk("scsi%d: IRQ%d not free: %d\n", + host->host_no, host->irq, ret); + goto out_release; + } + + if (host->dma_channel != NO_DMA) { + if (request_dma(host->dma_channel, "powertec")) { + printk("scsi%d: DMA%d not free, using PIO\n", + host->host_no, host->dma_channel); + host->dma_channel = NO_DMA; + } else { + set_dma_speed(host->dma_channel, 180); + info->info.ifcfg.capabilities |= FASCAP_DMA; + } + } + + ret = fas216_add(host, &ec->dev); + if (ret == 0) + goto out; + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, host); + + out_release: + fas216_release(host); + + out_free: + device_remove_file(&ec->dev, &dev_attr_bus_term); + scsi_unregister(host); + + out_unmap: + iounmap(base); + + out_region: + release_mem_region(resbase, reslen); + + out: + return ret; +} + +static void __devexit powertecscsi_remove(struct expansion_card *ec) +{ + struct Scsi_Host *host = ecard_get_drvdata(ec); + struct powertecscsi_info *info = (struct powertecscsi_info *)host->hostdata; + unsigned long resbase, reslen; + + ecard_set_drvdata(ec, NULL); + fas216_remove(host); + + device_remove_file(&ec->dev, &dev_attr_bus_term); + + if (host->dma_channel != NO_DMA) + free_dma(host->dma_channel); + free_irq(host->irq, info); + + iounmap((void *)host->base); + + resbase = ecard_resource_start(ec, ECARD_RES_IOCFAST); + reslen = ecard_resource_len(ec, ECARD_RES_IOCFAST); + + release_mem_region(resbase, reslen); + + fas216_release(host); + scsi_unregister(host); +} + +static const struct ecard_id powertecscsi_cids[] = { + { MANU_ALSYSTEMS, PROD_ALSYS_SCSIATAPI }, + { 0xffff, 0xffff }, +}; + +static struct ecard_driver powertecscsi_driver = { + .probe = powertecscsi_probe, + .remove = __devexit_p(powertecscsi_remove), + .id_table = powertecscsi_cids, + .drv = { + .name = "powertecscsi", + }, +}; + +static int __init powertecscsi_init(void) +{ + return ecard_register_driver(&powertecscsi_driver); +} + +static void __exit powertecscsi_exit(void) +{ + ecard_remove_driver(&powertecscsi_driver); +} + +module_init(powertecscsi_init); +module_exit(powertecscsi_exit); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("Powertec SCSI driver"); +MODULE_PARM(term, "1-8i"); +MODULE_PARM_DESC(term, "SCSI bus termination"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/queue.c b/drivers/scsi/arm/queue.c new file mode 100644 index 000000000000..fec52a7ae746 --- /dev/null +++ b/drivers/scsi/arm/queue.c @@ -0,0 +1,319 @@ +/* + * linux/drivers/acorn/scsi/queue.c: queue handling primitives + * + * Copyright (C) 1997-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Changelog: + * 15-Sep-1997 RMK Created. + * 11-Oct-1997 RMK Corrected problem with queue_remove_exclude + * not updating internal linked list properly + * (was causing commands to go missing). + * 30-Aug-2000 RMK Use Linux list handling and spinlocks + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../scsi.h" + +#define DEBUG + +typedef struct queue_entry { + struct list_head list; + Scsi_Cmnd *SCpnt; +#ifdef DEBUG + unsigned long magic; +#endif +} QE_t; + +#ifdef DEBUG +#define QUEUE_MAGIC_FREE 0xf7e1c9a3 +#define QUEUE_MAGIC_USED 0xf7e1cc33 + +#define SET_MAGIC(q,m) ((q)->magic = (m)) +#define BAD_MAGIC(q,m) ((q)->magic != (m)) +#else +#define SET_MAGIC(q,m) do { } while (0) +#define BAD_MAGIC(q,m) (0) +#endif + +#include "queue.h" + +#define NR_QE 32 + +/* + * Function: void queue_initialise (Queue_t *queue) + * Purpose : initialise a queue + * Params : queue - queue to initialise + */ +int queue_initialise (Queue_t *queue) +{ + unsigned int nqueues = NR_QE; + QE_t *q; + + spin_lock_init(&queue->queue_lock); + INIT_LIST_HEAD(&queue->head); + INIT_LIST_HEAD(&queue->free); + + /* + * If life was easier, then SCpnt would have a + * host-available list head, and we wouldn't + * need to keep free lists or allocate this + * memory. + */ + queue->alloc = q = kmalloc(sizeof(QE_t) * nqueues, GFP_KERNEL); + if (q) { + for (; nqueues; q++, nqueues--) { + SET_MAGIC(q, QUEUE_MAGIC_FREE); + q->SCpnt = NULL; + list_add(&q->list, &queue->free); + } + } + + return queue->alloc != NULL; +} + +/* + * Function: void queue_free (Queue_t *queue) + * Purpose : free a queue + * Params : queue - queue to free + */ +void queue_free (Queue_t *queue) +{ + if (!list_empty(&queue->head)) + printk(KERN_WARNING "freeing non-empty queue %p\n", queue); + if (queue->alloc) + kfree(queue->alloc); +} + + +/* + * Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) + * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head. + * Params : queue - destination queue + * SCpnt - command to add + * head - add command to head of queue + * Returns : 0 on error, !0 on success + */ +int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) +{ + unsigned long flags; + struct list_head *l; + QE_t *q; + int ret = 0; + + spin_lock_irqsave(&queue->queue_lock, flags); + if (list_empty(&queue->free)) + goto empty; + + l = queue->free.next; + list_del(l); + + q = list_entry(l, QE_t, list); + if (BAD_MAGIC(q, QUEUE_MAGIC_FREE)) + BUG(); + + SET_MAGIC(q, QUEUE_MAGIC_USED); + q->SCpnt = SCpnt; + + if (head) + list_add(l, &queue->head); + else + list_add_tail(l, &queue->head); + + ret = 1; +empty: + spin_unlock_irqrestore(&queue->queue_lock, flags); + return ret; +} + +static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) +{ + QE_t *q; + + /* + * Move the entry from the "used" list onto the "free" list + */ + list_del(ent); + q = list_entry(ent, QE_t, list); + if (BAD_MAGIC(q, QUEUE_MAGIC_USED)) + BUG(); + + SET_MAGIC(q, QUEUE_MAGIC_FREE); + list_add(ent, &queue->free); + + return q->SCpnt; +} + +/* + * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude) + * Purpose : remove a SCSI command from a queue + * Params : queue - queue to remove command from + * exclude - bit array of target&lun which is busy + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) +{ + unsigned long flags; + struct list_head *l; + Scsi_Cmnd *SCpnt = NULL; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (!test_bit(q->SCpnt->device->id * 8 + q->SCpnt->device->lun, exclude)) { + SCpnt = __queue_remove(queue, l); + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return SCpnt; +} + +/* + * Function: Scsi_Cmnd *queue_remove (queue) + * Purpose : removes first SCSI command from a queue + * Params : queue - queue to remove command from + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +Scsi_Cmnd *queue_remove(Queue_t *queue) +{ + unsigned long flags; + Scsi_Cmnd *SCpnt = NULL; + + spin_lock_irqsave(&queue->queue_lock, flags); + if (!list_empty(&queue->head)) + SCpnt = __queue_remove(queue, queue->head.next); + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return SCpnt; +} + +/* + * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) + * Purpose : remove a SCSI command from the queue for a specified target/lun/tag + * Params : queue - queue to remove command from + * target - target that we want + * lun - lun on device + * tag - tag on device + * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements + */ +Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag) +{ + unsigned long flags; + struct list_head *l; + Scsi_Cmnd *SCpnt = NULL; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun && + q->SCpnt->tag == tag) { + SCpnt = __queue_remove(queue, l); + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return SCpnt; +} + +/* + * Function: queue_remove_all_target(queue, target) + * Purpose : remove all SCSI commands from the queue for a specified target + * Params : queue - queue to remove command from + * target - target device id + * Returns : nothing + */ +void queue_remove_all_target(Queue_t *queue, int target) +{ + unsigned long flags; + struct list_head *l; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt->device->id == target) + __queue_remove(queue, l); + } + spin_unlock_irqrestore(&queue->queue_lock, flags); +} + +/* + * Function: int queue_probetgtlun (queue, target, lun) + * Purpose : check to see if we have a command in the queue for the specified + * target/lun. + * Params : queue - queue to look in + * target - target we want to probe + * lun - lun on target + * Returns : 0 if not found, != 0 if found + */ +int queue_probetgtlun (Queue_t *queue, int target, int lun) +{ + unsigned long flags; + struct list_head *l; + int found = 0; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt->device->id == target && q->SCpnt->device->lun == lun) { + found = 1; + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return found; +} + +/* + * Function: int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) + * Purpose : remove a specific command from the queues + * Params : queue - queue to look in + * SCpnt - command to find + * Returns : 0 if not found + */ +int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) +{ + unsigned long flags; + struct list_head *l; + int found = 0; + + spin_lock_irqsave(&queue->queue_lock, flags); + list_for_each(l, &queue->head) { + QE_t *q = list_entry(l, QE_t, list); + if (q->SCpnt == SCpnt) { + __queue_remove(queue, l); + found = 1; + break; + } + } + spin_unlock_irqrestore(&queue->queue_lock, flags); + + return found; +} + +EXPORT_SYMBOL(queue_initialise); +EXPORT_SYMBOL(queue_free); +EXPORT_SYMBOL(__queue_add); +EXPORT_SYMBOL(queue_remove); +EXPORT_SYMBOL(queue_remove_exclude); +EXPORT_SYMBOL(queue_remove_tgtluntag); +EXPORT_SYMBOL(queue_remove_cmd); +EXPORT_SYMBOL(queue_remove_all_target); +EXPORT_SYMBOL(queue_probetgtlun); + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("SCSI command queueing"); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/arm/queue.h b/drivers/scsi/arm/queue.h new file mode 100644 index 000000000000..0c9dec4c1716 --- /dev/null +++ b/drivers/scsi/arm/queue.h @@ -0,0 +1,105 @@ +/* + * linux/drivers/acorn/scsi/queue.h: queue handling + * + * Copyright (C) 1997 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef QUEUE_H +#define QUEUE_H + +typedef struct { + struct list_head head; + struct list_head free; + spinlock_t queue_lock; + void *alloc; /* start of allocated mem */ +} Queue_t; + +/* + * Function: void queue_initialise (Queue_t *queue) + * Purpose : initialise a queue + * Params : queue - queue to initialise + */ +extern int queue_initialise (Queue_t *queue); + +/* + * Function: void queue_free (Queue_t *queue) + * Purpose : free a queue + * Params : queue - queue to free + */ +extern void queue_free (Queue_t *queue); + +/* + * Function: Scsi_Cmnd *queue_remove (queue) + * Purpose : removes first SCSI command from a queue + * Params : queue - queue to remove command from + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +extern Scsi_Cmnd *queue_remove (Queue_t *queue); + +/* + * Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude) + * Purpose : remove a SCSI command from a queue + * Params : queue - queue to remove command from + * exclude - array of busy LUNs + * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + */ +extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned long *exclude); + +#define queue_add_cmd_ordered(queue,SCpnt) \ + __queue_add(queue,SCpnt,(SCpnt)->cmnd[0] == REQUEST_SENSE) +#define queue_add_cmd_tail(queue,SCpnt) \ + __queue_add(queue,SCpnt,0) +/* + * Function: int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) + * Purpose : Add a new command onto a queue + * Params : queue - destination queue + * SCpnt - command to add + * head - add command to head of queue + * Returns : 0 on error, !0 on success + */ +extern int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head); + +/* + * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) + * Purpose : remove a SCSI command from the queue for a specified target/lun/tag + * Params : queue - queue to remove command from + * target - target that we want + * lun - lun on device + * tag - tag on device + * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements + */ +extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag); + +/* + * Function: queue_remove_all_target(queue, target) + * Purpose : remove all SCSI commands from the queue for a specified target + * Params : queue - queue to remove command from + * target - target device id + * Returns : nothing + */ +extern void queue_remove_all_target(Queue_t *queue, int target); + +/* + * Function: int queue_probetgtlun (queue, target, lun) + * Purpose : check to see if we have a command in the queue for the specified + * target/lun. + * Params : queue - queue to look in + * target - target we want to probe + * lun - lun on target + * Returns : 0 if not found, != 0 if found + */ +extern int queue_probetgtlun (Queue_t *queue, int target, int lun); + +/* + * Function: int queue_remove_cmd (Queue_t *queue, Scsi_Cmnd *SCpnt) + * Purpose : remove a specific command from the queues + * Params : queue - queue to look in + * SCpnt - command to find + * Returns : 0 if not found + */ +int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt); + +#endif /* QUEUE_H */ diff --git a/drivers/scsi/arm/scsi.h b/drivers/scsi/arm/scsi.h new file mode 100644 index 000000000000..3b82817d88f4 --- /dev/null +++ b/drivers/scsi/arm/scsi.h @@ -0,0 +1,115 @@ +/* + * linux/drivers/acorn/scsi/scsi.h + * + * Copyright (C) 2002 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Commonly used scsi driver functions. + */ + +#define BELT_AND_BRACES + +/* + * The scatter-gather list handling. This contains all + * the yucky stuff that needs to be fixed properly. + */ +static inline int copy_SCp_to_sg(struct scatterlist *sg, Scsi_Pointer *SCp, int max) +{ + int bufs = SCp->buffers_residual; + + BUG_ON(bufs + 1 > max); + + sg->page = virt_to_page(SCp->ptr); + sg->offset = ((unsigned int)SCp->ptr) & ~PAGE_MASK; + sg->length = SCp->this_residual; + + if (bufs) + memcpy(sg + 1, SCp->buffer + 1, + sizeof(struct scatterlist) * bufs); + return bufs + 1; +} + +static inline int next_SCp(Scsi_Pointer *SCp) +{ + int ret = SCp->buffers_residual; + if (ret) { + SCp->buffer++; + SCp->buffers_residual--; + SCp->ptr = (char *) + (page_address(SCp->buffer->page) + + SCp->buffer->offset); + SCp->this_residual = SCp->buffer->length; + } else { + SCp->ptr = NULL; + SCp->this_residual = 0; + } + return ret; +} + +static inline unsigned char get_next_SCp_byte(Scsi_Pointer *SCp) +{ + char c = *SCp->ptr; + + SCp->ptr += 1; + SCp->this_residual -= 1; + + return c; +} + +static inline void put_next_SCp_byte(Scsi_Pointer *SCp, unsigned char c) +{ + *SCp->ptr = c; + SCp->ptr += 1; + SCp->this_residual -= 1; +} + +static inline void init_SCp(Scsi_Cmnd *SCpnt) +{ + memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer)); + + if (SCpnt->use_sg) { + unsigned long len = 0; + int buf; + + SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; + SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; + SCpnt->SCp.ptr = (char *) + (page_address(SCpnt->SCp.buffer->page) + + SCpnt->SCp.buffer->offset); + SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + +#ifdef BELT_AND_BRACES + /* + * Calculate correct buffer length. Some commands + * come in with the wrong request_bufflen. + */ + for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++) + len += SCpnt->SCp.buffer[buf].length; + + if (SCpnt->request_bufflen != len) + printk(KERN_WARNING "scsi%d.%c: bad request buffer " + "length %d, should be %ld\n", SCpnt->device->host->host_no, + '0' + SCpnt->device->id, SCpnt->request_bufflen, len); + SCpnt->request_bufflen = len; +#endif + } else { + SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer; + SCpnt->SCp.this_residual = SCpnt->request_bufflen; + } + + /* + * If the upper SCSI layers pass a buffer, but zero length, + * we aren't interested in the buffer pointer. + */ + if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) { +#if 0 //def BELT_AND_BRACES + printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for " + "command ", SCpnt->host->host_no, '0' + SCpnt->target); + print_command(SCpnt->cmnd); +#endif + SCpnt->SCp.ptr = NULL; + } +} -- cgit v1.2.3 From 14a307fd918af3dc9070083863922d266c183008 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 17 May 2003 17:21:47 +0100 Subject: [ARM] Update cyber2000fb.c - Fix build warnings. - Don't modify region in copyarea method. - Remove FBCON_HAS_CFBx preprocessor tests. - Use runtime test for netwinder mclk parameters. --- drivers/video/cyber2000fb.c | 51 ++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 993a69d81ad5..827a1977ef91 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -146,7 +146,7 @@ cyber2000_seqw(unsigned int reg, unsigned int val, struct cfb_info *cfb) * Hardware Cyber2000 Acceleration */ static void -cyber2000fb_fillrect(struct fb_info *info, struct fb_fillrect *rect) +cyber2000fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct cfb_info *cfb = (struct cfb_info *)info; unsigned long dst, col; @@ -178,7 +178,7 @@ cyber2000fb_fillrect(struct fb_info *info, struct fb_fillrect *rect) } static void -cyber2000fb_copyarea(struct fb_info *info, struct fb_copyarea *region) +cyber2000fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) { struct cfb_info *cfb = (struct cfb_info *)info; unsigned int cmd = CO_CMD_L_PATTERN_FGCOL; @@ -189,24 +189,25 @@ cyber2000fb_copyarea(struct fb_info *info, struct fb_copyarea *region) return; } + cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); + cyber2000fb_writew(region->width - 1, CO_REG_PIXWIDTH, cfb); + cyber2000fb_writew(region->height - 1, CO_REG_PIXHEIGHT, cfb); + + src = region->sx + region->sy * cfb->fb.var.xres_virtual; + dst = region->dx + region->dy * cfb->fb.var.xres_virtual; + if (region->sx < region->dx) { - region->sx += region->width - 1; - region->dx += region->width - 1; + src += region->width - 1; + dst += region->width - 1; cmd |= CO_CMD_L_INC_LEFT; } if (region->sy < region->dy) { - region->sy += region->height - 1; - region->dy += region->height - 1; + src += (region->height - 1) * cfb->fb.var.xres_virtual; + dst += (region->height - 1) * cfb->fb.var.xres_virtual; cmd |= CO_CMD_L_INC_UP; } - cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); - cyber2000fb_writew(region->width - 1, CO_REG_PIXWIDTH, cfb); - cyber2000fb_writew(region->height - 1, CO_REG_PIXHEIGHT, cfb); - - src = region->sx + region->sy * cfb->fb.var.xres_virtual; - dst = region->dx + region->dy * cfb->fb.var.xres_virtual; if (cfb->fb.var.bits_per_pixel == 24) { cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb); src *= 3; @@ -221,9 +222,9 @@ cyber2000fb_copyarea(struct fb_info *info, struct fb_copyarea *region) } static void -cyber2000fb_imageblit(struct fb_info *info, struct fb_image *image) +cyber2000fb_imageblit(struct fb_info *info, const struct fb_image *image) { - struct cfb_info *cfb = (struct cfb_info *)info; +// struct cfb_info *cfb = (struct cfb_info *)info; // if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) { cfb_imageblit(info, image); @@ -754,7 +755,6 @@ cyber2000fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->blue.msb_right = 0; switch (var->bits_per_pixel) { -#ifdef FBCON_HAS_CFB8 case 8: /* PSEUDOCOLOUR, 256 */ var->transp.offset = 0; var->transp.length = 0; @@ -765,8 +765,7 @@ cyber2000fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->blue.offset = 0; var->blue.length = 8; break; -#endif -#ifdef FBCON_HAS_CFB16 + case 16:/* DIRECTCOLOUR, 64k or 32k */ switch (var->green.length) { case 6: /* RGB565, 64k */ @@ -804,8 +803,7 @@ cyber2000fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) break; } break; -#endif -#ifdef FBCON_HAS_CFB24 + case 24:/* TRUECOLOUR, 16m */ var->transp.offset = 0; var->transp.length = 0; @@ -816,8 +814,7 @@ cyber2000fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->blue.offset = 0; var->blue.length = 8; break; -#endif -#ifdef FBCON_HAS_CFB32 + case 32:/* TRUECOLOUR, 16m */ var->transp.offset = 24; var->transp.length = 8; @@ -828,7 +825,7 @@ cyber2000fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) var->blue.offset = 0; var->blue.length = 8; break; -#endif + default: return -EINVAL; } @@ -1601,15 +1598,17 @@ cyberpro_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) /* * Use MCLK from BIOS. FIXME: what about hotplug? */ -#ifndef __arm__ cfb->mclk_mult = cyber2000_grphr(EXT_MCLK_MULT, cfb); cfb->mclk_div = cyber2000_grphr(EXT_MCLK_DIV, cfb); -#else + +#ifdef __arm__ /* * MCLK on the NetWinder and the Shark is fixed at 75MHz */ - cfb->mclk_mult = 0xdb; - cfb->mclk_div = 0x54; + if (machine_is_netwinder()) { + cfb->mclk_mult = 0xdb; + cfb->mclk_div = 0x54; + } #endif err = cyberpro_common_probe(cfb); -- cgit v1.2.3 From 86ca095488ac731cfcfd1d17f7f1a2d8d7f58bd5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 17 May 2003 17:30:51 +0100 Subject: [ARM] Fixup yet another missing irqreturn_t --- drivers/char/nwbutton.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/nwbutton.h b/drivers/char/nwbutton.h index 5f5c8ddd041f..a59a045af441 100644 --- a/drivers/char/nwbutton.h +++ b/drivers/char/nwbutton.h @@ -25,7 +25,7 @@ struct button_callback { /* Function prototypes: */ static void button_sequence_finished (unsigned long parameters); -static void button_handler (int irq, void *dev_id, struct pt_regs *regs); +static irqreturn_t button_handler (int irq, void *dev_id, struct pt_regs *regs); static int button_read (struct file *filp, char *buffer, size_t count, loff_t *ppos); int button_init (void); -- cgit v1.2.3 From add26e4a4fee2b85d4c0b851566fac5088611259 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 17 May 2003 17:42:42 +0100 Subject: [ARM] Update Acorn IDE drivers. icside.c: - Use C99 structure initialisers. - Use generic DMA API. - Use new hwif->hwif_data rather than hwif->hw.priv. - Add DMA supported/dma supported masks. - Remember the card type for the remove/shutdown methods. - Add shutdown method. rapide.c - ecard_{claim,release} are no longer required. --- drivers/ide/arm/icside.c | 340 ++++++++++++++++++++++++++--------------------- drivers/ide/arm/rapide.c | 3 - 2 files changed, 189 insertions(+), 154 deletions(-) diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 9f3bb7919dd4..653de7cd0523 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include @@ -27,12 +27,7 @@ #include #include -/* - * Maximum number of interfaces per card - */ -#define MAX_IFS 2 - -#define ICS_IDENT_OFFSET 0x8a0 +#define ICS_IDENT_OFFSET 0x2280 #define ICS_ARCIN_V5_INTRSTAT 0x000 #define ICS_ARCIN_V5_INTROFFSET 0x001 @@ -77,14 +72,19 @@ static struct cardinfo icside_cardinfo_v6_2 = { struct icside_state { unsigned int channel; unsigned int enabled; - unsigned int irq_port; + unsigned long irq_port; + unsigned long slot_port; + unsigned int type; + /* parent device... until the IDE core gets one of its own */ + struct device *dev; + ide_hwif_t *hwif[2]; }; -typedef enum { - ics_if_unknown, - ics_if_arcin_v5, - ics_if_arcin_v6 -} iftype_t; +#define ICS_TYPE_A3IN 0 +#define ICS_TYPE_A3USER 1 +#define ICS_TYPE_V6 3 +#define ICS_TYPE_V5 15 +#define ICS_TYPE_NOTYPE ((unsigned int)-1) /* ---------------- Version 5 PCB Support Functions --------------------- */ /* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) @@ -92,8 +92,10 @@ typedef enum { */ static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) { - unsigned int memc_port = (unsigned int)ec->irq_data; - outb(0, memc_port + ICS_ARCIN_V5_INTROFFSET); + struct icside_state *state = ec->irq_data; + unsigned int base = state->irq_port; + + outb(0, base + ICS_ARCIN_V5_INTROFFSET); } /* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) @@ -101,17 +103,15 @@ static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) */ static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) { - unsigned int memc_port = (unsigned int)ec->irq_data; - inb(memc_port + ICS_ARCIN_V5_INTROFFSET); + struct icside_state *state = ec->irq_data; + unsigned int base = state->irq_port; + + inb(base + ICS_ARCIN_V5_INTROFFSET); } static const expansioncard_ops_t icside_ops_arcin_v5 = { - icside_irqenable_arcin_v5, - icside_irqdisable_arcin_v5, - NULL, - NULL, - NULL, - NULL + .irqenable = icside_irqenable_arcin_v5, + .irqdisable = icside_irqdisable_arcin_v5, }; @@ -163,65 +163,11 @@ static int icside_irqpending_arcin_v6(struct expansion_card *ec) } static const expansioncard_ops_t icside_ops_arcin_v6 = { - icside_irqenable_arcin_v6, - icside_irqdisable_arcin_v6, - icside_irqpending_arcin_v6, - NULL, - NULL, - NULL + .irqenable = icside_irqenable_arcin_v6, + .irqdisable = icside_irqdisable_arcin_v6, + .irqpending = icside_irqpending_arcin_v6, }; -/* Prototype: icside_identifyif (struct expansion_card *ec) - * Purpose : identify IDE interface type - * Notes : checks the description string - */ -static iftype_t __init icside_identifyif (struct expansion_card *ec) -{ - unsigned int addr; - iftype_t iftype; - int id = 0; - - iftype = ics_if_unknown; - - addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; - - id = inb(addr) & 1; - id |= (inb(addr + 1) & 1) << 1; - id |= (inb(addr + 2) & 1) << 2; - id |= (inb(addr + 3) & 1) << 3; - - switch (id) { - case 0: /* A3IN */ - printk("icside: A3IN unsupported\n"); - break; - - case 1: /* A3USER */ - printk("icside: A3USER unsupported\n"); - break; - - case 3: /* ARCIN V6 */ - printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v6; - break; - - case 15:/* ARCIN V5 (no id) */ - printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no); - iftype = ics_if_arcin_v5; - break; - - default:/* we don't know - complain very loudly */ - printk("icside: ***********************************\n"); - printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); - printk("icside: ***********************************\n"); - printk("icside: please report this to linux@arm.linux.org.uk\n"); - printk("icside: defaulting to ARCIN V5\n"); - iftype = ics_if_arcin_v5; - break; - } - - return iftype; -} - /* * Handle routing of interrupts. This is called before * we write the command to the drive. @@ -229,7 +175,7 @@ static iftype_t __init icside_identifyif (struct expansion_card *ec) static void icside_maskproc(ide_drive_t *drive, int mask) { ide_hwif_t *hwif = HWIF(drive); - struct icside_state *state = hwif->hw.priv; + struct icside_state *state = hwif->hwif_data; unsigned long flags; local_irq_save(flags); @@ -271,6 +217,7 @@ static void icside_maskproc(ide_drive_t *drive, int mask) static void ide_build_sglist(ide_drive_t *drive, struct request *rq) { ide_hwif_t *hwif = drive->hwif; + struct icside_state *state = hwif->hwif_data; struct scatterlist *sg = hwif->sg_table; int nents; @@ -280,9 +227,9 @@ static void ide_build_sglist(ide_drive_t *drive, struct request *rq) ide_task_t *args = rq->special; if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) - hwif->sg_dma_direction = PCI_DMA_TODEVICE; + hwif->sg_dma_direction = DMA_TO_DEVICE; else - hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + hwif->sg_dma_direction = DMA_FROM_DEVICE; memset(sg, 0, sizeof(*sg)); sg->page = virt_to_page(rq->buffer); @@ -293,12 +240,12 @@ static void ide_build_sglist(ide_drive_t *drive, struct request *rq) nents = blk_rq_map_sg(&drive->queue, rq, sg); if (rq_data_dir(rq) == READ) - hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + hwif->sg_dma_direction = DMA_FROM_DEVICE; else - hwif->sg_dma_direction = PCI_DMA_TODEVICE; + hwif->sg_dma_direction = DMA_TO_DEVICE; } - nents = pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); + nents = dma_map_sg(state->dev, sg, nents, hwif->sg_dma_direction); hwif->sg_nents = nents; } @@ -484,7 +431,7 @@ static int icside_dma_check(ide_drive_t *drive) int xfer_mode = XFER_PIO_2; int on; - if (!id || !(id->capability & 1) || !hwif->autodma) + if (!(id->capability & 1) || !hwif->autodma) goto out; /* @@ -500,13 +447,7 @@ static int icside_dma_check(ide_drive_t *drive) * Enable DMA on any drive that has multiword DMA */ if (id->field_valid & 2) { - if (id->dma_mword & 4) { - xfer_mode = XFER_MW_DMA_2; - } else if (id->dma_mword & 2) { - xfer_mode = XFER_MW_DMA_1; - } else if (id->dma_mword & 1) { - xfer_mode = XFER_MW_DMA_0; - } + xfer_mode = ide_dma_speed(drive, 0); goto out; } @@ -531,13 +472,14 @@ out: static int icside_dma_end(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); + struct icside_state *state = hwif->hwif_data; drive->waiting_for_dma = 0; disable_dma(hwif->hw.dma); /* Teardown mappings after DMA has completed. */ - pci_unmap_sg(NULL, hwif->sg_table, hwif->sg_nents, + dma_unmap_sg(state->dev, hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); hwif->sg_dma_active = 0; @@ -713,7 +655,7 @@ int icside_dma_write(ide_drive_t *drive) static int icside_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); - struct icside_state *state = hwif->hw.priv; + struct icside_state *state = hwif->hwif_data; return inb(state->irq_port + (hwif->channel ? @@ -748,7 +690,7 @@ static int icside_dma_lostirq(ide_drive_t *drive) return 1; } -static int icside_setup_dma(ide_hwif_t *hwif) +static int icside_dma_init(ide_hwif_t *hwif) { int autodma = 0; @@ -763,6 +705,10 @@ static int icside_setup_dma(ide_hwif_t *hwif) if (!hwif->sg_table) goto failed; + hwif->atapi_dma = 1; + hwif->mwdma_mask = 7; /* MW0..2 */ + hwif->swdma_mask = 7; /* SW0..2 */ + hwif->dmatable_cpu = NULL; hwif->dmatable_dma = 0; hwif->speedproc = icside_set_speed; @@ -784,10 +730,10 @@ static int icside_setup_dma(ide_hwif_t *hwif) hwif->ide_dma_timeout = icside_dma_timeout; hwif->ide_dma_lostirq = icside_dma_lostirq; - hwif->drives[0].autodma = autodma; - hwif->drives[1].autodma = autodma; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; - printk(" capable%s\n", autodma ? ", auto-enable" : ""); + printk(" capable%s\n", hwif->autodma ? ", auto-enable" : ""); return 1; @@ -796,14 +742,16 @@ failed: return 0; } -int ide_release_dma(ide_hwif_t *hwif) +static void icside_dma_exit(ide_hwif_t *hwif) { if (hwif->sg_table) { kfree(hwif->sg_table); hwif->sg_table = NULL; } - return 1; } +#else +#define icside_dma_init(hwif) (0) +#define icside_dma_exit(hwif) do { } while (0) #endif static ide_hwif_t *icside_find_hwif(unsigned long dataport) @@ -829,7 +777,7 @@ found: } static ide_hwif_t * -icside_setup(unsigned long base, struct cardinfo *info, int irq) +icside_setup(unsigned long base, struct cardinfo *info, struct expansion_card *ec) { unsigned long port = base + info->dataoffset; ide_hwif_t *hwif; @@ -847,42 +795,47 @@ icside_setup(unsigned long base, struct cardinfo *info, int irq) } hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; hwif->io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; - hwif->hw.irq = irq; - hwif->irq = irq; - hwif->hw.dma = NO_DMA; + hwif->hw.irq = ec->irq; + hwif->irq = ec->irq; hwif->noprobe = 0; hwif->chipset = ide_acorn; + hwif->gendev.parent = &ec->dev; } return hwif; } -static int __init icside_register_v5(struct expansion_card *ec) +static int __init +icside_register_v5(struct icside_state *state, struct expansion_card *ec) { unsigned long slot_port; ide_hwif_t *hwif; slot_port = ecard_address(ec, ECARD_MEMC, 0); + state->irq_port = slot_port; + ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT); ec->irqmask = 1; - ec->irq_data = (void *)slot_port; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v5; /* * Be on the safe side - disable interrupts */ inb(slot_port + ICS_ARCIN_V5_INTROFFSET); - hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq); + hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec); + + state->hwif[0] = hwif; return hwif ? 0 : -ENODEV; } -static int __init icside_register_v6(struct expansion_card *ec) +static int __init +icside_register_v6(struct icside_state *state, struct expansion_card *ec) { unsigned long slot_port, port; - struct icside_state *state; ide_hwif_t *hwif, *mate; unsigned int sel = 0; @@ -905,89 +858,173 @@ static int __init icside_register_v6(struct expansion_card *ec) /* * Find and register the interfaces. */ - hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq); - mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq); + hwif = icside_setup(port, &icside_cardinfo_v6_1, ec); + mate = icside_setup(port, &icside_cardinfo_v6_2, ec); if (!hwif || !mate) return -ENODEV; - state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - state->channel = 0; - state->enabled = 0; state->irq_port = port; + state->slot_port = slot_port; + state->hwif[0] = hwif; + state->hwif[1] = mate; - ec->irq_data = state; - ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; + ec->irq_data = state; + ec->ops = &icside_ops_arcin_v6; hwif->maskproc = icside_maskproc; hwif->channel = 0; - hwif->hw.priv = state; + hwif->hwif_data = state; hwif->mate = mate; hwif->serialized = 1; hwif->config_data = slot_port; hwif->select_data = sel; - hwif->hw.dma = ec->dma; + hwif->hw.dma = ec->dma; mate->maskproc = icside_maskproc; mate->channel = 1; - mate->hw.priv = state; + mate->hwif_data = state; mate->mate = hwif; mate->serialized = 1; mate->config_data = slot_port; mate->select_data = sel | 1; - mate->hw.dma = ec->dma; + mate->hw.dma = ec->dma; -#ifdef CONFIG_BLK_DEV_IDEDMA_ICS if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) { - icside_setup_dma(hwif); - icside_setup_dma(mate); + icside_dma_init(hwif); + icside_dma_init(mate); } -#endif + return 0; } static int __devinit icside_probe(struct expansion_card *ec, const struct ecard_id *id) { - int result; + struct icside_state *state; + void *idmem; + int ret; - ecard_claim(ec); + state = kmalloc(sizeof(struct icside_state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto out; + } + + memset(state, 0, sizeof(state)); + state->type = ICS_TYPE_NOTYPE; + state->dev = &ec->dev; + + idmem = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST), + ecard_resource_len(ec, ECARD_RES_IOCFAST)); + if (idmem) { + unsigned int type; + + type = readb(idmem + ICS_IDENT_OFFSET) & 1; + type |= (readb(idmem + ICS_IDENT_OFFSET + 4) & 1) << 1; + type |= (readb(idmem + ICS_IDENT_OFFSET + 8) & 1) << 2; + type |= (readb(idmem + ICS_IDENT_OFFSET + 12) & 1) << 3; + iounmap(idmem); - switch (icside_identifyif(ec)) { - case ics_if_arcin_v5: - result = icside_register_v5(ec); + state->type = type; + } + + switch (state->type) { + case ICS_TYPE_A3IN: + printk(KERN_WARNING "icside: A3IN unsupported\n"); + ret = -ENODEV; break; - case ics_if_arcin_v6: - result = icside_register_v6(ec); + case ICS_TYPE_A3USER: + printk(KERN_WARNING "icside: A3USER unsupported\n"); + ret = -ENODEV; + break; + + case ICS_TYPE_V5: + ret = icside_register_v5(state, ec); + break; + + case ICS_TYPE_V6: + ret = icside_register_v6(state, ec); break; default: - result = -ENODEV; + printk(KERN_WARNING "icside: unknown interface type\n"); + ret = -ENODEV; break; } - if (result) - ecard_release(ec); - /* - * this locks the driver in-core - remove this - * comment and the two lines below when we can - * safely remove interfaces. - */ - else + if (ret == 0) { + ecard_set_drvdata(ec, state); + + /* + * this locks the driver in-core - remove this + * comment and the line below when we can + * safely remove interfaces. + */ MOD_INC_USE_COUNT; + } else { + kfree(state); + } + out: + return ret; +} + +static void __devexit icside_remove(struct expansion_card *ec) +{ + struct icside_state *state = ecard_get_drvdata(ec); - return result; + switch (state->type) { + case ICS_TYPE_V5: + /* FIXME: tell IDE to stop using the interface */ + + /* Disable interrupts */ + inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); + break; + + case ICS_TYPE_V6: + /* FIXME: tell IDE to stop using the interface */ + icside_dma_exit(state->hwif[1]); + icside_dma_exit(state->hwif[0]); + + if (ec->dma != NO_DMA) + free_dma(ec->dma); + + /* Disable interrupts */ + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + + /* Reset the ROM pointer/EASI selection */ + outb(0, state->slot_port); + break; + } + + ecard_set_drvdata(ec, NULL); + ec->ops = NULL; + ec->irq_data = NULL; + + kfree(state); } -static void __devexit -icside_remove(struct expansion_card *ec) +static void icside_shutdown(struct expansion_card *ec) { - /* need to do more */ - ecard_release(ec); + struct icside_state *state = ecard_get_drvdata(ec); + + switch (state->type) { + case ICS_TYPE_V5: + /* Disable interrupts */ + inb(state->slot_port + ICS_ARCIN_V5_INTROFFSET); + break; + + case ICS_TYPE_V6: + /* Disable interrupts */ + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1); + inb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2); + + /* Reset the ROM pointer/EASI selection */ + outb(0, state->slot_port); + break; + } } static const struct ecard_id icside_ids[] = { @@ -999,6 +1036,7 @@ static const struct ecard_id icside_ids[] = { static struct ecard_driver icside_driver = { .probe = icside_probe, .remove = __devexit_p(icside_remove), + .shutdown = icside_shutdown, .id_table = icside_ids, .drv = { .name = "icside", diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 9291933174ae..f8af375acf24 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -20,8 +20,6 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) hw_regs_t hw; int i, ret; - ecard_claim(ec); - memset(&hw, 0, sizeof(hw)); for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { @@ -49,7 +47,6 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id) static void __devexit rapide_remove(struct expansion_card *ec) { /* need to do more */ - ecard_release(ec); } static struct ecard_id rapide_ids[] = { -- cgit v1.2.3 From b9d88a8fe5064c84736df51f0629c2d7c1a89cc1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 17 May 2003 17:52:30 +0100 Subject: [ARM] Remove .devclass initialiser from sa1111ps2. --- drivers/input/serio/sa1111ps2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/serio/sa1111ps2.c b/drivers/input/serio/sa1111ps2.c index bf9adc378a4d..390c4d7e5b9d 100644 --- a/drivers/input/serio/sa1111ps2.c +++ b/drivers/input/serio/sa1111ps2.c @@ -347,7 +347,6 @@ static struct sa1111_driver ps2_driver = { .drv = { .name = "sa1111-ps2", .bus = &sa1111_bus_type, - .devclass = &input_devclass, .probe = ps2_probe, .remove = ps2_remove, .suspend = ps2_suspend, -- cgit v1.2.3 From 1b473f0993f3f90b7db099a805514cfcbb61b680 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 17 May 2003 17:57:26 +0100 Subject: [ARM] Fix time_after() warnings in ether1.c. --- drivers/net/arm/ether1.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c index 758ff8bb8231..1ceee2381085 100644 --- a/drivers/net/arm/ether1.c +++ b/drivers/net/arm/ether1.c @@ -450,6 +450,7 @@ ether1_init_for_open (struct net_device *dev) struct ether1_priv *priv = (struct ether1_priv *)dev->priv; int i, status, addr, next, next2; int failures = 0; + unsigned long timeout; outb (CTRL_RST|CTRL_ACK, REG_CONTROL); @@ -515,19 +516,19 @@ ether1_init_for_open (struct net_device *dev) outb (CTRL_CA, REG_CONTROL); /* 586 should now unset iscp.busy */ - i = jiffies + HZ/2; + timeout = jiffies + HZ/2; while (ether1_inw (dev, ISCP_ADDR, iscp_t, iscp_busy, DISABLEIRQS) == 1) { - if (time_after(jiffies, i)) { + if (time_after(jiffies, timeout)) { printk (KERN_WARNING "%s: can't initialise 82586: iscp is busy\n", dev->name); return 1; } } /* check status of commands that we issued */ - i += HZ/10; + timeout += HZ/10; while (((status = ether1_inw (dev, CFG_ADDR, cfg_t, cfg_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) + if (time_after(jiffies, timeout)) break; } @@ -541,10 +542,10 @@ ether1_init_for_open (struct net_device *dev) failures += 1; } - i += HZ/10; + timeout += HZ/10; while (((status = ether1_inw (dev, SA_ADDR, sa_t, sa_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) + if (time_after(jiffies, timeout)) break; } @@ -558,10 +559,10 @@ ether1_init_for_open (struct net_device *dev) failures += 1; } - i += HZ/10; + timeout += HZ/10; while (((status = ether1_inw (dev, MC_ADDR, mc_t, mc_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) + if (time_after(jiffies, timeout)) break; } @@ -575,10 +576,10 @@ ether1_init_for_open (struct net_device *dev) failures += 1; } - i += HZ; + timeout += HZ; while (((status = ether1_inw (dev, TDR_ADDR, tdr_t, tdr_status, DISABLEIRQS)) & STAT_COMPLETE) == 0) { - if (time_after(jiffies, i)) + if (time_after(jiffies, timeout)) break; } -- cgit v1.2.3