#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <uvm/uvm_extern.h>
#include <machine/autoconf.h>
#include <machine/board.h>
#include <machine/xpio.h>
#define XP_SHM_BASE TRI_PORT_RAM
#define XP_SHM_SIZE 0x00010000
#define XP_TAS_ADDR OBIO_TAS
struct xp_softc {
struct device sc_dev;
vaddr_t sc_shm_base;
vsize_t sc_shm_size;
vaddr_t sc_tas;
bool sc_isopen;
};
static int xp_match(struct device *, void *, void *);
static void xp_attach(struct device *, struct device *, void *);
const struct cfattach xp_ca = {
sizeof (struct xp_softc), xp_match, xp_attach
};
struct cfdriver xp_cd = {
NULL, "xp", DV_DULL
};
#ifdef XP_DEBUG
#define XP_DEBUG_ALL 0xff
uint32_t xp_debug = 0;
#define DPRINTF(x, y) if (xp_debug & (x)) printf y
#else
#define DPRINTF(x, y)
#endif
static bool xp_matched;
#define PIO_ADDR OBIO_PIO0_BASE
#define PORT_A (0 * 4)
#define PORT_B (1 * 4)
#define PORT_C (2 * 4)
#define CTRL (3 * 4)
#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
static 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];
}
static int
xp_match(struct device *parent, void *cf, void *aux)
{
struct mainbus_attach_args *maa = aux;
if (xp_matched)
return 0;
if (strcmp(maa->ma_name, xp_cd.cd_name))
return 0;
if (maa->ma_addr != XP_SHM_BASE)
return 0;
xp_matched = true;
return 1;
}
static void
xp_attach(struct device *parent, struct device *self, void *aux)
{
struct xp_softc *sc = (void *)self;
printf(": HD647180X I/O processor\n");
sc->sc_shm_base = XP_SHM_BASE;
sc->sc_shm_size = XP_SHM_SIZE;
sc->sc_tas = XP_TAS_ADDR;
}
int
xpopen(dev_t dev, int flags, int mode, struct proc *p)
{
struct xp_softc *sc;
int unit;
DPRINTF(XP_DEBUG_ALL, ("%s\n", __func__));
unit = minor(dev);
if (unit >= xp_cd.cd_ndevs)
return ENXIO;
sc = xp_cd.cd_devs[unit];
if (sc == NULL)
return ENXIO;
if (sc->sc_isopen)
return EBUSY;
sc->sc_isopen = true;
return 0;
}
int
xpclose(dev_t dev, int flags, int mode, struct proc *p)
{
struct xp_softc *sc;
int unit;
DPRINTF(XP_DEBUG_ALL, ("%s\n", __func__));
unit = minor(dev);
if (unit >= xp_cd.cd_ndevs)
return ENXIO;
sc = xp_cd.cd_devs[unit];
sc->sc_isopen = false;
return 0;
}
int
xpioctl(dev_t dev, u_long cmd, void *addr, int flags, struct proc *p)
{
struct xp_softc *sc;
int unit, error;
struct xp_download *downld;
uint8_t *loadbuf;
size_t loadsize;
DPRINTF(XP_DEBUG_ALL, ("%s\n", __func__));
unit = minor(dev);
if (unit >= xp_cd.cd_ndevs)
return ENXIO;
sc = xp_cd.cd_devs[unit];
switch (cmd) {
case XPIOCDOWNLD:
downld = addr;
loadsize = downld->size;
if (loadsize == 0 || loadsize > sc->sc_shm_size) {
return EINVAL;
}
loadbuf = malloc(loadsize, M_DEVBUF, M_WAITOK);
if (loadbuf == NULL) {
return ENOMEM;
}
error = copyin(downld->data, loadbuf, loadsize);
if (error == 0) {
put_pio0c(XP_RESET, ON);
delay(100);
memcpy((void *)sc->sc_shm_base, loadbuf, loadsize);
delay(100);
put_pio0c(XP_RESET, OFF);
} else {
DPRINTF(XP_DEBUG_ALL, ("%s: ioctl failed (err = %d)\n",
__func__, error));
}
free(loadbuf, M_DEVBUF, loadsize);
return error;
default:
return ENOTTY;
}
}
paddr_t
xpmmap(dev_t dev, off_t offset, int prot)
{
struct xp_softc *sc;
int unit;
paddr_t pa;
pa = -1;
unit = minor(dev);
sc = xp_cd.cd_devs[unit];
if (offset >= 0 &&
offset < sc->sc_shm_size) {
pa = (paddr_t)(trunc_page(sc->sc_shm_base) + offset);
}
return pa;
}