/*
* Copyright (C) 1996 Scott Reynolds. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This file contains only the machine-dependent parts of the mac68k
* NCR 5380 SCSI driver. (Autoconfig stuff and PDMA functions.)
* The machine-independent parts are in ncr5380sbc.c
*
* Supported hardware includes:
* Macintosh II family 5380-based controller
*
* Credits, history:
*
* Scott Reynolds wrote this module, based on work by Allen Briggs
* (mac68k), Gordon W. Ross and David Jones (sun3), and Leo Weppelman
* (atari). Thanks to Allen for supplying crucial interpretation of the
* NetBSD/mac68k 1.1 'ncrscsi' driver. Also, Allen, Gordon, and Jason
* Thorpe all helped to refine this code, and were considerable sources
* of moral support.
*/
static int sbc_wait_busy(struct ncr5380_softc *);
static int sbc_ready(struct ncr5380_softc *);
static int sbc_wait_dreq(struct ncr5380_softc *);
/***
* General support for Mac-specific SCSI logic.
***/
/* These are used in the following inline functions. */
int sbc_wait_busy_timo = 1000 * 5000; /* X2 = 10 S. */
int sbc_ready_timo = 1000 * 5000; /* X2 = 10 S. */
int sbc_wait_dreq_timo = 1000 * 5000; /* X2 = 10 S. */
/* Return zero on success. */
static inline int
sbc_wait_busy(struct ncr5380_softc *sc)
{
int timo = sbc_wait_busy_timo;
for (;;) {
if (SCI_BUSY(sc)) {
timo = 0; /* return 0 */
break;
}
if (--timo < 0)
break; /* return -1 */
delay(2);
}
return (timo);
}
static inline int
sbc_ready(struct ncr5380_softc *sc)
{
int timo = sbc_ready_timo;
for (;;) {
if ((*sc->sci_csr & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH))
== (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) {
timo = 0;
break;
}
if (((*sc->sci_csr & SCI_CSR_PHASE_MATCH) == 0)
|| (SCI_BUSY(sc) == 0)) {
timo = -1;
break;
}
if (--timo < 0)
break; /* return -1 */
delay(2);
}
return (timo);
}
static inline int
sbc_wait_dreq(struct ncr5380_softc *sc)
{
int timo = sbc_wait_dreq_timo;
for (;;) {
if ((*sc->sci_csr & (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH))
== (SCI_CSR_DREQ|SCI_CSR_PHASE_MATCH)) {
timo = 0;
break;
}
if (--timo < 0)
break; /* return -1 */
delay(2);
}
return (timo);
}
/*
* Setup for a possible bus error caused by SCSI controller
* switching out of DATA OUT before we're done with the
* current transfer. (See comment before sbc_drq_intr().)
*/
nofault = &faultbuf;
if (setjmp(nofault)) {
goto interrupt;
}
/*
* Setup for a possible bus error caused by SCSI controller
* switching out of DATA OUT before we're done with the
* current transfer. (See comment before sbc_drq_intr().)
*/
nofault = &faultbuf;
if (setjmp(nofault)) {
printf("buf = 0x%lx, fault = 0x%lx\n",
(u_long)sc->sc_drq_addr, (u_long)m68k_fault_addr);
panic("Unexpected bus error in sbc_pdma_out()");
}
#define W1 *byte_data = *(u_int8_t *)data, data += 1
#define W4 *long_data = *(u_int32_t *)data, data += 4
for (resid = datalen; resid >= 64; resid -= 60) {
if (sbc_ready(ncr_sc))
goto interrupt;
W1;
resid--;
if (sbc_ready(ncr_sc))
goto interrupt;
W1;
resid--;
if (sbc_ready(ncr_sc))
goto interrupt;
W1;
resid--;
if (sbc_ready(ncr_sc))
goto interrupt;
W1;
resid--;
if (sbc_ready(ncr_sc))
goto interrupt;
W4; W4; W4; W4;
W4; W4; W4; W4;
W4; W4; W4; W4;
W4; W4; W4;
}
while (resid) {
if (sbc_ready(ncr_sc))
goto interrupt;
W1;
resid--;
}
#undef W1
#undef W4
if (sbc_wait_dreq(ncr_sc))
printf("%s: timeout waiting for DREQ.\n",
device_xname(ncr_sc->sc_dev));
/***
* The following code implements interrupt-driven PDMA.
***/
/*
* This is the meat of the PDMA transfer.
* When we get here, we shove data as fast as the mac can take it.
* We depend on several things:
* * All macs after the Mac Plus that have a 5380 chip should have a general
* logic IC that handshakes data for blind transfers.
* * If the SCSI controller finishes sending/receiving data before we do,
* the same general logic IC will generate a /BERR for us in short order.
* * The fault address for said /BERR minus the base address for the
* transfer will be the amount of data that was actually written.
*
* We use the nofault flag and the setjmp/longjmp in locore.s so we can
* detect and handle the bus error for early termination of a command.
* This is usually caused by a disconnecting target.
*/
void
sbc_drq_intr(void *p)
{
struct sbc_softc *sc = (struct sbc_softc *)p;
struct ncr5380_softc *ncr_sc = (struct ncr5380_softc *)p;
struct sci_req *sr = ncr_sc->sc_current;
struct sbc_pdma_handle *dh = sr->sr_dma_hand;
label_t faultbuf;
volatile u_int32_t *long_drq;
u_int32_t *long_data;
volatile u_int8_t *drq = 0; /* XXX gcc4 -Wuninitialized */
u_int8_t *data;
int count, dcount, s;
/*
* If we're not ready to xfer data, or have no more, just return.
*/
if (sbc_ready(ncr_sc) || dh->dh_len == 0)
return;
#ifdef SBC_DEBUG
if (sbc_debug & SBC_DB_INTR)
printf("%s: drq intr, dh_len=0x%x, dh_flags=0x%x\n",
device_xname(ncr_sc->sc_dev), dh->dh_len, dh->dh_flags);
#endif
mutex_enter(&sc->sc_drq_lock);
s = splbio();
/*
* Setup for a possible bus error caused by SCSI controller
* switching out of DATA-IN/OUT before we're done with the
* current transfer.
*/
nofault = &faultbuf;
#define W1 *drq++ = *data++
while (count) {
W1; count--;
}
#undef W1
dh->dh_len -= dcount;
dh->dh_addr += dcount;
}
dh->dh_flags |= SBC_DH_DONE;
if (dcount >= MAX_DMA_LEN)
drq = (volatile u_int8_t *)sc->sc_drq_addr;
/*
* Write an extra byte to handle last ack.
* From NCR5380 Interface manual.
*/
if (*ncr_sc->sci_csr & SCI_CSR_ACK)
*drq = 0;
/*
* XXX -- Read a byte from the SBC to trigger a /BERR.
* This seems to be necessary for us to notice that
* the target has disconnected. Ick. 06 jun 1996 (sr)
* Unsure if this is still necessary - See comment above.
*/
(void)*drq;
} else { /* Data In */
/*
* Get the dest address aligned.
*/
dcount =
count = uimin(dh->dh_len, 4 - (((int)dh->dh_addr) & 0x3));
if (count && count < 4) {
data = (u_int8_t *)dh->dh_addr;
drq = (volatile u_int8_t *)sc->sc_drq_addr;
while (count) {
*data++ = *drq++;
count--;
}
dh->dh_addr += dcount;
dh->dh_len -= dcount;
}
#ifdef DIAGNOSTIC
if (sr->sr_dma_hand != NULL)
panic("sbc_dma_alloc: already have PDMA handle");
#endif
/* Polled transfers shouldn't allocate a PDMA handle. */
if (sr->sr_flags & SR_IMMED)
return;
xlen = ncr_sc->sc_datalen;
/* Make sure our caller checked sc_min_dma_len. */
if (xlen < MIN_DMA_LEN)
panic("sbc_dma_alloc: len=0x%x", xlen);
/*
* Find free PDMA handle. Guaranteed to find one since we
* have as many PDMA handles as the driver has processes.
* (instances?)
*/
for (i = 0; i < SCI_OPENINGS; i++) {
if ((sc->sc_pdma[i].dh_flags & SBC_DH_BUSY) == 0)
goto found;
}
panic("sbc: no free PDMA handles");
found:
dh = &sc->sc_pdma[i];
dh->dh_flags = SBC_DH_BUSY;
dh->dh_addr = ncr_sc->sc_dataptr;
dh->dh_len = xlen;
/* Copy the 'write' flag for convenience. */
if (xs->xs_control & XS_CTL_DATA_OUT)
dh->dh_flags |= SBC_DH_OUT;
/*
* We shouldn't arrive here; if SR_IMMED is set, then
* dma_alloc() should have refused to allocate a handle
* for the transfer. This forces the polled PDMA code
* to handle the request...
*/
#ifdef SBC_DEBUG
if (sbc_debug & SBC_DB_DMA)
printf("%s: lost DRQ interrupt?\n",
device_xname(ncr_sc->sc_dev));
#endif
sr->sr_flags |= SR_OVERDUE;
}
void
sbc_dma_setup(struct ncr5380_softc *ncr_sc)
{
/* Not needed; we don't have real DMA */
}