patch-2.4.19 linux-2.4.19/drivers/scsi/mesh.c
Next file: linux-2.4.19/drivers/scsi/mesh.h
Previous file: linux-2.4.19/drivers/scsi/megaraid.c
Back to the patch index
Back to the overall index
- Lines: 571
- Date:
Fri Aug 2 17:39:44 2002
- Orig file:
linux-2.4.18/drivers/scsi/mesh.c
- Orig date:
Mon Feb 25 11:38:04 2002
diff -urN linux-2.4.18/drivers/scsi/mesh.c linux-2.4.19/drivers/scsi/mesh.c
@@ -6,8 +6,16 @@
*
* Paul Mackerras, August 1996.
* Copyright (C) 1996 Paul Mackerras.
+ *
+ * Apr. 14 2002 - BenH Rework bus reset code for new error handler
+ * Add delay after initial bus reset
+ * Add module parameters
+ * To do:
+ * - handle aborts correctly
+ * - retry arbitration if lost (unless higher levels do this for us)
*/
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/types.h>
@@ -20,6 +28,7 @@
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
+#include <linux/pci.h>
#include <asm/dbdma.h>
#include <asm/io.h>
#include <asm/pgtable.h>
@@ -30,6 +39,7 @@
#include <asm/processor.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
+#include <asm/pci-bridge.h>
#ifdef CONFIG_PMAC_PBOOK
#include <linux/adb.h>
#include <linux/pmu.h>
@@ -39,35 +49,39 @@
#include "hosts.h"
#include "mesh.h"
-/*
- * To do:
- * - handle aborts correctly
- * - retry arbitration if lost (unless higher levels do this for us)
- */
-
-#define MESH_NEW_STYLE_EH
-
#if 1
#undef KERN_DEBUG
#define KERN_DEBUG KERN_WARNING
#endif
-#if CONFIG_SCSI_MESH_SYNC_RATE == 0
-int mesh_sync_period = 100;
-int mesh_sync_offset = 0;
-#else
-int mesh_sync_period = 1000 / CONFIG_SCSI_MESH_SYNC_RATE; /* ns */
-int mesh_sync_offset = 15;
-#endif
-
-int mesh_sync_targets = 0xff; /* targets to set synchronous (bitmap) */
-int mesh_resel_targets = 0xff; /* targets that we let disconnect (bitmap) */
-int mesh_debug_targets = 0; /* print debug for these targets */
-unsigned char use_active_neg = 0; /* bit mask for SEQ_ACTIVE_NEG if used */
-
-#define ALLOW_SYNC(tgt) ((mesh_sync_targets >> (tgt)) & 1)
-#define ALLOW_RESEL(tgt) ((mesh_resel_targets >> (tgt)) & 1)
-#define ALLOW_DEBUG(tgt) ((mesh_debug_targets >> (tgt)) & 1)
+MODULE_AUTHOR("Paul Mackerras (paulus@samba.org)");
+MODULE_DESCRIPTION("PowerMac MESH SCSI driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(sync_rate, "i");
+MODULE_PARM_DESC(sync_rate, "Synchronous rate (0..10, 0=async)");
+MODULE_PARM(sync_targets, "i");
+MODULE_PARM_DESC(sync_targets, "Bitmask of targets allowed to set synchronous");
+MODULE_PARM(resel_targets, "i");
+MODULE_PARM_DESC(resel_targets, "Bitmask of targets allowed to set disconnect");
+MODULE_PARM(debug_targets, "i");
+MODULE_PARM_DESC(debug_targets, "Bitmask of debugged targets");
+MODULE_PARM(init_reset_delay, "i");
+MODULE_PARM_DESC(init_reset_delay, "Initial bus reset delay (0=no reset)");
+
+static int sync_rate = CONFIG_SCSI_MESH_SYNC_RATE;
+static int sync_targets = 0xff;
+static int resel_targets = 0xff;
+static int debug_targets = 0; /* print debug for these targets */
+static int init_reset_delay = CONFIG_SCSI_MESH_RESET_DELAY_MS;
+
+static int mesh_sync_period = 100;
+static int mesh_sync_offset = 0;
+static unsigned char use_active_neg = 0; /* bit mask for SEQ_ACTIVE_NEG if used */
+
+#define ALLOW_SYNC(tgt) ((sync_targets >> (tgt)) & 1)
+#define ALLOW_RESEL(tgt) ((resel_targets >> (tgt)) & 1)
+#define ALLOW_DEBUG(tgt) ((debug_targets >> (tgt)) & 1)
#define DEBUG_TARGET(cmd) ((cmd) && ALLOW_DEBUG((cmd)->target))
#undef MESH_DBG
@@ -95,7 +109,8 @@
statusing,
busfreeing,
disconnecting,
- reselecting
+ reselecting,
+ sleeping
};
enum msg_phase {
@@ -119,7 +134,6 @@
int data_goes_out; /* guess as to data direction */
Scsi_Cmnd *current_req;
u32 saved_ptr;
- int want_abort;
#ifdef MESH_DBG
int log_ix;
int n_log;
@@ -156,11 +170,7 @@
struct mesh_target tgts[8];
void *dma_cmd_space;
struct device_node *ofnode;
-#ifndef MESH_NEW_STYLE_EH
- Scsi_Cmnd *completed_q;
- Scsi_Cmnd *completed_qtail;
- struct tq_struct tqueue;
-#endif
+ struct pci_dev* pdev;
#ifdef MESH_DBG
int log_ix;
int n_log;
@@ -192,9 +202,6 @@
static void mesh_dump_regs(struct mesh_state *);
static void mesh_start(struct mesh_state *);
static void mesh_start_cmd(struct mesh_state *, Scsi_Cmnd *);
-#ifndef MESH_NEW_STYLE_EH
-static void finish_cmds(void *);
-#endif
static void add_sdtr_msg(struct mesh_state *);
static void set_sdtr(struct mesh_state *, int, int);
static void start_phase(struct mesh_state *);
@@ -240,6 +247,12 @@
struct Scsi_Host *mesh_host;
void *dma_cmd_space;
+ /* The SCSI layer is dumb ! It calls us with the IO request
+ * lock held and interrupts disabled here... This doesn't happen
+ * in 2.5
+ */
+ spin_unlock_irq(&io_request_lock);
+
if (_machine == _MACH_Pmac) {
use_active_neg = (find_devices("mac-io") ? 0 : SEQ_ACTIVE_NEG);
} else {
@@ -247,6 +260,16 @@
use_active_neg = SEQ_ACTIVE_NEG;
}
+ /* Calculate sync rate from module parameters */
+ if (sync_rate > 10)
+ sync_rate = 10;
+ if (sync_rate > 0) {
+ printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
+ mesh_sync_period = 1000 / sync_rate; /* ns */
+ mesh_sync_offset = 15;
+ } else
+ printk(KERN_INFO "mesh: configured for asynchronous\n");
+
nmeshes = 0;
prev_statep = &all_meshes;
/*
@@ -258,11 +281,23 @@
if (mesh == 0)
mesh = find_compatible_devices("scsi", "chrp,mesh0");
for (; mesh != 0; mesh = mesh->next) {
+ u8 pci_bus, pci_devfn;
+ struct pci_dev* pdev = NULL;
+
if (mesh->n_addrs != 2 || mesh->n_intrs != 2) {
printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
- " (got %d,%d)", mesh->n_addrs, mesh->n_intrs);
+ " (got %d,%d)\n", mesh->n_addrs, mesh->n_intrs);
+ continue;
+ }
+ if (mesh->parent != NULL
+ && pci_device_from_OF_node(mesh->parent, &pci_bus,
+ &pci_devfn) == 0)
+ pdev = pci_find_slot(pci_bus, pci_devfn);
+ if (pdev == NULL) {
+ printk(KERN_ERR "mesh: Can't locate PCI entry\n");
continue;
}
+
mesh_host = scsi_register(tp, sizeof(struct mesh_state));
if (mesh_host == 0) {
printk(KERN_ERR "mesh: couldn't register host");
@@ -279,6 +314,7 @@
memset(ms, 0, sizeof(*ms));
ms->host = mesh_host;
ms->ofnode = mesh;
+ ms->pdev = pdev;
ms->mesh = (volatile struct mesh_regs *)
ioremap(mesh->addrs[0].address, 0x1000);
ms->dma = (volatile struct dbdma_regs *)
@@ -303,10 +339,6 @@
ms->tgts[tgt].sync_params = ASYNC_PARAMS;
ms->tgts[tgt].current_req = 0;
}
-#ifndef MESH_NEW_STYLE_EH
- ms->tqueue.routine = finish_cmds;
- ms->tqueue.data = ms;
-#endif
*prev_statep = ms;
prev_statep = &ms->next;
@@ -341,6 +373,9 @@
register_reboot_notifier(&mesh_notifier);
}
+ /* Return to the SCSI layer in the same state we got called */
+ spin_lock_irq(&io_request_lock);
+
return nmeshes;
}
@@ -395,15 +430,33 @@
case PBOOK_SLEEP_NOW:
for (ms = all_meshes; ms != 0; ms = ms->next) {
+ unsigned long flags;
+
+ scsi_block_requests(ms->host);
+ spin_lock_irqsave(&io_request_lock, flags);
+ while(ms->phase != idle) {
+ spin_unlock_irqrestore(&io_request_lock, flags);
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout(1);
+ spin_lock_irqsave(&io_request_lock, flags);
+ }
+ ms->phase = sleeping;
+ spin_unlock_irqrestore(&io_request_lock, flags);
disable_irq(ms->meshintr);
set_mesh_power(ms, 0);
}
break;
case PBOOK_WAKE:
for (ms = all_meshes; ms != 0; ms = ms->next) {
+ unsigned long flags;
+
set_mesh_power(ms, 1);
mesh_init(ms);
+ spin_lock_irqsave(&io_request_lock, flags);
+ mesh_start(ms);
+ spin_unlock_irqrestore(&io_request_lock, flags);
enable_irq(ms->meshintr);
+ scsi_unblock_requests(ms->host);
}
break;
}
@@ -411,10 +464,13 @@
}
#endif /* CONFIG_PMAC_PBOOK */
+/*
+ * Called by midlayer with host locked to queue a new
+ * request
+ */
int
mesh_queue(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
- unsigned long flags;
struct mesh_state *ms;
cmd->scsi_done = done;
@@ -422,8 +478,6 @@
ms = (struct mesh_state *) cmd->host->hostdata;
- save_flags(flags);
- cli();
if (ms->request_q == NULL)
ms->request_q = cmd;
else
@@ -433,19 +487,25 @@
if (ms->phase == idle)
mesh_start(ms);
- restore_flags(flags);
return 0;
}
+/* Todo: here we can at least try to remove the command from the
+ * queue if it isn't connected yet, and for pending command, assert
+ * ATN until the bus gets freed.
+ */
int
mesh_abort(Scsi_Cmnd *cmd)
{
struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;
+ unsigned long flags;
printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
+ spin_lock_irqsave(&io_request_lock, flags);
mesh_dump_regs(ms);
dumplog(ms, cmd->target);
dumpslog(ms);
+ spin_unlock_irqrestore(&io_request_lock, flags);
return SCSI_ABORT_SNOOZE;
}
@@ -482,49 +542,41 @@
}
}
+/*
+ * Called by the midlayer with the lock held to reset the
+ * SCSI host and bus.
+ * The midlayer will wait for devices to come back, we don't need
+ * to do that ourselves
+ */
int
-mesh_reset(Scsi_Cmnd *cmd, unsigned how)
+mesh_host_reset(Scsi_Cmnd *cmd)
{
struct mesh_state *ms = (struct mesh_state *) cmd->host->hostdata;
volatile struct mesh_regs *mr = ms->mesh;
volatile struct dbdma_regs *md = ms->dma;
- unsigned long flags;
- int ret;
- printk(KERN_DEBUG "mesh_reset %x\n", how);
- ret = SCSI_RESET_BUS_RESET;
- save_flags(flags);
- cli();
+ printk(KERN_DEBUG "mesh_host_reset\n");
+
+ /* Reset the controller & dbdma channel */
out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */
out_8(&mr->exception, 0xff); /* clear all exception bits */
out_8(&mr->error, 0xff); /* clear all error bits */
- if (how & SCSI_RESET_SUGGEST_HOST_RESET) {
- out_8(&mr->sequence, SEQ_RESETMESH);
- ret |= SCSI_RESET_HOST_RESET;
- udelay(1);
- out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
- out_8(&mr->source_id, ms->host->this_id);
- out_8(&mr->sel_timeout, 25); /* 250ms */
- out_8(&mr->sync_params, ASYNC_PARAMS);
- }
+ out_8(&mr->sequence, SEQ_RESETMESH);
+ udelay(1);
+ out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
+ out_8(&mr->source_id, ms->host->this_id);
+ out_8(&mr->sel_timeout, 25); /* 250ms */
+ out_8(&mr->sync_params, ASYNC_PARAMS);
+
+ /* Reset the bus */
out_8(&mr->bus_status1, BS1_RST); /* assert RST */
udelay(30); /* leave it on for >= 25us */
out_8(&mr->bus_status1, 0); /* negate RST */
-#ifdef DO_ASYNC_RESET
- if (how & SCSI_RESET_ASYNCHRONOUS) {
- restore_flags(flags);
- ret |= SCSI_RESET_PENDING;
- } else
-#endif
- {
- handle_reset(ms);
- restore_flags(flags);
-#ifndef MESH_NEW_STYLE_EH
- finish_cmds(ms);
-#endif
- ret |= SCSI_RESET_SUCCESS;
- }
- return ret;
+
+ /* Complete pending commands */
+ handle_reset(ms);
+
+ return SUCCESS;
}
/*
@@ -553,13 +605,10 @@
return NOTIFY_DONE;
}
-int
-mesh_command(Scsi_Cmnd *cmd)
-{
- printk(KERN_WARNING "whoops... mesh_command called\n");
- return -1;
-}
-
+/* Called with meshinterrupt disabled, initialize the chipset
+ * and eventually do the initial bus reset. The lock must not be
+ * held since we can schedule.
+ */
static void
mesh_init(struct mesh_state *ms)
{
@@ -568,6 +617,7 @@
udelay(100);
+ /* Reset controller */
out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16); /* stop dma */
out_8(&mr->exception, 0xff); /* clear all exception bits */
out_8(&mr->error, 0xff); /* clear all error bits */
@@ -578,15 +628,28 @@
out_8(&mr->sel_timeout, 25); /* 250ms */
out_8(&mr->sync_params, ASYNC_PARAMS);
- out_8(&mr->bus_status1, BS1_RST); /* assert RST */
- udelay(30); /* leave it on for >= 25us */
- out_8(&mr->bus_status1, 0); /* negate RST */
-
+ if (init_reset_delay) {
+ printk(KERN_INFO "mesh: performing initial bus reset...\n");
+
+ /* Reset bus */
+ out_8(&mr->bus_status1, BS1_RST); /* assert RST */
+ udelay(30); /* leave it on for >= 25us */
+ out_8(&mr->bus_status1, 0); /* negate RST */
+
+ /* Wait for bus to come back */
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout((init_reset_delay * HZ) / 1000);
+ }
+
+ /* Reconfigure controller */
+ out_8(&mr->interrupt, 0xff); /* clear all interrupt bits */
out_8(&mr->sequence, SEQ_FLUSHFIFO);
udelay(1);
out_8(&mr->sync_params, ASYNC_PARAMS);
out_8(&mr->sequence, SEQ_ENBRESEL);
- out_8(&mr->interrupt, 0xff); /* clear all interrupt bits */
+
+ ms->phase = idle;
+ ms->msgphase = msg_none;
}
/*
@@ -646,6 +709,8 @@
cmd->use_sg, cmd->request_buffer, cmd->request_bufflen);
}
#endif
+ if (ms->dma_started)
+ panic("mesh: double DMA start !\n");
ms->phase = arbitrating;
ms->msgphase = msg_none;
@@ -770,28 +835,6 @@
}
}
-#ifndef MESH_NEW_STYLE_EH
-static void
-finish_cmds(void *data)
-{
- struct mesh_state *ms = data;
- Scsi_Cmnd *cmd;
- unsigned long flags;
-
- for (;;) {
- spin_lock_irqsave(&io_request_lock, flags);
- cmd = ms->completed_q;
- if (cmd == NULL) {
- spin_unlock_irqrestore(&io_request_lock, flags);
- break;
- }
- ms->completed_q = (Scsi_Cmnd *) cmd->host_scribble;
- (*cmd->scsi_done)(cmd);
- spin_unlock_irqrestore(&io_request_lock, flags);
- }
-}
-#endif /* MESH_NEW_STYLE_EH */
-
static inline void
add_sdtr_msg(struct mesh_state *ms)
{
@@ -1316,10 +1359,13 @@
dumpslog(ms);
}
+ if (ms->dma_started) {
+ printk(KERN_ERR "mesh: reselected with DMA started !\n");
+ halt_dma(ms);
+ }
ms->current_req = NULL;
ms->phase = dataing;
ms->msgphase = msg_in;
- ms->dma_started = 0;
ms->n_msgout = 0;
ms->last_n_msgout = 0;
prev = ms->conn_tgt;
@@ -1738,18 +1784,7 @@
static void
mesh_completed(struct mesh_state *ms, Scsi_Cmnd *cmd)
{
-#ifdef MESH_NEW_STYLE_EH
(*cmd->scsi_done)(cmd);
-#else
- if (ms->completed_q == NULL)
- ms->completed_q = cmd;
- else
- ms->completed_qtail->host_scribble = (void *) cmd;
- ms->completed_qtail = cmd;
- cmd->host_scribble = NULL;
- queue_task(&ms->tqueue, &tq_immediate);
- mark_bh(IMMEDIATE_BH);
-#endif /* MESH_NEW_STYLE_EH */
}
/*
@@ -1769,24 +1804,29 @@
if (cmd) {
cmd->SCp.this_residual = cmd->request_bufflen;
if (cmd->use_sg > 0) {
+ int nseg;
total = 0;
scl = (struct scatterlist *) cmd->buffer;
off = ms->data_ptr;
- for (i = 0; i < cmd->use_sg; ++i, ++scl) {
+ nseg = pci_map_sg(ms->pdev, scl, cmd->use_sg,
+ scsi_to_pci_dma_dir(cmd ->sc_data_direction));
+ for (i = 0; i <nseg; ++i, ++scl) {
+ u32 dma_addr = sg_dma_address(scl);
+ u32 dma_len = sg_dma_len(scl);
+
total += scl->length;
- if (off >= scl->length) {
- off -= scl->length;
+ if (off >= dma_len) {
+ off -= dma_len;
continue;
}
- if (scl->length > 0xffff)
+ if (dma_len > 0xffff)
panic("mesh: scatterlist element >= 64k");
- st_le16(&dcmds->req_count, scl->length - off);
+ st_le16(&dcmds->req_count, dma_len - off);
st_le16(&dcmds->command, dma_cmd);
- st_le32(&dcmds->phy_addr,
- virt_to_phys(scl->address) + off);
+ st_le32(&dcmds->phy_addr, dma_addr + off);
dcmds->xfer_status = 0;
++dcmds;
- dtot += scl->length - off;
+ dtot += dma_len - off;
off = 0;
}
} else if (ms->data_ptr < cmd->request_bufflen) {
@@ -1794,6 +1834,7 @@
if (dtot > 0xffff)
panic("mesh: transfer size >= 64k");
st_le16(&dcmds->req_count, dtot);
+ /* XXX Use pci DMA API here ... */
st_le32(&dcmds->phy_addr,
virt_to_phys(cmd->request_buffer) + ms->data_ptr);
dcmds->xfer_status = 0;
@@ -1860,6 +1901,12 @@
ms->conn_tgt, ms->data_ptr, cmd->request_bufflen,
ms->tgts[ms->conn_tgt].data_goes_out);
}
+ if (cmd->use_sg != 0) {
+ struct scatterlist *sg;
+ sg = (struct scatterlist *)cmd->request_buffer;
+ pci_unmap_sg(ms->pdev, sg, cmd->use_sg,
+ scsi_to_pci_dma_dir(cmd->sc_data_direction));
+ }
ms->dma_started = 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)