#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_ie_vme.c,v 1.36 2025/10/04 04:44:21 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_types.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_ether.h>
#include <sys/bus.h>
#include <sys/intr.h>
#include <dev/vme/vmevar.h>
#include <dev/ic/i82586reg.h>
#include <dev/ic/i82586var.h>
#include "locators.h"
#define IEVME_PAGESIZE 1024
#define IEVME_PAGSHIFT 10
#define IEVME_NPAGES 256
#define IEVME_MAPSZ 1024
#define IEVME_SBORDR 0x8000
#define IEVME_IBORDR 0x0000
#define IEVME_P2MEM 0x2000
#define IEVME_OBMEM 0x0000
#define IEVME_PGMASK 0x0fff
struct ievme {
uint16_t pgmap[IEVME_MAPSZ];
uint16_t xxx[32];
uint16_t status;
uint16_t xxx2;
uint16_t pectrl;
uint16_t peaddr;
};
#define IEVME_RESET 0x8000
#define IEVME_ONAIR 0x4000
#define IEVME_ATTEN 0x2000
#define IEVME_IENAB 0x1000
#define IEVME_PEINT 0x0800
#define IEVME_PERR 0x0200
#define IEVME_INT 0x0100
#define IEVME_P2EN 0x0020
#define IEVME_256K 0x0010
#define IEVME_HADDR 0x000f
#define IEVME_PARACK 0x0100
#define IEVME_PARSRC 0x0080
#define IEVME_PAREND 0x0040
#define IEVME_PARADR 0x000f
static int media[] = {
IFM_ETHER | IFM_10_2,
};
#define NMEDIA __arraycount(media)
static void ie_vmereset(struct ie_softc *, int);
static void ie_vmeattend(struct ie_softc *, int);
static void ie_vmerun(struct ie_softc *);
static int ie_vmeintr(struct ie_softc *, int);
int ie_vme_match(device_t, cfdata_t, void *);
void ie_vme_attach(device_t, device_t, void *);
struct ie_vme_softc {
struct ie_softc ie;
bus_space_tag_t ievt;
bus_space_handle_t ievh;
};
CFATTACH_DECL_NEW(ie_vme, sizeof(struct ie_vme_softc),
ie_vme_match, ie_vme_attach, NULL, NULL);
#define read_iev(sc, reg) \
bus_space_read_2(sc->ievt, sc->ievh, offsetof(struct ievme, reg))
#define write_iev(sc, reg, val) \
bus_space_write_2(sc->ievt, sc->ievh, offsetof(struct ievme, reg), val)
void
ie_vmereset(struct ie_softc *sc, int what)
{
struct ie_vme_softc *vsc = (struct ie_vme_softc *)sc;
write_iev(vsc, status, IEVME_RESET);
delay(100);
write_iev(vsc, status, 0);
}
void
ie_vmeattend(struct ie_softc *sc, int why)
{
struct ie_vme_softc *vsc = (struct ie_vme_softc *)sc;
write_iev(vsc, status, read_iev(vsc, status) | IEVME_ATTEN);
write_iev(vsc, status, read_iev(vsc, status) & ~IEVME_ATTEN);
}
void
ie_vmerun(struct ie_softc *sc)
{
struct ie_vme_softc *vsc = (struct ie_vme_softc *)sc;
write_iev(vsc, status, read_iev(vsc, status)
| IEVME_ONAIR | IEVME_IENAB | IEVME_PEINT);
}
int
ie_vmeintr(struct ie_softc *sc, int where)
{
struct ie_vme_softc *vsc = (struct ie_vme_softc *)sc;
if (where != INTR_ENTER)
return 0;
if (read_iev(vsc, status) & IEVME_PERR) {
aprint_error_dev(sc->sc_dev,
"parity error (ctrl 0x%x @ 0x%02x%04x)\n",
read_iev(vsc, pectrl),
read_iev(vsc, pectrl) & IEVME_HADDR,
read_iev(vsc, peaddr));
write_iev(vsc, pectrl, read_iev(vsc, pectrl) | IEVME_PARACK);
}
return 0;
}
void ie_memcopyin(struct ie_softc *, void *, int, size_t);
void ie_memcopyout(struct ie_softc *, const void *, int, size_t);
void
ie_memcopyin(struct ie_softc *sc, void *p, int offset, size_t size)
{
size_t help;
if ((offset & 1) && ((u_long)p & 1) && size > 0) {
*(uint8_t *)p = bus_space_read_1(sc->bt, sc->bh, offset);
offset++;
p = (uint8_t *)p + 1;
size--;
}
if ((offset & 1) || ((u_long)p & 1)) {
bus_space_read_region_1(sc->bt, sc->bh, offset, p, size);
return;
}
help = size / 2;
bus_space_read_region_2(sc->bt, sc->bh, offset, p, help);
if (2 * help == size)
return;
offset += 2 * help;
p = (uint16_t *)p + help;
*(uint8_t *)p = bus_space_read_1(sc->bt, sc->bh, offset);
}
void
ie_memcopyout(struct ie_softc *sc, const void *p, int offset, size_t size)
{
size_t help;
if ((offset & 1) && ((u_long)p & 1) && size > 0) {
bus_space_write_1(sc->bt, sc->bh, offset, *(const uint8_t *)p);
offset++;
p = (const uint8_t *)p + 1;
size--;
}
if ((offset & 1) || ((u_long)p & 1)) {
bus_space_write_region_1(sc->bt, sc->bh, offset, p, size);
return;
}
help = size / 2;
bus_space_write_region_2(sc->bt, sc->bh, offset, p, help);
if (2 * help == size)
return;
offset += 2 * help;
p = (const uint16_t *)p + help;
bus_space_write_1(sc->bt, sc->bh, offset, *(const uint8_t *)p);
}
uint16_t ie_vme_read16(struct ie_softc *, int offset);
void ie_vme_write16(struct ie_softc *, int offset, uint16_t value);
void ie_vme_write24(struct ie_softc *, int offset, int addr);
uint16_t
ie_vme_read16(struct ie_softc *sc, int offset)
{
uint16_t v;
v = bus_space_read_2(sc->bt, sc->bh, offset);
return (((v&0xff)<<8) | ((v>>8)&0xff));
}
void
ie_vme_write16(struct ie_softc *sc, int offset, uint16_t v)
{
int v0 = ((((v)&0xff)<<8) | (((v)>>8)&0xff));
bus_space_write_2(sc->bt, sc->bh, offset, v0);
}
void
ie_vme_write24(struct ie_softc *sc, int offset, int addr)
{
u_char *f = (u_char *)&addr;
uint16_t v0, v1;
u_char *t;
t = (u_char *)&v0;
t[0] = f[3]; t[1] = f[2];
bus_space_write_2(sc->bt, sc->bh, offset, v0);
t = (u_char *)&v1;
t[0] = f[1]; t[1] = 0;
bus_space_write_2(sc->bt, sc->bh, offset+2, v1);
}
int
ie_vme_match(device_t parent, cfdata_t cf, void *aux)
{
struct vme_attach_args *va = aux;
vme_chipset_tag_t ct = va->va_vct;
vme_am_t mod;
int error;
if (va->numcfranges < 2) {
printf("ie_vme_match: need 2 ranges\n");
return 0;
}
if ((va->r[1].offset & 0xff0fffff) ||
((va->r[0].offset & 0xfff00000)
!= (va->r[1].offset & 0xfff00000))) {
printf("ie_vme_match: base address mismatch\n");
return 0;
}
if (va->r[0].size != VMECF_LEN_DEFAULT &&
va->r[0].size != sizeof(struct ievme)) {
printf("ie_vme_match: bad csr size\n");
return 0;
}
if (va->r[1].size == VMECF_LEN_DEFAULT) {
printf("ie_vme_match: must specify memory size\n");
return 0;
}
mod = 0x3d;
if (va->r[0].am != VMECF_AM_DEFAULT &&
va->r[0].am != mod)
return 0;
if (vme_space_alloc(va->va_vct, va->r[0].offset,
sizeof(struct ievme), mod))
return 0;
if (vme_space_alloc(va->va_vct, va->r[1].offset, va->r[1].size, mod)) {
vme_space_free(va->va_vct, va->r[0].offset,
sizeof(struct ievme), mod);
return 0;
}
error = vme_probe(ct, va->r[0].offset, 2, mod, VME_D16, 0, 0);
vme_space_free(va->va_vct, va->r[0].offset, sizeof(struct ievme), mod);
vme_space_free(va->va_vct, va->r[1].offset, va->r[1].size, mod);
return (error == 0);
}
void
ie_vme_attach(device_t parent, device_t self, void *aux)
{
uint8_t myaddr[ETHER_ADDR_LEN];
struct ie_vme_softc *vsc = device_private(self);
struct vme_attach_args *va = aux;
vme_chipset_tag_t ct = va->va_vct;
struct ie_softc *sc;
vme_intr_handle_t ih;
vme_addr_t rampaddr;
vme_size_t memsize;
vme_mapresc_t resc;
int lcv;
vme_am_t mod;
mod = 0x3d;
if (vme_space_alloc(va->va_vct, va->r[0].offset,
sizeof(struct ievme), mod) ||
vme_space_alloc(va->va_vct, va->r[1].offset,
va->r[1].size, mod))
panic("if_ie: vme alloc");
sc = &vsc->ie;
sc->sc_dev = self;
sc->hwreset = ie_vmereset;
sc->hwinit = ie_vmerun;
sc->chan_attn = ie_vmeattend;
sc->intrhook = ie_vmeintr;
sc->memcopyout = ie_memcopyout;
sc->memcopyin = ie_memcopyin;
sc->ie_bus_barrier = NULL;
sc->ie_bus_read16 = ie_vme_read16;
sc->ie_bus_write16 = ie_vme_write16;
sc->ie_bus_write24 = ie_vme_write24;
memsize = va->r[1].size;
if (vme_space_map(ct, va->r[0].offset, sizeof(struct ievme), mod,
VME_D16 | VME_D8, 0, &vsc->ievt, &vsc->ievh, &resc) != 0)
panic("if_ie: vme map csr");
rampaddr = va->r[1].offset;
rampaddr = rampaddr | ((read_iev(vsc, status) & IEVME_HADDR) << 16);
if (vme_space_map(ct, rampaddr, memsize, mod, VME_D16 | VME_D8, 0,
&sc->bt, &sc->bh, &resc) != 0)
panic("if_ie: vme map mem");
write_iev(vsc, pectrl, read_iev(vsc, pectrl) | IEVME_PARACK);
for (lcv = 0; lcv < IEVME_MAPSZ - 1; lcv++)
write_iev(vsc, pgmap[lcv], IEVME_SBORDR | IEVME_OBMEM | lcv);
write_iev(vsc, pgmap[IEVME_MAPSZ - 1], IEVME_SBORDR | IEVME_OBMEM | 0);
bus_space_set_region_2(sc->bt, sc->bh, 0, 0, memsize/2);
sc->scp = (IE_SCP_ADDR & (IEVME_PAGESIZE - 1));
sc->iscp = 0;
sc->scb = IE_ISCP_SZ;
ie_vme_write16(sc, IE_ISCP_SCB((long)sc->iscp), sc->scb);
ie_vme_write16(sc, IE_ISCP_BASE((u_long)sc->iscp), 0);
ie_vme_write24(sc, IE_SCP_ISCP((u_long)sc->scp), 0);
if (i82586_proberam(sc) == 0) {
printf(": memory probe failed\n");
return;
}
sc->buf_area = IEVME_PAGESIZE;
sc->buf_area_sz = memsize - IEVME_PAGESIZE;
sc->do_xmitnopchain = 0;
printf("\n%s:", device_xname(self));
ether_getaddr(self, myaddr);
i82586_attach(sc, "multibus/vme", myaddr, media, NMEDIA, media[0]);
vme_intr_map(ct, va->ilevel, va->ivector, &ih);
vme_intr_establish(ct, ih, IPL_NET, i82586_intr, sc);
}