#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mac68k5380.c,v 1.51 2026/04/26 10:52:15 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/syslog.h>
#include <sys/buf.h>
#include <uvm/uvm_extern.h>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsi_message.h>
#include <dev/scsipi/scsiconf.h>
#include "ncr5380reg.h"
#include <machine/cpu.h>
#include <machine/viareg.h>
#include <mac68k/dev/ncr5380var.h>
#define NREQ 18
#define AUTO_SENSE 1
#define DRNAME ncrscsi
#undef DBG_SEL
#undef DBG_REQ
#undef DBG_NOWRITE
#undef DBG_PIO
#undef DBG_INF
#define DBG_NOSTATIC
#define DBG_PID 25
#ifdef DBG_NOSTATIC
# define static
#endif
#ifdef DBG_SEL
# define DBG_SELPRINT(a,b) printf(a,b)
#else
# define DBG_SELPRINT(a,b)
#endif
#ifdef DBG_PIO
# define DBG_PIOPRINT(a,b,c) printf(a,b,c)
#else
# define DBG_PIOPRINT(a,b,c)
#endif
#ifdef DBG_INF
# define DBG_INFPRINT(a,b,c) a(b,c)
#else
# define DBG_INFPRINT(a,b,c)
#endif
#ifdef DBG_PID
static const char *last_hit[DBG_PID];
# define PID(a) \
{ int i; \
for (i = 0; i < DBG_PID - 1; i++) \
last_hit[i] = last_hit[i + 1]; \
last_hit[DBG_PID - 1] = a; }
#else
# define PID(a)
#endif
#undef REAL_DMA
#define scsi_ipending() (GET_5380_REG(NCR5380_DMSTAT) & SC_IRQ_SET)
#define fair_to_keep_dma() 1
#define claimed_dma() 1
#define reconsider_dma()
#define USE_PDMA 1
#define MIN_PHYS 0x2000
#define ENABLE_NCR5380(sc) cur_softc = sc;
static struct ncr_softc *cur_softc;
struct scsi_5380 {
volatile u_char scsi_5380[8*16];
};
extern vaddr_t SCSIBase;
static volatile u_char *ncr = (volatile u_char *) 0x10000;
static volatile u_char *ncr_5380_with_drq = (volatile u_char *) 0x6000;
static volatile u_char *ncr_5380_without_drq = (volatile u_char *) 0x12000;
#define SCSI_5380 ((volatile struct scsi_5380 *) ncr)
#define GET_5380_REG(rnum) SCSI_5380->scsi_5380[((rnum)<<4)]
#define SET_5380_REG(rnum,val) (SCSI_5380->scsi_5380[((rnum)<<4)] = (val))
static void ncr5380_irq_intr(void *);
static void ncr5380_drq_intr(void *);
static void do_ncr5380_drq_intr(void *);
static void scsi_clr_ipend(void);
static void scsi_mach_init(struct ncr_softc *);
static int machine_match(device_t, cfdata_t, void *,
struct cfdriver *);
static int pdma_ready(void);
static int transfer_pdma(u_char *, u_char *, u_long *);
static void
scsi_clr_ipend(void)
{
GET_5380_REG(NCR5380_IRCV);
scsi_clear_irq();
}
static void
scsi_mach_init(struct ncr_softc *sc)
{
static int initted = 0;
if (initted++)
panic("scsi_mach_init called again.");
ncr = (volatile u_char *)
(SCSIBase + (u_long) ncr);
ncr_5380_with_drq = (volatile u_char *)
(SCSIBase + (u_int) ncr_5380_with_drq);
ncr_5380_without_drq = (volatile u_char *)
(SCSIBase + (u_int) ncr_5380_without_drq);
if (VIA2 == VIA2OFF) {
scsi_enable = Via1Base + VIA2 * 0x2000 + vIER;
scsi_flag = Via1Base + VIA2 * 0x2000 + vIFR;
} else {
scsi_enable = Via1Base + VIA2 * 0x2000 + rIER;
scsi_flag = Via1Base + VIA2 * 0x2000 + rIFR;
}
via2_register_irq(VIA2_SCSIIRQ, ncr5380_irq_intr, sc);
via2_register_irq(VIA2_SCSIDRQ, ncr5380_drq_intr, sc);
}
static int
machine_match(device_t parent, cfdata_t cf, void *aux,
struct cfdriver *cd)
{
if (!mac68k_machine.scsi80)
return 0;
return 1;
}
#if USE_PDMA
int pdma_5380_dir = 0;
u_char *pending_5380_data;
u_long pending_5380_count;
#define NCR5380_PDMA_DEBUG 1
#if NCR5380_PDMA_DEBUG
int pdma_5380_sends = 0;
int pdma_5380_bytes = 0;
void
pdma_stat(void)
{
printf("PDMA SCSI: %d xfers completed for %d bytes.\n",
pdma_5380_sends, pdma_5380_bytes);
printf("pdma_5380_dir = %d\t",
pdma_5380_dir);
printf("datap = %p, remainder = %ld.\n",
pending_5380_data, pending_5380_count);
scsi_show();
}
#endif
void
pdma_cleanup(void)
{
SC_REQ *reqp = connected;
int s;
s = splbio();
PID("pdma_cleanup0");
pdma_5380_dir = 0;
#if NCR5380_PDMA_DEBUG
pdma_5380_sends++;
pdma_5380_bytes+=(reqp->xdata_len - pending_5380_count);
#endif
reqp->xdata_ptr += reqp->xdata_len - pending_5380_count;
reqp->xdata_len = pending_5380_count;
SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE) & ~SC_M_DMA);
scsi_clr_ipend();
reqp->dr_flag &= ~DRIVER_IN_DMA;
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
SET_5380_REG(NCR5380_ICOM, 0);
splx(s);
PID("pdma_cleanup1");
run_main(cur_softc);
PID("pdma_cleanup2");
}
#endif
static int
pdma_ready(void)
{
#if USE_PDMA
SC_REQ *reqp = connected;
int dmstat, idstat;
extern u_char ncr5380_no_parchk;
PID("pdma_ready0");
if (pdma_5380_dir) {
PID("pdma_ready1.");
dmstat = GET_5380_REG(NCR5380_DMSTAT);
idstat = GET_5380_REG(NCR5380_IDSTAT);
if ( ((dmstat & (0xff & ~SC_ATN_STAT)) == SC_IRQ_SET)
&& ((idstat & (SC_S_BSY|SC_S_REQ))
== (SC_S_BSY | SC_S_REQ)) ) {
PID("pdma_ready2");
pdma_cleanup();
return 1;
} else if (PH_IN(reqp->phase) && (dmstat & SC_PAR_ERR)) {
if (!(ncr5380_no_parchk & (1 << reqp->targ_id)))
reqp->xs->error = XS_DRIVER_STUFFUP;
PID("pdma_ready3");
pdma_cleanup();
return 1;
} else if ( !(idstat & SC_S_REQ)
|| (((idstat>>2) & 7) != reqp->phase)) {
#ifdef DIAGNOSTIC
scsi_show();
printf("Unexpected phase change.\n");
#endif
reqp->xs->error = XS_DRIVER_STUFFUP;
pdma_cleanup();
return 1;
} else {
scsi_show();
panic("Spurious interrupt during PDMA xfer.");
}
} else
PID("pdma_ready4");
#endif
return 0;
}
static void
ncr5380_irq_intr(void *p)
{
PID("irq");
#if USE_PDMA
if (pdma_ready()) {
return;
}
#endif
scsi_idisable();
ncr_ctrl_intr(cur_softc);
}
static void
do_ncr5380_drq_intr(void *p)
{
#if USE_PDMA
extern int m68k_fault_addr;
label_t faultbuf;
register int count;
volatile u_int32_t *long_drq;
u_int32_t *long_data;
volatile u_int8_t *drq;
u_int8_t *data;
#if DBG_PID
if (pdma_5380_dir == 2) {
PID("drq (in)");
} else {
PID("drq (out)");
}
#endif
nofault = &faultbuf;
if (setjmp(nofault)) {
PID("drq berr");
nofault = NULL;
count = ( (u_long) m68k_fault_addr
- (u_long) ncr_5380_with_drq);
if ((count < 0) || (count > pending_5380_count)) {
printf("pdma %s: cnt = %d (0x%x) (pending cnt %ld)\n",
(pdma_5380_dir == 2) ? "in" : "out",
count, count, pending_5380_count);
panic("something is wrong");
}
pending_5380_data += count;
pending_5380_count -= count;
m68k_fault_addr = 0;
PID("end drq early");
return;
}
if (pdma_5380_dir == 2) {
int resid;
resid = count = uimin(pending_5380_count,
4 - (((int) pending_5380_data) & 0x3));
if (count && (count < 4)) {
data = (u_int8_t *) pending_5380_data;
drq = (volatile u_int8_t *) ncr_5380_with_drq;
while (count) {
*data++ = *drq++;
count--;
}
pending_5380_data += resid;
pending_5380_count -= resid;
}
while (pending_5380_count) {
int dcount;
dcount = count = uimin(pending_5380_count, MIN_PHYS);
long_drq = (volatile u_int32_t *) ncr_5380_with_drq;
long_data = (u_int32_t *) pending_5380_data;
#define R4 *long_data++ = *long_drq++
while ( count > 64 ) {
R4; R4; R4; R4; R4; R4; R4; R4;
R4; R4; R4; R4; R4; R4; R4; R4;
count -= 64;
}
while (count > 8) {
R4; R4; count -= 8;
}
#undef R4
data = (u_int8_t *) long_data;
drq = (volatile u_int8_t *) long_drq;
while (count) {
*data++ = *drq++;
count--;
}
pending_5380_count -= dcount;
pending_5380_data += dcount;
}
} else {
int resid;
resid = count = uimin(pending_5380_count,
4 - (((int) pending_5380_data) & 0x3));
if (count && (count < 4)) {
data = (u_int8_t *) pending_5380_data;
drq = (volatile u_int8_t *) ncr_5380_with_drq;
while (count) {
#define W1 *drq++ = *data++
W1; count--;
#undef W1
}
pending_5380_data += resid;
pending_5380_count -= resid;
}
while (pending_5380_count) {
int dcount;
dcount = count = uimin(pending_5380_count, MIN_PHYS);
long_drq = (volatile u_int32_t *) ncr_5380_with_drq;
long_data = (u_int32_t *) pending_5380_data;
#define W4 *long_drq++ = *long_data++
while ( count > 64 ) {
W4; W4; W4; W4; W4; W4; W4; W4;
W4; W4; W4; W4; W4; W4; W4; W4;
count -= 64;
}
while ( count > 8 ) {
W4; W4;
count -= 8;
}
#undef W4
data = (u_int8_t *) long_data;
drq = (volatile u_int8_t *) long_drq;
while (count) {
#define W1 *drq++ = *data++
W1; count--;
#undef W1
}
pending_5380_count -= dcount;
pending_5380_data += dcount;
}
PID("write complete");
(void)*((volatile u_int8_t *) ncr_5380_with_drq);
PID("read a byte to force a phase change");
}
nofault = NULL;
PID("end drq");
return;
#else
return;
#endif
}
static void
ncr5380_drq_intr(void *p)
{
while (GET_5380_REG(NCR5380_DMSTAT) & SC_DMA_REQ) {
do_ncr5380_drq_intr(p);
scsi_clear_drq();
}
}
#if USE_PDMA
#define SCSI_TIMEOUT_VAL 10000000
static int
transfer_pdma(u_char *phasep, u_char *data, u_long *count)
{
SC_REQ *reqp = connected;
int len = *count, s, scsi_timeout = SCSI_TIMEOUT_VAL;
if (pdma_5380_dir) {
panic("ncrscsi: transfer_pdma called when operation already "
"pending.");
}
PID("transfer_pdma0")
if (reqp->dr_flag & DRIVER_NOINT) {
PID("pdma, falling back to transfer_pio.")
transfer_pio(phasep, data, count, 0);
return -1;
}
s = splbio();
scsi_idisable();
SET_5380_REG(NCR5380_TCOM, *phasep);
scsi_clr_ipend();
while ( ((GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY) == 0)
&& (--scsi_timeout) );
if (!scsi_timeout) {
#if DIAGNOSTIC
printf("scsi timeout: waiting for BSY in %s.\n",
(*phasep == PH_DATAOUT) ? "pdma_out" : "pdma_in");
#endif
goto scsi_timeout_error;
}
reqp->dr_flag |= DRIVER_IN_DMA;
pending_5380_data = data;
pending_5380_count = len;
switch (*phasep) {
default:
panic("Unexpected phase in transfer_pdma.");
case PH_DATAOUT:
pdma_5380_dir = 1;
SET_5380_REG(NCR5380_ICOM, GET_5380_REG(NCR5380_ICOM)|SC_ADTB);
SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE)|SC_M_DMA);
SET_5380_REG(NCR5380_DMSTAT, 0);
break;
case PH_DATAIN:
pdma_5380_dir = 2;
SET_5380_REG(NCR5380_ICOM, 0);
SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE)|SC_M_DMA);
SET_5380_REG(NCR5380_IRCV, 0);
break;
}
PID("waiting for interrupt.")
scsi_ienable();
splx(s);
return 0;
scsi_timeout_error:
SET_5380_REG(NCR5380_MODE, GET_5380_REG(NCR5380_MODE) & ~SC_M_DMA);
return -1;
}
#endif
#include <mac68k/dev/ncr5380.c>