#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: xpbus.c,v 1.1 2022/06/10 21:42:23 tsutsui Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/atomic.h>
#include <sys/device.h>
#include <machine/autoconf.h>
#include <machine/board.h>
#include <luna68k/dev/xpbusvar.h>
#include <luna68k/dev/xplxfirm.h>
#define PIO_ADDR OBIO_PIO0_BASE
#define PORT_A 0
#define PORT_B 1
#define PORT_C 2
#define CTRL 3
#define XP_INT1_REQ 0
#define XP_INT1_ENA 2
#define XP_INT5_REQ 3
#define XP_INT5_ENA 4
#define PARITY 6
#define XP_RESET 7
#define ON 1
#define OFF 0
struct xpbus_softc {
device_t sc_dev;
};
static const struct xpbus_attach_args xpdevs[] = {
{ "xp" },
{ "psgpam" },
};
static int xpbus_match(device_t, cfdata_t, void *);
static void xpbus_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(xpbus, sizeof(struct xpbus_softc),
xpbus_match, xpbus_attach, NULL, NULL);
static bool xpbus_matched;
static volatile unsigned int xp_acquired;
static bool xp_shm_dirty = true;
static int
xpbus_match(device_t parent, cfdata_t cf, void *aux)
{
struct mainbus_attach_args *ma = aux;
if (xpbus_matched)
return 0;
if (ma->ma_addr != XP_SHM_BASE)
return 0;
xpbus_matched = true;
return 1;
}
static void
xpbus_attach(device_t parent, device_t self, void *aux)
{
struct xpbus_softc *sc = device_private(self);
struct xpbus_attach_args xa;
int i;
sc->sc_dev = self;
aprint_normal("\n");
for (i = 0; i < __arraycount(xpdevs); i++) {
xa = xpdevs[i];
config_found(self, &xa, NULL, CFARGS_NONE);
}
}
u_int
xp_acquire(int xplx_devid, u_int excl)
{
for (;;) {
unsigned int before, after;
before = xp_acquired;
if (before & XP_ACQ_EXCL)
return 0;
if (before & (1 << xplx_devid))
return 0;
after = before | (1 << xplx_devid) | excl;
if (atomic_cas_uint(&xp_acquired, before, after) == before) {
return after & ~(excl);
}
}
}
void
xp_release(int xplx_devid)
{
for (;;) {
unsigned int before, after;
before = xp_acquired;
after = before & ~(1 << xplx_devid) & ~XP_ACQ_EXCL;
if (atomic_cas_uint(&xp_acquired, before, after) == before) {
return;
}
}
}
void
xp_set_shm_dirty(void)
{
xp_shm_dirty = true;
}
void
xp_ensure_firmware(void)
{
if (xp_shm_dirty) {
xp_cpu_reset_hold();
delay(100);
memcpy((void *)XP_SHM_BASE, xplx, xplx_size);
delay(100);
xp_cpu_reset_release();
xp_shm_dirty = false;
}
}
uint8_t
put_pio0c(uint8_t bit, uint8_t set)
{
volatile uint8_t * const pio0 = (uint8_t *)PIO_ADDR;
pio0[CTRL] = (bit << 1) | (set & 0x01);
return pio0[PORT_C];
}
void
xp_cpu_reset_hold(void)
{
put_pio0c(XP_RESET, ON);
}
void
xp_cpu_reset_release(void)
{
put_pio0c(XP_RESET, OFF);
}
void
xp_cpu_reset(void)
{
xp_cpu_reset_hold();
delay(100);
xp_cpu_reset_release();
}
void
xp_intr1_enable(void)
{
put_pio0c(XP_INT1_ENA, ON);
}
void
xp_intr1_disable(void)
{
put_pio0c(XP_INT1_ENA, OFF);
}
void
xp_intr1_acknowledge(void)
{
*(volatile uint8_t *)OBIO_PIO0A;
*(volatile uint8_t *)OBIO_PIO0B;
}
void
xp_intr5_enable(void)
{
put_pio0c(XP_INT5_ENA, ON);
}
void
xp_intr5_disable(void)
{
put_pio0c(XP_INT5_ENA, OFF);
}
void
xp_intr5_acknowledge(void)
{
(void)*(volatile uint8_t *)OBIO_PIO0A;
}
void *
xp_shmptr(int offset)
{
return (uint8_t *)XP_SHM_BASE + offset;
}
int
xp_readmem8(int offset)
{
return *((volatile uint8_t *)xp_shmptr(offset));
}
int
xp_readmem16le(int offset)
{
return le16toh(*(volatile uint16_t *)xp_shmptr(offset));
}
void
xp_writemem8(int offset, int v)
{
*(volatile uint8_t *)(xp_shmptr(offset)) = v;
}
void
xp_writemem16le(int offset, int v)
{
*((volatile uint16_t *)xp_shmptr(offset)) = htole16((uint16_t)v);
}