// SPDX-License-Identifier: GPL-2.0-only /* * SCSI low-level driver for the MESH (Macintosh Enhanced SCSI Hardware) * bus adaptor found on Power Macintosh computers. * We assume the MESH is connected to a DBDMA (descriptor-based DMA) * controller. * * Paul Mackerras, August 1996. * Copyright (C) 1996 Paul Mackerras. * * Apr. 21 2002 - BenH Rework bus reset code for new error handler * Add delay after initial bus reset * Add module parameters * * Sep. 27 2003 - BenH Move to new driver model, fix some write posting * issues * To do: * - handle aborts correctly * - retry arbitration if lost (unless higher levels do this for us) * - power down the chip when no device is detected
*/ #include <linux/module.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/types.h> #include <linux/string.h> #include <linux/blkdev.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/interrupt.h> #include <linux/reboot.h> #include <linux/spinlock.h> #include <linux/pci.h> #include <linux/pgtable.h> #include <asm/dbdma.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/irq.h> #include <asm/hydra.h> #include <asm/processor.h> #include <asm/setup.h> #include <asm/pmac_feature.h> #include <asm/macio.h>
struct mesh_target { enum sdtr_phase sdtr_state; int sync_params; int data_goes_out; /* guess as to data direction */ struct scsi_cmnd *current_req;
u32 saved_ptr; #ifdef MESH_DBG int log_ix; int n_log; struct dbglog log[N_DBG_LOG]; #endif
};
struct mesh_state { volatilestruct mesh_regs __iomem *mesh; int meshintr; volatilestruct dbdma_regs __iomem *dma; int dmaintr; struct Scsi_Host *host; struct mesh_state *next; struct scsi_cmnd *request_q; struct scsi_cmnd *request_qtail; enum mesh_phase phase; /* what we're currently trying to do */ enum msg_phase msgphase; int conn_tgt; /* target we're connected to */ struct scsi_cmnd *current_req; /* req we're currently working on */ int data_ptr; int dma_started; int dma_count; int stat; int aborting; int expect_reply; int n_msgin;
u8 msgin[16]; int n_msgout; int last_n_msgout;
u8 msgout[16]; struct dbdma_cmd *dma_cmds; /* space for dbdma commands, aligned */
dma_addr_t dma_cmd_bus; void *dma_cmd_space; int dma_cmd_size; int clk_freq; struct mesh_target tgts[8]; struct macio_dev *mdev; struct pci_dev* pdev; #ifdef MESH_DBG int log_ix; int n_log; struct dbglog log[N_DBG_SLOG]; #endif
};
/* * Driver is too messy, we need a few prototypes...
*/ staticvoid mesh_done(struct mesh_state *ms, int start_next); staticvoid mesh_interrupt(struct mesh_state *ms); staticvoid cmd_complete(struct mesh_state *ms); staticvoid set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd); staticvoid halt_dma(struct mesh_state *ms); staticvoid phase_mismatch(struct mesh_state *ms);
/* * Some debugging & logging routines
*/
#ifdef MESH_DBG
staticinline u32 readtb(void)
{
u32 tb;
#ifdef DBG_USE_TB /* Beware: if you enable this, it will crash on 601s. */ asm ("mftb %0" : "=r" (tb) : ); #else
tb = 0; #endif return tb;
}
staticvoid dlog(struct mesh_state *ms, char *fmt, int a)
{ struct mesh_target *tp = &ms->tgts[ms->conn_tgt]; struct dbglog *tlp, *slp;
/* * Flush write buffers on the bus path to the mesh
*/ staticinlinevoid mesh_flush_io(volatilestruct mesh_regs __iomem *mr)
{
(void)in_8(&mr->mesh_id);
}
/* Called with meshinterrupt disabled, initialize the chipset * and eventually do the initial bus reset. The lock must not be * held since we can schedule.
*/ staticvoid mesh_init(struct mesh_state *ms)
{ volatilestruct mesh_regs __iomem *mr = ms->mesh; volatilestruct dbdma_regs __iomem *md = ms->dma;
/* Off we go */
dlog(ms, "about to arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
out_8(&mr->interrupt, INT_CMDDONE);
out_8(&mr->sequence, SEQ_ENBRESEL);
mesh_flush_io(mr);
udelay(1);
if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) { /* * Some other device has the bus or is arbitrating for it - * probably a target which is about to reselect us.
*/
dlog(ms, "busy b4 arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception,
mr->error, mr->fifo_count)); for (t = 100; t > 0; --t) { if ((in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) == 0) break; if (in_8(&mr->interrupt) != 0) {
dlog(ms, "intr b4 arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception,
mr->error, mr->fifo_count));
mesh_interrupt(ms); if (ms->phase != arbitrating) return;
}
udelay(1);
} if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) { /* XXX should try again in a little while */
ms->stat = DID_BUS_BUSY;
ms->phase = idle;
mesh_done(ms, 0); return;
}
}
/* * Apparently the mesh has a bug where it will assert both its * own bit and the target's bit on the bus during arbitration.
*/
out_8(&mr->dest_id, mr->source_id);
/* * There appears to be a race with reselection sometimes, * where a target reselects us just as we issue the * arbitrate command. It seems that then the arbitrate * command just hangs waiting for the bus to be free * without giving us a reselection exception. * The only way I have found to get it to respond correctly * is this: disable reselection before issuing the arbitrate * command, then after issuing it, if it looks like a target * is trying to reselect us, reset the mesh and then enable * reselection.
*/
out_8(&mr->sequence, SEQ_DISRESEL); if (in_8(&mr->interrupt) != 0) {
dlog(ms, "intr after disresel, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception,
mr->error, mr->fifo_count));
mesh_interrupt(ms); if (ms->phase != arbitrating) return;
dlog(ms, "after intr after disresel, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception,
mr->error, mr->fifo_count));
}
out_8(&mr->sequence, SEQ_ARBITRATE);
for (t = 230; t > 0; --t) { if (in_8(&mr->interrupt) != 0) break;
udelay(1);
}
dlog(ms, "after arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count)); if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
&& (in_8(&mr->bus_status0) & BS0_IO)) { /* looks like a reselection - try resetting the mesh */
dlog(ms, "resel? after arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
out_8(&mr->sequence, SEQ_RESETMESH);
mesh_flush_io(mr);
udelay(10);
out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
out_8(&mr->sequence, SEQ_ENBRESEL);
mesh_flush_io(mr); for (t = 10; t > 0 && in_8(&mr->interrupt) == 0; --t)
udelay(1);
dlog(ms, "tried reset after arb, intr/exc/err/fc=%.8x",
MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count)); #ifndef MESH_MULTIPLE_HOSTS if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
&& (in_8(&mr->bus_status0) & BS0_IO)) {
printk(KERN_ERR "mesh: controller not responding" " to reselection!\n"); /* * If this is a target reselecting us, and the * mesh isn't responding, the higher levels of * the scsi code will eventually time out and * reset the bus.
*/
} #endif
}
}
/* * Start the next command for a MESH. * Should be called with interrupts disabled.
*/ staticvoid mesh_start(struct mesh_state *ms)
{ struct scsi_cmnd *cmd, *prev, *next;
case msg_out: /* * To make sure ATN drops before we assert ACK for * the last byte of the message, we have to do the * last byte specially.
*/ if (ms->n_msgout <= 0) {
printk(KERN_ERR "mesh: msg_out but n_msgout=%d\n",
ms->n_msgout);
mesh_dump_regs(ms);
ms->msgphase = msg_none; break;
} if (ALLOW_DEBUG(ms->conn_tgt)) {
printk(KERN_DEBUG "mesh: sending %d msg bytes:",
ms->n_msgout); for (i = 0; i < ms->n_msgout; ++i)
printk(" %x", ms->msgout[i]);
printk("\n");
}
dlog(ms, "msgout msg=%.8x", MKWORD(ms->n_msgout, ms->msgout[0],
ms->msgout[1], ms->msgout[2]));
out_8(&mr->count_hi, 0);
out_8(&mr->sequence, SEQ_FLUSHFIFO);
mesh_flush_io(mr);
udelay(1); /* * If ATN is not already asserted, we assert it, then * issue a SEQ_MSGOUT to get the mesh to drop ACK.
*/ if ((in_8(&mr->bus_status0) & BS0_ATN) == 0) {
dlog(ms, "bus0 was %.2x explicitly asserting ATN", mr->bus_status0);
out_8(&mr->bus_status0, BS0_ATN); /* explicit ATN */
mesh_flush_io(mr);
udelay(1);
out_8(&mr->count_lo, 1);
out_8(&mr->sequence, SEQ_MSGOUT + seq);
out_8(&mr->bus_status0, 0); /* release explicit ATN */
dlog(ms,"hace: after explicit ATN bus0=%.2x",mr->bus_status0);
} if (ms->n_msgout == 1) { /* * We can't issue the SEQ_MSGOUT without ATN * until the target has asserted REQ. The logic * in cmd_complete handles both situations: * REQ already asserted or not.
*/
cmd_complete(ms);
} else {
out_8(&mr->count_lo, ms->n_msgout - 1);
out_8(&mr->sequence, SEQ_MSGOUT + seq); for (i = 0; i < ms->n_msgout - 1; ++i)
out_8(&mr->fifo, ms->msgout[i]);
} return;
/* * Find out who reselected us.
*/ if (in_8(&mr->fifo_count) == 0) {
printk(KERN_ERR "mesh: reselection but nothing in fifo?\n");
ms->conn_tgt = ms->host->this_id; goto bogus;
} /* get the last byte in the fifo */ do {
b = in_8(&mr->fifo);
dlog(ms, "reseldata %x", b);
} while (in_8(&mr->fifo_count)); for (t = 0; t < 8; ++t) if ((b & (1 << t)) != 0 && t != ms->host->this_id) break; if (b != (1 << t) + (1 << ms->host->this_id)) {
printk(KERN_ERR "mesh: bad reselection data %x\n", b);
ms->conn_tgt = ms->host->this_id; goto bogus;
}
/* * Set up to continue with that target's transfer.
*/
ms->conn_tgt = t;
tp = &ms->tgts[t];
out_8(&mr->sync_params, tp->sync_params); if (ALLOW_DEBUG(t)) {
printk(KERN_DEBUG "mesh: reselected by target %d\n", t);
printk(KERN_DEBUG "mesh: saved_ptr=%x goes_out=%d cmd=%p\n",
tp->saved_ptr, tp->data_goes_out, tp->current_req);
}
ms->current_req = tp->current_req; if (tp->current_req == NULL) {
printk(KERN_ERR "mesh: reselected by tgt %d but no cmd!\n", t); goto bogus;
}
ms->data_ptr = tp->saved_ptr;
dlog(ms, "resel prev tgt=%d", prev);
dlog(ms, "resel err/exc=%.4x", MKWORD(0, 0, mr->error, mr->exception));
start_phase(ms); return;
case msg_in: /* should have some message bytes in fifo */
get_msgin(ms);
n = msgin_length(ms); if (ms->n_msgin < n) {
out_8(&mr->count_lo, n - ms->n_msgin);
out_8(&mr->sequence, SEQ_MSGIN + seq);
} else {
ms->msgphase = msg_none;
handle_msgin(ms);
start_phase(ms);
} break;
case msg_out: /* * To get the right timing on ATN wrt ACK, we have * to get the MESH to drop ACK, wait until REQ gets * asserted, then drop ATN. To do this we first * issue a SEQ_MSGOUT with ATN and wait for REQ, * then change the command to a SEQ_MSGOUT w/o ATN. * If we don't see REQ in a reasonable time, we * change the command to SEQ_MSGIN with ATN, * wait for the phase mismatch interrupt, then * issue the SEQ_MSGOUT without ATN.
*/
out_8(&mr->count_lo, 1);
out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg + SEQ_ATN);
t = 30; /* wait up to 30us */ while ((in_8(&mr->bus_status0) & BS0_REQ) == 0 && --t >= 0)
udelay(1);
dlog(ms, "last_mbyte err/exc/fc/cl=%.8x",
MKWORD(mr->error, mr->exception,
mr->fifo_count, mr->count_lo)); if (in_8(&mr->interrupt) & (INT_ERROR | INT_EXCEPTION)) { /* whoops, target didn't do what we expected */
ms->last_n_msgout = ms->n_msgout;
ms->n_msgout = 0; if (in_8(&mr->interrupt) & INT_ERROR) {
printk(KERN_ERR "mesh: error %x in msg_out\n",
in_8(&mr->error));
handle_error(ms); return;
} if (in_8(&mr->exception) != EXC_PHASEMM)
printk(KERN_ERR "mesh: exc %x in msg_out\n",
in_8(&mr->exception)); else
printk(KERN_DEBUG "mesh: bs0=%x in msg_out\n",
in_8(&mr->bus_status0));
handle_exception(ms); return;
} if (in_8(&mr->bus_status0) & BS0_REQ) {
out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
mesh_flush_io(mr);
udelay(1);
out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
ms->msgphase = msg_out_last;
} else {
out_8(&mr->sequence, SEQ_MSGIN + use_active_neg + SEQ_ATN);
ms->msgphase = msg_out_xxx;
} break;
case msg_none: switch (ms->phase) { case idle:
printk(KERN_ERR "mesh: interrupt in idle phase?\n");
dumpslog(ms); return; case selecting:
dlog(ms, "Selecting phase at command completion",0);
ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt),
(cmd? cmd->device->lun: 0));
ms->n_msgout = 1;
ms->expect_reply = 0; if (ms->aborting) {
ms->msgout[0] = ABORT;
ms->n_msgout++;
} elseif (tp->sdtr_state == do_sdtr) { /* add SDTR message */
add_sdtr_msg(ms);
ms->expect_reply = 1;
tp->sdtr_state = sdtr_sent;
}
ms->msgphase = msg_out; /* * We need to wait for REQ before dropping ATN. * We wait for at most 30us, then fall back to * a scheme where we issue a SEQ_COMMAND with ATN, * which will give us a phase mismatch interrupt * when REQ does come, and then we send the message.
*/
t = 230; /* wait up to 230us */ while ((in_8(&mr->bus_status0) & BS0_REQ) == 0) { if (--t < 0) {
dlog(ms, "impatient for req", ms->n_msgout);
ms->msgphase = msg_none; break;
}
udelay(1);
} break; case dataing: if (ms->dma_count != 0) {
start_phase(ms); return;
} /* * We can get a phase mismatch here if the target * changes to the status phase, even though we have * had a command complete interrupt. Then, if we * issue the SEQ_STATUS command, we'll get a sequence * error interrupt. Which isn't so bad except that * occasionally the mesh actually executes the * SEQ_STATUS *as well as* giving us the sequence * error and phase mismatch exception.
*/
out_8(&mr->sequence, 0);
out_8(&mr->interrupt,
INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
halt_dma(ms); break; case statusing: if (cmd) { struct mesh_cmd_priv *mcmd = mesh_priv(cmd);
mcmd->status = mr->fifo; if (DEBUG_TARGET(cmd))
printk(KERN_DEBUG "mesh: status is %x\n",
mcmd->status);
}
ms->msgphase = msg_in; break; case busfreeing:
mesh_done(ms, 1); return; case disconnecting:
ms->current_req = NULL;
ms->phase = idle;
mesh_start(ms); return; default: break;
}
++ms->phase;
start_phase(ms); break;
}
}
/* * Called by midlayer with host locked to queue a new * request
*/ staticint mesh_queue_lck(struct scsi_cmnd *cmd)
{ struct mesh_state *ms;
cmd->host_scribble = NULL;
ms = (struct mesh_state *) cmd->device->host->hostdata;
/* * Called to handle interrupts, either call by the interrupt * handler (do_mesh_interrupt) or by other functions in * exceptional circumstances
*/ staticvoid mesh_interrupt(struct mesh_state *ms)
{ volatilestruct mesh_regs __iomem *mr = ms->mesh; int intr;
/* 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.
*/ staticint mesh_abort(struct scsi_cmnd *cmd)
{ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
/* * 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
*/ staticint mesh_host_reset(struct scsi_cmnd *cmd)
{ struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata; volatilestruct mesh_regs __iomem *mr = ms->mesh; volatilestruct dbdma_regs __iomem *md = ms->dma; unsignedlong flags;
switch (mesg.event) { case PM_EVENT_SUSPEND: case PM_EVENT_HIBERNATE: case PM_EVENT_FREEZE: break; default: return 0;
} if (ms->phase == sleeping) return 0;
/* * If we leave drives set for synchronous transfers (especially * CDROMs), and reboot to MacOS, it gets confused, poor thing. * So, on reboot we reset the SCSI bus.
*/ staticint mesh_shutdown(struct macio_dev *mdev)
{ struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); volatilestruct mesh_regs __iomem *mr; unsignedlong flags;
/* Space for dma command list: +1 for stop command, * +1 to allow for aligning.
*/
ms->dma_cmd_size = (mesh_host->sg_tablesize + 2) * sizeof(struct dbdma_cmd);
/* We use the PCI APIs for now until the generic one gets fixed * enough or until we get some macio-specific versions
*/
dma_cmd_space = dma_alloc_coherent(&macio_get_pci_dev(mdev)->dev,
ms->dma_cmd_size, &dma_cmd_bus,
GFP_KERNEL); if (dma_cmd_space == NULL) {
printk(KERN_ERR "mesh: can't allocate DMA table\n"); goto out_unmap;
}
out_release_irq:
free_irq(ms->meshintr, ms);
out_shutdown: /* shutdown & reset bus in case of error or macos can be confused * at reboot if the bus was set to synchronous mode already
*/
mesh_shutdown(mdev);
set_mesh_power(ms, 0);
dma_free_coherent(&macio_get_pci_dev(mdev)->dev, ms->dma_cmd_size,
ms->dma_cmd_space, ms->dma_cmd_bus);
out_unmap:
iounmap(ms->dma);
iounmap(ms->mesh);
out_free:
scsi_host_put(mesh_host);
out_release:
macio_release_resources(mdev);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.