#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pi1ppc.c,v 1.17 2022/11/01 19:45:35 andvar Exp $");
#include "opt_pi1ppc.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <dev/ppbus/ppbus_conf.h>
#include <dev/ppbus/ppbus_msq.h>
#include <dev/ppbus/ppbus_io.h>
#include <dev/ppbus/ppbus_var.h>
#include <machine/autoconf.h>
#include <machine/machtype.h>
#include <sgimips/ioc/iocreg.h>
#include <sgimips/hpc/hpcvar.h>
#include <sgimips/hpc/hpcreg.h>
#include <sgimips/hpc/pi1ppcreg.h>
#include <sgimips/hpc/pi1ppcvar.h>
#ifdef PI1PPC_DEBUG
int pi1ppc_debug = 1;
#endif
#ifdef PI1PPC_VERBOSE
int pi1ppc_verbose = 1;
#endif
static uint8_t r_reg(int reg, struct pi1ppc_softc *pi1ppc);
static void w_reg(int reg, struct pi1ppc_softc *pi1ppc, uint8_t byte);
#define AT_DATA_REG 0
#define AT_STAT_REG 1
#define AT_CTL_REG 2
#define pi1ppc_r_str(_x) r_reg(AT_STAT_REG,_x)
#define pi1ppc_r_ctr(_x) r_reg(AT_CTL_REG,_x)
#define pi1ppc_r_dtr(_x) r_reg(AT_DATA_REG,_x)
#define pi1ppc_w_str(_x,_y)
#define pi1ppc_w_ctr(_x,_y) w_reg(AT_CTL_REG,_x,_y)
#define pi1ppc_w_dtr(_x,_y) w_reg(AT_DATA_REG,_x,_y)
#define pi1ppc_barrier_r(_x) bus_space_barrier(_x->sc_iot,_x->sc_ioh, \
0,4,BUS_SPACE_BARRIER_READ)
#define pi1ppc_barrier_w(_x) bus_space_barrier(_x->sc_iot,_x->sc_ioh, \
0,4,BUS_SPACE_BARRIER_WRITE)
#define pi1ppc_barrier(_x) pi1ppc_barrier_r(_x)
static int pi1ppc_print(void *, const char *);
static int pi1ppc_read(device_t, char *, int, int, size_t *);
static int pi1ppc_write(device_t, char *, int, int, size_t *);
static int pi1ppc_setmode(device_t, int);
static int pi1ppc_getmode(device_t);
static int pi1ppc_exec_microseq(device_t, struct ppbus_microseq * *);
static uint8_t pi1ppc_io(device_t, int, u_char *, int, u_char);
static int pi1ppc_read_ivar(device_t, int, unsigned int *);
static int pi1ppc_write_ivar(device_t, int, unsigned int *);
static int pi1ppc_add_handler(device_t, void (*)(void *), void *);
static int pi1ppc_remove_handler(device_t, void (*)(void *));
static void pi1ppc_reset_epp_timeout(device_t);
static void pi1ppc_ecp_sync(device_t);
static void pi1ppc_nibble_read(struct pi1ppc_softc * const);
static void pi1ppc_byte_read(struct pi1ppc_softc * const);
static void pi1ppc_std_write(struct pi1ppc_softc * const);
static void pi1ppc_set_intr_mask(struct pi1ppc_softc * const, uint8_t);
static uint8_t pi1ppc_get_intr_stat(struct pi1ppc_softc * const);
#ifdef USE_INDY_ACK_HACK
static uint8_t pi1ppc_get_intr_mask(struct pi1ppc_softc * const);
#endif
static int pi1ppc_poll_str(struct pi1ppc_softc * const, const uint8_t,
const uint8_t);
static int pi1ppc_wait_interrupt(struct pi1ppc_softc * const, kcondvar_t *,
const uint8_t);
static int pi1ppc_poll_interrupt_stat(struct pi1ppc_softc * const,
const uint8_t);
static int pi1ppc_match(device_t parent, cfdata_t match, void *aux);
static void pi1ppc_attach(device_t parent, device_t self, void *aux);
CFATTACH_DECL_NEW(pi1ppc, sizeof(struct pi1ppc_softc),
pi1ppc_match,
pi1ppc_attach,
NULL,
NULL);
static int
pi1ppc_match(device_t parent, cfdata_t match, void *aux)
{
struct hpc_attach_args *ha = aux;
if (strcmp(ha->ha_name, match->cf_name) != 0)
return 0;
if (mach_type == MACH_SGI_IP22)
return 1;
return 0;
}
static void
pi1ppc_attach(device_t parent, device_t self, void *aux)
{
struct pi1ppc_softc *sc;
struct hpc_attach_args *haa;
sc = device_private(self);
sc->sc_dev = self;
haa = aux;
sc->sc_iot = haa->ha_st;
if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff,
0x28,
&sc->sc_ioh)) {
aprint_error(": unable to map control registers\n");
return;
}
pi1ppc_sc_attach(sc);
}
void
pi1ppc_sc_attach(struct pi1ppc_softc *lsc)
{
struct parport_adapter sc_parport_adapter;
char buf[64];
lsc->sc_type = PI1PPC_TYPE_INDY;
lsc->sc_model = GENERIC;
lsc->sc_has = PI1PPC_HAS_PS2;
mutex_init(&lsc->sc_lock, MUTEX_DEFAULT, IPL_TTY);
cv_init(&lsc->sc_in_cv, "pi1ppcin");
cv_init(&lsc->sc_out_cv, "pi1ppcou");
snprintb(buf, sizeof(buf), "\20\1INTR\2DMA\3FIFO\4PS2\5ECP\6EPP",
lsc->sc_has);
printf("\n%s: capabilities=%s\n", device_xname(lsc->sc_dev), buf);
lsc->sc_outb = lsc->sc_outbstart = lsc->sc_inb = lsc->sc_inbstart
= NULL;
lsc->sc_inb_nbytes = lsc->sc_outb_nbytes = 0;
if (pi1ppc_setmode(lsc->sc_dev, PPBUS_COMPATIBLE) != 0) {
PI1PPC_DPRINTF(("%s: unable to initialize mode.\n",
device_xname(lsc->sc_dev)));
}
sc_parport_adapter.capabilities = 0;
if (lsc->sc_has & PI1PPC_HAS_INTR) {
sc_parport_adapter.capabilities |= PPBUS_HAS_INTR;
}
if (lsc->sc_has & PI1PPC_HAS_DMA) {
sc_parport_adapter.capabilities |= PPBUS_HAS_DMA;
}
if (lsc->sc_has & PI1PPC_HAS_FIFO) {
sc_parport_adapter.capabilities |= PPBUS_HAS_FIFO;
}
if (lsc->sc_has & PI1PPC_HAS_PS2) {
sc_parport_adapter.capabilities |= PPBUS_HAS_PS2;
}
sc_parport_adapter.parport_io = pi1ppc_io;
sc_parport_adapter.parport_exec_microseq = pi1ppc_exec_microseq;
sc_parport_adapter.parport_setmode = pi1ppc_setmode;
sc_parport_adapter.parport_getmode = pi1ppc_getmode;
sc_parport_adapter.parport_read = pi1ppc_read;
sc_parport_adapter.parport_write = pi1ppc_write;
sc_parport_adapter.parport_read_ivar = pi1ppc_read_ivar;
sc_parport_adapter.parport_write_ivar = pi1ppc_write_ivar;
sc_parport_adapter.parport_dma_malloc = lsc->sc_dma_malloc;
sc_parport_adapter.parport_dma_free = lsc->sc_dma_free;
sc_parport_adapter.parport_add_handler = pi1ppc_add_handler;
sc_parport_adapter.parport_remove_handler = pi1ppc_remove_handler;
sc_parport_adapter.parport_ecp_sync = pi1ppc_ecp_sync;
sc_parport_adapter.parport_reset_epp_timeout =
pi1ppc_reset_epp_timeout;
SLIST_INIT(&(lsc->sc_handler_listhead));
lsc->sc_irqstat = PI1PPC_IRQ_NONE;
lsc->sc_ecr_intr = lsc->sc_ctr_intr = lsc->sc_str_intr = 0;
lsc->sc_use = 0;
lsc->child = config_found(lsc->sc_dev, &(sc_parport_adapter),
pi1ppc_print, CFARGS_NONE);
return;
}
int
pi1ppc_sc_detach(struct pi1ppc_softc *lsc, int flag)
{
device_t dev = lsc->sc_dev;
if (config_detach(lsc->child, flag) && !(flag & DETACH_QUIET)) {
printf("%s not able to detach child device, ", device_xname(dev));
if (!(flag & DETACH_FORCE)) {
printf("cannot detach\n");
return 1;
} else {
printf("continuing (DETACH_FORCE)\n");
}
}
if (!(flag & DETACH_QUIET))
printf("%s detached", device_xname(dev));
mutex_destroy(&lsc->sc_lock);
cv_destroy(&lsc->sc_in_cv);
cv_destroy(&lsc->sc_out_cv);
return 0;
}
static int
pi1ppc_print(void *aux, const char *name)
{
if (name != NULL) {
printf("%s: child devices", name);
return UNCONF;
}
return QUIET;
}
int
pi1ppcintr(void *arg)
{
#if 0
device_t dev = arg;
struct pi1ppc_softc *pi1ppc = device_private(dev);
int claim = 1;
enum { NONE, READER, WRITER } wake_up = NONE;
PI1PPC_LOCK(pi1ppc);
pi1ppc->sc_str_intr = pi1ppc_r_str(pi1ppc);
pi1ppc->sc_ctr_intr = pi1ppc_r_ctr(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
switch (atppc->sc_mode) {
case ATPPC_MODE_STD:
atppc->sc_irqstat = ATPPC_IRQ_nACK;
if (atppc->sc_outb)
wake_up = WRITER;
else
claim = 0;
break;
case ATPPC_MODE_NIBBLE:
case ATPPC_MODE_PS2:
if (!(atppc->sc_str_intr & nACK)) {
claim = 0;
break;
}
atppc->sc_irqstat = ATPPC_IRQ_nACK;
if (atppc->sc_inb)
wake_up = READER;
break;
case ATPPC_MODE_ECP:
case ATPPC_MODE_FAST:
if (atppc->sc_ecr_intr & ATPPC_SERVICE_INTR) {
if (atppc->sc_ecr_intr & ATPPC_ENABLE_DMA)
atppc->sc_irqstat |= ATPPC_IRQ_DMA;
else
atppc->sc_irqstat |= ATPPC_IRQ_FIFO;
if (atppc->sc_mode & ATPPC_MODE_ECP) {
if (atppc->sc_ctr_intr & PCD) {
if (atppc->sc_inb)
wake_up = READER;
else
claim = 0;
} else {
if (atppc->sc_outb)
wake_up = WRITER;
else
claim = 0;
}
} else {
if (atppc->sc_outb)
wake_up = WRITER;
else
claim = 0;
}
}
if ((atppc->sc_mode & ATPPC_MODE_ECP) &&
(atppc->sc_ecr_intr & ATPPC_nFAULT_INTR) &&
!(atppc->sc_str_intr & nFAULT)) {
atppc->sc_irqstat |= ATPPC_IRQ_nFAULT;
claim = 1;
}
break;
case ATPPC_MODE_EPP:
atppc->sc_irqstat = ATPPC_IRQ_nACK;
if (atppc->sc_inb)
wake_up = WRITER;
else if (atppc->sc_outb)
wake_up = READER;
else
claim = 0;
break;
default:
panic("%s: chipset is in invalid mode.", device_xname(dev));
}
if (claim) {
switch (wake_up) {
case NONE:
break;
case READER:
cv_broadcast(atppc->sc_in_cv);
break;
case WRITER:
cv_broadcast(atppc->sc_out_cv);
break;
}
}
if (claim) {
struct atppc_handler_node * callback;
SLIST_FOREACH(callback, &(atppc->sc_handler_listhead),
entries) {
(*callback->func)(callback->arg);
}
}
PI1PPC_UNLOCK(atppc);
return claim;
#else
return 0;
#endif
}
static void
pi1ppc_reset_epp_timeout(device_t dev)
{
return;
}
static int
pi1ppc_read(device_t dev, char *buf, int len, int ioflag,
size_t *cnt)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
int error = 0;
PI1PPC_LOCK(pi1ppc);
*cnt = 0;
pi1ppc->sc_inb = pi1ppc->sc_inbstart = buf;
pi1ppc->sc_inb_nbytes = len;
pi1ppc->sc_inerr = 0;
switch(pi1ppc->sc_mode) {
case PI1PPC_MODE_STD:
error = ENODEV;
break;
case PI1PPC_MODE_NIBBLE:
pi1ppc_nibble_read(pi1ppc);
break;
case PI1PPC_MODE_PS2:
pi1ppc_byte_read(pi1ppc);
break;
default:
panic("%s(%s): chipset in invalid mode.\n", __func__,
device_xname(dev));
}
*cnt = (pi1ppc->sc_inbstart - pi1ppc->sc_inb);
pi1ppc->sc_inb = pi1ppc->sc_inbstart = NULL;
pi1ppc->sc_inb_nbytes = 0;
if (!(error))
error = pi1ppc->sc_inerr;
PI1PPC_UNLOCK(pi1ppc);
return (error);
}
static int
pi1ppc_write(device_t dev, char *buf, int len, int ioflag, size_t *cnt)
{
struct pi1ppc_softc * const pi1ppc = device_private(dev);
int error = 0;
*cnt = 0;
PI1PPC_LOCK(pi1ppc);
pi1ppc->sc_outb = pi1ppc->sc_outbstart = buf;
pi1ppc->sc_outb_nbytes = len;
pi1ppc->sc_outerr = 0;
switch (pi1ppc->sc_mode) {
case PI1PPC_MODE_STD:
pi1ppc_std_write(pi1ppc);
break;
case PI1PPC_MODE_NIBBLE:
case PI1PPC_MODE_PS2:
error = ENODEV;
break;
default:
panic("%s(%s): chipset in invalid mode.\n", __func__,
device_xname(dev));
}
*cnt = (pi1ppc->sc_outbstart - pi1ppc->sc_outb);
pi1ppc->sc_outb = pi1ppc->sc_outbstart = NULL;
pi1ppc->sc_outb_nbytes = 0;
if (!(error))
error = pi1ppc->sc_outerr;
PI1PPC_UNLOCK(pi1ppc);
return (error);
}
static int
pi1ppc_setmode(device_t dev, int mode)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
uint8_t ecr;
uint8_t chipset_mode;
int rval = 0;
PI1PPC_LOCK(pi1ppc);
switch (mode) {
case PPBUS_PS2:
chipset_mode = PI1PPC_MODE_PS2;
break;
case PPBUS_NIBBLE:
chipset_mode = PI1PPC_MODE_NIBBLE;
break;
case PPBUS_COMPATIBLE:
chipset_mode = PI1PPC_MODE_STD;
break;
case PPBUS_ECP:
case PPBUS_EPP:
rval = ENODEV;
goto end;
default:
PI1PPC_DPRINTF(("%s(%s): invalid mode passed as "
"argument.\n", __func__, device_xname(dev)));
rval = ENODEV;
goto end;
}
pi1ppc->sc_mode = chipset_mode;
if (chipset_mode == PI1PPC_MODE_PS2) {
ecr = pi1ppc_r_ctr(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
ecr |= PCD;
pi1ppc_w_ctr(pi1ppc, ecr);
pi1ppc_barrier_w(pi1ppc);
}
end:
PI1PPC_UNLOCK(pi1ppc);
return rval;
}
static int
pi1ppc_getmode(device_t dev)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
int mode;
PI1PPC_LOCK(pi1ppc);
switch (pi1ppc->sc_mode) {
case PI1PPC_MODE_PS2:
mode = PPBUS_PS2;
break;
case PI1PPC_MODE_STD:
mode = PPBUS_COMPATIBLE;
break;
case PI1PPC_MODE_NIBBLE:
mode = PPBUS_NIBBLE;
break;
default:
panic("%s(%s): device is in invalid mode!", __func__,
device_xname(dev));
break;
}
PI1PPC_UNLOCK(pi1ppc);
return mode;
}
static void
pi1ppc_ecp_sync(device_t dev)
{
return;
}
static uint8_t ctl_reg_int_en = 0;
static uint8_t
r_reg(int reg, struct pi1ppc_softc *pi1ppc)
{
int val = 0;
if(reg == AT_STAT_REG) {
val = bus_space_read_4((pi1ppc)->sc_iot,
(pi1ppc)->sc_ioh, IOC_PLP_STAT);
val &= 0xff;
val ^= 0x80;
val &= 0xf8;
return val;
}
if(reg == AT_CTL_REG) {
val = bus_space_read_4((pi1ppc)->sc_iot,
(pi1ppc)->sc_ioh, IOC_PLP_CTL);
val &= 0xff;
val = ((val >> 1) & 0x20) | (val & 0x0f);
val ^= 0x0b;
val |= (ctl_reg_int_en & 0x10);
return val;
}
if(reg == AT_DATA_REG) {
val = bus_space_read_4((pi1ppc)->sc_iot,
(pi1ppc)->sc_ioh, IOC_PLP_DATA);
val &= 0xff;
return val;
}
return 0;
}
static void
w_reg(int reg, struct pi1ppc_softc *pi1ppc, uint8_t byte)
{
if(reg == AT_CTL_REG) {
ctl_reg_int_en = (byte & 0x10);
byte = ((byte << 1) & 0x40) | (byte & 0x0f);
byte ^= 0x0b;
bus_space_write_4((pi1ppc)->sc_iot,
(pi1ppc)->sc_ioh, IOC_PLP_CTL, byte);
}
if(reg == AT_DATA_REG) {
bus_space_write_4((pi1ppc)->sc_iot,
(pi1ppc)->sc_ioh, IOC_PLP_DATA, byte);
}
}
static int
pi1ppc_exec_microseq(device_t dev, struct ppbus_microseq **p_msq)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
struct ppbus_microseq *mi = *p_msq;
char cc, *p;
int i, iter, len;
int error;
register int reg;
register unsigned char mask;
register int accum = 0;
register char *ptr = NULL;
struct ppbus_microseq *stack = NULL;
PI1PPC_LOCK(pi1ppc);
for (;;) {
switch (mi->opcode) {
case MS_OP_RSET:
cc = r_reg(mi->arg[0].i, pi1ppc);
pi1ppc_barrier_r(pi1ppc);
cc &= (char)mi->arg[2].i;
cc |= (char)mi->arg[1].i;
w_reg(mi->arg[0].i, pi1ppc, cc);
pi1ppc_barrier_w(pi1ppc);
mi++;
break;
case MS_OP_RASSERT_P:
reg = mi->arg[1].i;
ptr = pi1ppc->sc_ptr;
if ((len = mi->arg[0].i) == MS_ACCUM) {
accum = pi1ppc->sc_accum;
for (; accum; accum--) {
w_reg(reg, pi1ppc, *ptr++);
pi1ppc_barrier_w(pi1ppc);
}
pi1ppc->sc_accum = accum;
} else {
for (i = 0; i < len; i++) {
w_reg(reg, pi1ppc, *ptr++);
pi1ppc_barrier_w(pi1ppc);
}
}
pi1ppc->sc_ptr = ptr;
mi++;
break;
case MS_OP_RFETCH_P:
reg = mi->arg[1].i;
mask = (char)mi->arg[2].i;
ptr = pi1ppc->sc_ptr;
if ((len = mi->arg[0].i) == MS_ACCUM) {
accum = pi1ppc->sc_accum;
for (; accum; accum--) {
*ptr++ = r_reg(reg, pi1ppc) & mask;
pi1ppc_barrier_r(pi1ppc);
}
pi1ppc->sc_accum = accum;
} else {
for (i = 0; i < len; i++) {
*ptr++ = r_reg(reg, pi1ppc) & mask;
pi1ppc_barrier_r(pi1ppc);
}
}
pi1ppc->sc_ptr = ptr;
mi++;
break;
case MS_OP_RFETCH:
*((char *)mi->arg[2].p) = r_reg(mi->arg[0].i, pi1ppc) &
(char)mi->arg[1].i;
pi1ppc_barrier_r(pi1ppc);
mi++;
break;
case MS_OP_RASSERT:
case MS_OP_DELAY:
do {
for (;mi->opcode == MS_OP_RASSERT; mi++) {
w_reg(mi->arg[0].i, pi1ppc,
(char)mi->arg[1].i);
pi1ppc_barrier_w(pi1ppc);
}
for (;mi->opcode == MS_OP_DELAY; mi++) {
delay(mi->arg[0].i);
}
} while (mi->opcode == MS_OP_RASSERT);
break;
case MS_OP_ADELAY:
if (mi->arg[0].i) {
DELAY(mi->arg[0].i * 1000);
}
mi++;
break;
case MS_OP_TRIG:
reg = mi->arg[0].i;
iter = mi->arg[1].i;
p = (char *)mi->arg[2].p;
for (i = 0; i < iter; i++) {
w_reg(reg, pi1ppc, *p++);
pi1ppc_barrier_w(pi1ppc);
delay((unsigned char)*p++);
}
mi++;
break;
case MS_OP_SET:
pi1ppc->sc_accum = mi->arg[0].i;
mi++;
break;
case MS_OP_DBRA:
if (--pi1ppc->sc_accum > 0) {
mi += mi->arg[0].i;
}
mi++;
break;
case MS_OP_BRSET:
cc = pi1ppc_r_str(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
if ((cc & (char)mi->arg[0].i) == (char)mi->arg[0].i) {
mi += mi->arg[1].i;
}
mi++;
break;
case MS_OP_BRCLEAR:
cc = pi1ppc_r_str(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
if ((cc & (char)mi->arg[0].i) == 0) {
mi += mi->arg[1].i;
}
mi++;
break;
case MS_OP_BRSTAT:
cc = pi1ppc_r_str(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
if ((cc & ((char)mi->arg[0].i | (char)mi->arg[1].i)) ==
(char)mi->arg[0].i) {
mi += mi->arg[2].i;
}
mi++;
break;
case MS_OP_C_CALL:
if ((error = mi->arg[0].f(mi->arg[1].p,
pi1ppc->sc_ptr))) {
PI1PPC_UNLOCK(pi1ppc);
return (error);
}
mi++;
break;
case MS_OP_PTR:
pi1ppc->sc_ptr = (char *)mi->arg[0].p;
mi++;
break;
case MS_OP_CALL:
if (stack) {
panic("%s - %s: too many calls", device_xname(dev),
__func__);
}
if (mi->arg[0].p) {
stack = mi;
mi = (struct ppbus_microseq *)mi->arg[0].p;
} else {
mi++;
}
break;
case MS_OP_SUBRET:
mi = stack;
stack = 0;
mi++;
break;
case MS_OP_PUT:
case MS_OP_GET:
case MS_OP_RET:
if (stack) {
panic("%s: cannot return to pi1ppc level",
__func__);
}
*p_msq = mi;
PI1PPC_UNLOCK(pi1ppc);
return (0);
default:
panic("%s: unknown microsequence "
"opcode 0x%x", __func__, mi->opcode);
break;
}
}
#ifdef PI1PPC_DEBUG
panic("%s: unexpected code reached!\n", __func__);
#endif
}
static uint8_t
pi1ppc_io(device_t dev, int iop, u_char *addr, int cnt, u_char byte)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
uint8_t val = 0;
PI1PPC_LOCK(pi1ppc);
switch (iop) {
case PPBUS_RDTR:
val = r_reg(AT_DATA_REG, pi1ppc);
break;
case PPBUS_RSTR:
val = r_reg(AT_STAT_REG, pi1ppc);
break;
case PPBUS_RCTR:
val = r_reg(AT_CTL_REG, pi1ppc);
break;
case PPBUS_WDTR:
w_reg(AT_DATA_REG, pi1ppc, byte);
break;
case PPBUS_WSTR:
break;
case PPBUS_WCTR:
w_reg(AT_CTL_REG, pi1ppc, byte);
break;
default:
panic("%s(%s): unknown I/O operation", device_xname(dev),
__func__);
break;
}
pi1ppc_barrier(pi1ppc);
PI1PPC_UNLOCK(pi1ppc);
return val;
}
static int
pi1ppc_read_ivar(device_t dev, int index, unsigned int *val)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
int rval = 0;
PI1PPC_LOCK(pi1ppc);
switch(index) {
case PPBUS_IVAR_INTR:
*val = ((pi1ppc->sc_use & PI1PPC_USE_INTR) != 0);
break;
case PPBUS_IVAR_DMA:
*val = ((pi1ppc->sc_use & PI1PPC_USE_DMA) != 0);
break;
default:
rval = ENODEV;
}
PI1PPC_UNLOCK(pi1ppc);
return rval;
}
static int
pi1ppc_write_ivar(device_t dev, int index, unsigned int *val)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
int rval = 0;
PI1PPC_LOCK(pi1ppc);
switch(index) {
case PPBUS_IVAR_INTR:
if (*val == 0)
pi1ppc->sc_use &= ~PI1PPC_USE_INTR;
else if (pi1ppc->sc_has & PI1PPC_HAS_INTR)
pi1ppc->sc_use |= PI1PPC_USE_INTR;
else
rval = ENODEV;
break;
case PPBUS_IVAR_DMA:
if (*val == 0)
pi1ppc->sc_use &= ~PI1PPC_USE_DMA;
else if (pi1ppc->sc_has & PI1PPC_HAS_DMA)
pi1ppc->sc_use |= PI1PPC_USE_DMA;
else
rval = ENODEV;
break;
default:
rval = ENODEV;
}
PI1PPC_UNLOCK(pi1ppc);
return rval;
}
static int
pi1ppc_add_handler(device_t dev, void (*handler)(void *), void *arg)
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
struct pi1ppc_handler_node *callback;
if (handler == NULL) {
PI1PPC_DPRINTF(("%s(%s): attempt to register NULL handler.\n",
__func__, device_xname(dev)));
return EINVAL;
}
callback = kmem_alloc(sizeof(struct pi1ppc_handler_node), KM_SLEEP);
PI1PPC_LOCK(pi1ppc);
callback->func = handler;
callback->arg = arg;
SLIST_INSERT_HEAD(&(pi1ppc->sc_handler_listhead), callback, entries);
PI1PPC_UNLOCK(pi1ppc);
return 0;
}
static int
pi1ppc_remove_handler(device_t dev, void (*handler)(void *))
{
struct pi1ppc_softc *pi1ppc = device_private(dev);
struct pi1ppc_handler_node *callback;
int rval;
PI1PPC_LOCK(pi1ppc);
KASSERT(!SLIST_EMPTY(&(pi1ppc->sc_handler_listhead)));
SLIST_FOREACH(callback, &(pi1ppc->sc_handler_listhead), entries) {
if (callback->func == handler) {
SLIST_REMOVE(&(pi1ppc->sc_handler_listhead), callback,
pi1ppc_handler_node, entries);
break;
}
}
PI1PPC_UNLOCK(pi1ppc);
if (callback) {
kmem_free(callback, sizeof(struct pi1ppc_handler_node));
rval = 0;
} else {
rval = EINVAL;
}
return rval;
}
#define nibble2char(s) ((((s) & ~nACK) >> 3) | (~(s) & nBUSY) >> 4)
static void
pi1ppc_nibble_read(struct pi1ppc_softc *pi1ppc)
{
int i;
uint8_t nibble[2];
uint8_t ctr;
uint8_t str;
if (pi1ppc->sc_use & PI1PPC_USE_INTR) {
#if 0
ctr = pi1ppc_r_ctr(pi1ppc);
pi1ppc_barrier_r(ioppc);
if (!(ctr & IRQENABLE)) {
ctr |= IRQENABLE;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
}
#endif
}
while (pi1ppc->sc_inbstart < (pi1ppc->sc_inb + pi1ppc->sc_inb_nbytes)) {
str = pi1ppc_r_str(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
if (str & nDATAVAIL) {
return;
}
for (i = 0; i < 2; i++) {
ctr = pi1ppc_r_ctr(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
ctr |= HOSTBUSY;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
pi1ppc->sc_inerr = pi1ppc_poll_str(pi1ppc, 0, PTRCLK);
if (pi1ppc->sc_inerr)
return;
nibble[i] = pi1ppc_r_str(pi1ppc);
ctr &= ~HOSTBUSY;
pi1ppc_w_ctr(pi1ppc, ctr);
if (pi1ppc->sc_use & PI1PPC_USE_INTR)
pi1ppc->sc_inerr = pi1ppc_wait_interrupt(pi1ppc,
&pi1ppc->sc_in_cv, PI1PPC_IRQ_nACK);
else
pi1ppc->sc_inerr = pi1ppc_poll_str(pi1ppc, PTRCLK,
PTRCLK);
if (pi1ppc->sc_inerr)
return;
}
*(pi1ppc->sc_inbstart) = ((nibble2char(nibble[1]) << 4) & 0xf0) |
(nibble2char(nibble[0]) & 0x0f);
pi1ppc->sc_inbstart++;
}
}
static void
pi1ppc_byte_read(struct pi1ppc_softc * const pi1ppc)
{
uint8_t ctr;
uint8_t str;
ctr = pi1ppc_r_ctr(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
if (!(ctr & PCD)) {
PI1PPC_DPRINTF(("%s: byte-mode read attempted without direction "
"bit set.", device_xname(pi1ppc->sc_dev)));
pi1ppc->sc_inerr = ENODEV;
return;
}
#if 0
if (pi1ppc->sc_use & PI1PPC_USE_INTR) {
if (!(ctr & IRQENABLE)) {
ctr |= IRQENABLE;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
}
}
#endif
while (pi1ppc->sc_inbstart < (pi1ppc->sc_inb + pi1ppc->sc_inb_nbytes)) {
str = pi1ppc_r_str(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
if (str & nDATAVAIL) {
return;
}
ctr |= HOSTBUSY;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
pi1ppc->sc_inerr = pi1ppc_poll_str(pi1ppc, 0, PTRCLK);
if (pi1ppc->sc_inerr)
return;
*(pi1ppc->sc_inbstart) = pi1ppc_r_dtr(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
ctr &= ~HOSTBUSY;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
if (pi1ppc->sc_use & PI1PPC_USE_INTR)
pi1ppc->sc_inerr = pi1ppc_wait_interrupt(pi1ppc,
&pi1ppc->sc_in_cv, PI1PPC_IRQ_nACK);
else
pi1ppc->sc_inerr = pi1ppc_poll_str(pi1ppc, PTRCLK, PTRCLK);
if (pi1ppc->sc_inerr)
return;
str |= HOSTCLK;
pi1ppc_w_str(pi1ppc, str);
pi1ppc_barrier_w(pi1ppc);
DELAY(1);
str &= ~HOSTCLK;
pi1ppc_w_str(pi1ppc, str);
pi1ppc_barrier_w(pi1ppc);
pi1ppc->sc_inbstart++;
}
}
static void
pi1ppc_set_intr_mask(struct pi1ppc_softc * const pi1ppc, uint8_t mask)
{
mask = ~mask;
mask &= 0xfc;
bus_space_write_4((pi1ppc)->sc_iot, (pi1ppc)->sc_ioh, IOC_PLP_INTMASK, mask);
pi1ppc_barrier_w(pi1ppc);
}
#ifdef USE_INDY_ACK_HACK
static uint8_t
pi1ppc_get_intr_mask(struct pi1ppc_softc * const pi1ppc)
{
int val;
val = bus_space_read_4((pi1ppc)->sc_iot, (pi1ppc)->sc_ioh, IOC_PLP_INTMASK);
pi1ppc_barrier_r(pi1ppc);
val = ~val;
return (val & 0xfc);
}
#endif
static uint8_t
pi1ppc_get_intr_stat(struct pi1ppc_softc * const pi1ppc)
{
int val;
val = bus_space_read_4((pi1ppc)->sc_iot, (pi1ppc)->sc_ioh, IOC_PLP_INTSTAT);
pi1ppc_barrier_r(pi1ppc);
return (val & 0xfc);
}
static void
pi1ppc_std_write(struct pi1ppc_softc * const pi1ppc)
{
unsigned char ctr;
ctr = pi1ppc_r_ctr(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
ctr &= ~PCD;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
#if 0
if (pi1ppc->sc_use & PI1PPC_USE_INTR) {
if (!(ctr & IRQENABLE)) {
ctr |= IRQENABLE;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
}
}
#endif
while (pi1ppc->sc_outbstart < (pi1ppc->sc_outb + pi1ppc->sc_outb_nbytes)) {
pi1ppc->sc_outerr = pi1ppc_poll_str(pi1ppc, SPP_READY, SPP_MASK);
if (pi1ppc->sc_outerr) {
printf("pi1ppc: timeout waiting for peripheral to become ready\n");
return;
}
pi1ppc_w_dtr(pi1ppc, *(pi1ppc->sc_outbstart));
pi1ppc_barrier_w(pi1ppc);
DELAY(1);
if (!(pi1ppc->sc_use & PI1PPC_USE_INTR)) {
pi1ppc_get_intr_stat(pi1ppc);
pi1ppc_set_intr_mask(pi1ppc, PI1_PLP_ACK_INTR);
}
ctr |= STROBE;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
DELAY(1);
ctr &= ~STROBE;
pi1ppc_w_ctr(pi1ppc, ctr);
pi1ppc_barrier_w(pi1ppc);
if (pi1ppc->sc_use & PI1PPC_USE_INTR) {
pi1ppc->sc_outerr = pi1ppc_wait_interrupt(pi1ppc,
&pi1ppc->sc_out_cv, PI1PPC_IRQ_nACK);
if (pi1ppc->sc_outerr)
return;
} else {
pi1ppc->sc_outerr = pi1ppc_poll_interrupt_stat(pi1ppc,
PI1_PLP_ACK_INTR);
if (pi1ppc->sc_outerr) {
printf("pi1ppc: timeout waiting for ACK: %02x\n",pi1ppc_r_str(pi1ppc));
return;
}
}
pi1ppc->sc_outbstart++;
}
}
static int
pi1ppc_poll_str(struct pi1ppc_softc * const pi1ppc, const uint8_t status,
const uint8_t mask)
{
unsigned int timecount;
uint8_t str;
int error = EIO;
for (timecount = 0; timecount < ((MAXBUSYWAIT/hz)*1000000);
timecount++) {
str = pi1ppc_r_str(pi1ppc);
pi1ppc_barrier_r(pi1ppc);
if ((str & mask) == status) {
error = 0;
break;
}
DELAY(1);
}
return error;
}
static int
pi1ppc_wait_interrupt(struct pi1ppc_softc * const sc, kcondvar_t *cv,
const uint8_t irqstat)
{
int error = EIO;
sc->sc_irqstat &= ~irqstat;
error = cv_timedwait_sig(cv, &sc->sc_lock, MAXBUSYWAIT);
if (!error && (sc->sc_irqstat & irqstat) == 0) {
sc->sc_irqstat &= ~irqstat;
error = 0;
}
return error;
}
#ifdef USE_INDY_ACK_HACK
#define ACK_TIMEOUT_SCALER 1000
#else
#define ACK_TIMEOUT_SCALER 1000000
#endif
static int
pi1ppc_poll_interrupt_stat(struct pi1ppc_softc * const pi1ppc,
const uint8_t match)
{
unsigned int timecount;
uint8_t cur;
int error = EIO;
#ifdef USE_INDY_ACK_HACK
int retry_count = 10000;
retry:
#endif
for (timecount = 0; timecount < ((MAXBUSYWAIT/hz)*ACK_TIMEOUT_SCALER);
timecount++) {
cur = pi1ppc_get_intr_stat(pi1ppc);
if ((cur & match) == match) {
error = 0;
break;
}
DELAY(1);
}
#ifdef USE_INDY_ACK_HACK
if(error != 0) {
cur = pi1ppc_r_str(pi1ppc);
if(((cur&nBUSY) == 0) && retry_count) {
retry_count--;
goto retry;
}
if((cur&(nFAULT|SELECT|nBUSY)) == (nFAULT|SELECT|nBUSY))
error = 0;
if(error!=0)
printf("int mask=%02x, int stat=%02x, str=%02x\n",
pi1ppc_get_intr_mask(pi1ppc),
pi1ppc_get_intr_stat(pi1ppc),
cur);
}
#endif
return error;
}