#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: iop.c,v 1.12 2023/12/20 00:40:43 thorpej Exp $");
#include "opt_mac68k.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <sys/queue.h>
#include <sys/systm.h>
#include <machine/cpu.h>
#include <machine/frame.h>
#include <machine/iopreg.h>
#include <machine/viareg.h>
static IOP mac68k_iops[2];
static void iopism_hand(void *);
static void load_msg_to_iop(IOPHW *, struct iop_msg *);
static void iop_message_sent(IOP *, int);
static void receive_iop_message(IOP *, int);
static void default_listener(IOP *, struct iop_msg *);
static inline int iop_alive(IOPHW *);
static inline int iop_read1(IOPHW *, u_long);
static inline void iop_write1(IOPHW *, u_long, u_char);
static inline void _iop_upload(IOPHW *, u_char *, u_long, u_long);
static inline void _iop_download(IOPHW *, u_char *, u_long, u_long);
static inline int
iop_read1(IOPHW *ioph, u_long iopbase)
{
IOP_LOADADDR(ioph, iopbase);
return ioph->data;
}
static inline void
iop_write1(IOPHW *ioph, u_long iopbase, u_char data)
{
IOP_LOADADDR(ioph, iopbase);
ioph->data = data;
}
static inline int
iop_alive(IOPHW *ioph)
{
int alive;
alive = iop_read1(ioph, IOP_ADDR_ALIVE);
iop_write1(ioph, IOP_ADDR_ALIVE, 0);
return alive;
}
static void
default_listener(IOP *iop, struct iop_msg *msg)
{
printf("unsolicited message on channel %d.\n", msg->channel);
}
void
iop_init(int fullinit)
{
IOPHW *ioph;
IOP *iop;
int i, ii;
switch (current_mac_model->machineid) {
default:
return;
case MACH_MACQ900:
case MACH_MACQ950:
mac68k_iops[SCC_IOP].iop = (IOPHW *)
((u_char *)IOBase + 0xc000);
mac68k_iops[ISM_IOP].iop = (IOPHW *)
((u_char *)IOBase + 0x1e000);
break;
case MACH_MACIIFX:
mac68k_iops[SCC_IOP].iop = (IOPHW *)
((u_char *)IOBase + 0x4000);
mac68k_iops[ISM_IOP].iop = (IOPHW *)
((u_char *)IOBase + 0x12000);
break;
}
if (!fullinit) {
ioph = mac68k_iops[SCC_IOP].iop;
ioph->control_status = 0;
ioph->control_status = IOP_BYPASS;
ioph = mac68k_iops[ISM_IOP].iop;
ioph->control_status = 0;
return;
}
for (ii = 0 ; ii < 2 ; ii++) {
iop = &mac68k_iops[ii];
ioph = iop->iop;
for (i = 0; i < IOP_MAXCHAN; i++) {
SIMPLEQ_INIT(&iop->sendq[i]);
SIMPLEQ_INIT(&iop->recvq[i]);
iop->listeners[i] = default_listener;
iop->listener_data[i] = NULL;
}
}
switch (current_mac_model->machineid) {
default:
return;
case MACH_MACQ900:
case MACH_MACQ950:
#ifdef notyet_maybe_not_ever
iop = &mac68k_iops[SCC_IOP];
intr_establish(iopscc_hand, iop, 4);
#endif
iop = &mac68k_iops[ISM_IOP];
via2_register_irq(0, iopism_hand, iop);
via_reg(VIA2, vIER) = 0x81;
via_reg(VIA2, vIFR) = 0x01;
break;
case MACH_MACIIFX:
break;
}
iop = &mac68k_iops[SCC_IOP];
ioph = iop->iop;
printf("SCC IOP base: 0x%x\n", (unsigned) ioph);
pool_init(&iop->pool, sizeof(struct iop_msg), 0, 0, 0, "mac68k_iop1",
NULL, IPL_NONE);
ioph->control_status = IOP_BYPASS;
iop = &mac68k_iops[ISM_IOP];
ioph = iop->iop;
printf("ISM IOP base: 0x%x, alive %x\n", (unsigned) ioph,
(unsigned) iop_alive(ioph));
pool_init(&iop->pool, sizeof(struct iop_msg), 0, 0, 0, "mac68k_iop2",
NULL, IPL_NONE);
iop_write1(ioph, IOP_ADDR_ALIVE, 0);
printf("OLD cs0: 0x%x\n", (unsigned) ioph->control_status);
ioph->control_status = IOP_CS_RUN | IOP_CS_AUTOINC;
{unsigned cs, c2;
cs = (unsigned) ioph->control_status;
printf("OLD cs1: 0x%x\n", cs);
cs = 0;
do { c2 = iop_read1(ioph, IOP_ADDR_ALIVE); cs++; } while (c2 != 0xff);
printf("OLD cs2: 0x%x (i = %d)\n", (unsigned) ioph->control_status, cs);
}
}
static inline void
_iop_upload(IOPHW *ioph, u_char *mem, u_long nb, u_long iopbase)
{
IOP_LOADADDR(ioph, iopbase);
while (nb--) {
ioph->data = *mem++;
}
}
void
iop_upload(int iopn, u_char *mem, u_long nb, u_long iopbase)
{
IOPHW *ioph;
if (iopn & ~1) return;
ioph = mac68k_iops[iopn].iop;
if (!ioph) return;
_iop_upload(ioph, mem, nb, iopbase);
}
static inline void
_iop_download(IOPHW *ioph, u_char *mem, u_long nb, u_long iopbase)
{
IOP_LOADADDR(ioph, iopbase);
while (nb--) {
*mem++ = ioph->data;
}
}
void
iop_download(int iopn, u_char *mem, u_long nb, u_long iopbase)
{
IOPHW *ioph;
if (iopn & ~1) return;
ioph = mac68k_iops[iopn].iop;
if (!ioph) return;
_iop_download(ioph, mem, nb, iopbase);
}
static void
iopism_hand(void *arg)
{
IOP *iop;
IOPHW *ioph;
u_char cs;
u_char m, s;
int i;
iop = (IOP *) arg;
ioph = iop->iop;
cs = ioph->control_status;
printf("iopism_hand.\n");
#if DIAGNOSTIC
if ((cs & IOP_INTERRUPT) == 0) {
printf("IOP_ISM interrupt--no interrupt!? (cs 0x%x)\n",
(u_int) cs);
}
#endif
if (cs & IOP_CS_INT0) {
ioph->control_status |= IOP_CS_INT0;
m = iop_read1(ioph, IOP_ADDR_MAX_SEND_CHAN);
for (i = 0; i < m; i++) {
s = iop_read1(ioph, IOP_ADDR_SEND_STATE + i);
if (s == IOP_MSG_COMPLETE) {
iop_message_sent(iop, i);
}
}
}
if (cs & IOP_CS_INT1) {
ioph->control_status |= IOP_CS_INT1;
m = iop_read1(ioph, IOP_ADDR_MAX_RECV_CHAN);
for (i = 0; i < m; i++) {
s = iop_read1(ioph, IOP_ADDR_RECV_STATE + i);
if (s == IOP_MSG_NEW) {
receive_iop_message(iop, i);
}
}
}
}
static void
load_msg_to_iop(IOPHW *ioph, struct iop_msg *msg)
{
int offset;
msg->status = IOP_MSGSTAT_SENDING;
offset = IOP_ADDR_SEND_MSG + msg->channel * IOP_MSGLEN;
_iop_upload(ioph, msg->msg, IOP_MSGLEN, offset);
iop_write1(ioph, IOP_ADDR_SEND_STATE + msg->channel, IOP_MSG_NEW);
ioph->control_status = (ioph->control_status & 0xfe) | IOP_CS_IRQ;
}
static void
iop_message_sent(IOP *iop, int chan)
{
IOPHW *ioph;
struct iop_msg *msg;
ioph = iop->iop;
msg = SIMPLEQ_FIRST(&iop->sendq[chan]);
msg->status = IOP_MSGSTAT_SENT;
SIMPLEQ_REMOVE_HEAD(&iop->sendq[chan], iopm);
msg->handler(iop, msg);
pool_put(&iop->pool, msg);
if (!(msg = SIMPLEQ_FIRST(&iop->sendq[chan]))) {
iop_write1(ioph, IOP_ADDR_SEND_STATE + chan, IOP_MSG_IDLE);
} else {
load_msg_to_iop(ioph, msg);
}
}
static void
receive_iop_message(IOP *iop, int chan)
{
IOPHW *ioph;
struct iop_msg *msg;
int offset;
ioph = iop->iop;
msg = SIMPLEQ_FIRST(&iop->recvq[chan]);
if (msg) {
SIMPLEQ_REMOVE_HEAD(&iop->recvq[chan], iopm);
} else {
msg = &iop->unsolicited_msg;
msg->channel = chan;
msg->handler = iop->listeners[chan];
msg->user_data = iop->listener_data[chan];
}
offset = IOP_ADDR_RECV_MSG + chan * IOP_MSGLEN;
_iop_download(ioph, msg->msg, IOP_MSGLEN, offset);
msg->status = IOP_MSGSTAT_RECEIVED;
msg->handler(iop, msg);
if (msg != &iop->unsolicited_msg)
pool_put(&iop->pool, msg);
iop_write1(ioph, IOP_ADDR_RECV_STATE + chan, IOP_MSG_COMPLETE);
ioph->control_status |= IOP_CS_IRQ;
if ((msg = SIMPLEQ_FIRST(&iop->recvq[chan])) != NULL) {
msg->status = IOP_MSGSTAT_RECEIVING;
}
}
int
iop_send_msg(int iopn, int chan, u_char *mesg, int msglen,
iop_msg_handler handler, void *user_data)
{
struct iop_msg *msg;
IOP *iop;
int s;
if (iopn & ~1) return -1;
iop = &mac68k_iops[iopn];
if (!iop) return -1;
if (msglen > IOP_MSGLEN) return -1;
msg = (struct iop_msg *) pool_get(&iop->pool, PR_WAITOK);
if (msg == NULL) return -1;
printf("have msg buffer for IOP: %#x\n", (unsigned) iop->iop);
msg->channel = chan;
if (msglen < IOP_MSGLEN) memset(msg->msg, '\0', IOP_MSGLEN);
memcpy(msg->msg, mesg, msglen);
msg->handler = handler;
msg->user_data = user_data;
msg->status = IOP_MSGSTAT_QUEUED;
s = splhigh();
SIMPLEQ_INSERT_TAIL(&iop->sendq[chan], msg, iopm);
if (msg == SIMPLEQ_FIRST(&iop->sendq[chan])) {
msg->status = IOP_MSGSTAT_SENDING;
printf("loading msg to iop: cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR));
load_msg_to_iop(iop->iop, msg);
printf("msg loaded to iop: cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR));
}
{int i; for (i=0;i<16;i++) {
printf(" cs: 0x%x V1-%x- ", (unsigned) iop->iop->control_status, (unsigned)via_reg(VIA1, vIFR));
delay(1000);
}}
splx(s);
return 0;
}
int
iop_queue_receipt(int iopn, int chan, iop_msg_handler handler, void *user_data)
{
struct iop_msg *msg;
IOP *iop;
int s;
if (iopn & ~1) return -1;
iop = &mac68k_iops[iopn];
if (!iop) return -1;
msg = (struct iop_msg *) pool_get(&iop->pool, PR_WAITOK);
if (msg == NULL) return -1;
msg->channel = chan;
msg->handler = handler;
msg->user_data = user_data;
msg->status = IOP_MSGSTAT_QUEUED;
s = splhigh();
SIMPLEQ_INSERT_TAIL(&iop->recvq[chan], msg, iopm);
if (msg == SIMPLEQ_FIRST(&iop->recvq[chan])) {
msg->status = IOP_MSGSTAT_RECEIVING;
}
splx(s);
return 0;
}
int
iop_register_listener(int iopn, int chan, iop_msg_handler handler,
void *user_data)
{
IOP *iop;
int s;
if (iopn & ~1) return -1;
iop = &mac68k_iops[iopn];
if (!iop) return -1;
s = splhigh();
iop->listeners[chan] = handler;
iop->listener_data[chan] = user_data;
splx(s);
return 0;
}