#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_ne_pbus.c,v 1.20 2019/05/29 06:21:56 msaitoh Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/socket.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/bus.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#include <net/if_media.h>
#include <machine/intr.h>
#include <machine/io.h>
#include <dev/ic/dp8390reg.h>
#include <dev/ic/dp8390var.h>
#include <dev/ic/ne2000reg.h>
#include <dev/ic/ne2000var.h>
#include <dev/ic/dp83905reg.h>
#include <dev/ic/dp83905var.h>
#include <dev/ic/mx98905var.h>
#include <arch/acorn32/podulebus/podulebus.h>
#include <arch/acorn32/podulebus/if_ne_pbusreg.h>
#include <dev/podulebus/podules.h>
struct ne_pbus_softc {
struct ne2000_softc sc_ne2000;
int sc_podule_number;
podule_t *sc_podule;
struct bus_space sc_tag;
irqhandler_t *sc_ih;
struct evcnt sc_intrcnt;
bus_space_handle_t sc_extrah;
};
static int ne_pbus_probe (device_t, cfdata_t , void *);
static void ne_pbus_attach (device_t, device_t, void *);
CFATTACH_DECL_NEW(ne_pbus, sizeof(struct ne_pbus_softc),
ne_pbus_probe, ne_pbus_attach, NULL, NULL);
static uint8_t *em_ea (struct ne_pbus_softc *sc, uint8_t *buffer);
static void em_postattach (struct ne_pbus_softc *sc);
static void eh600_postattach (struct ne_pbus_softc *sc);
static void eh600_preattach (struct ne_pbus_softc *sc);
static uint8_t *eh600_ea (struct ne_pbus_softc *sc, uint8_t *buffer);
void eh600_init_media (struct dp8390_softc *);
void en_postattach (struct ne_pbus_softc *);
void en_init_media (struct dp8390_softc *);
struct ne_clone {
int product;
unsigned int cookie;
unsigned int nicbase;
unsigned int nicsize;
unsigned int asicbase;
unsigned int asicsize;
unsigned int extrabase;
unsigned int extrasize;
unsigned char nicspace;
unsigned char asicspace;
unsigned char extraspace;
#define NE_SPACE_FAST 0
#define NE_SPACE_MOD 1
#define NE_SPACE_EASI 2
unsigned char reserved0;
const char *name;
uint8_t * (*getea)
(struct ne_pbus_softc *sc, uint8_t *buffer);
void (*preattach)
(struct ne_pbus_softc *sc);
void (*postattach)
(struct ne_pbus_softc *sc);
int (*mediachange)
(struct dp8390_softc *);
void (*mediastatus)
(struct dp8390_softc *, struct ifmediareq *);
void (*init_card)
(struct dp8390_softc *);
void (*init_media)
(struct dp8390_softc *);
} ne_clones[] = {
{
PODULE_ETHERM, EM_REGSHIFT,
EM_NIC_OFFSET, EM_NIC_SIZE, EM_ASIC_OFFSET, EM_ASIC_SIZE,
0,0, NE_SPACE_FAST,
NE_SPACE_FAST, NE_SPACE_FAST, 0,
"EtherM", em_ea, NULL, em_postattach,
NULL,NULL,NULL,NULL
},
{
PODULE_ETHERLAN600, EH600_REGSHIFT,
EH600_NIC_OFFSET, EH600_NIC_SIZE, EH600_ASIC_OFFSET, EH600_ASIC_SIZE,
EH600_CONTROL_OFFSET, EH600_CONTROL_SIZE, NE_SPACE_FAST,
NE_SPACE_FAST, NE_SPACE_FAST, 0,
"EtherLan 600", eh600_ea, eh600_preattach, eh600_postattach,
dp83905_mediachange, dp83905_mediastatus, dp83905_init_card,
eh600_init_media
},
{
PODULE_ETHERLAN600AEH, EH600_REGSHIFT,
EH600_NIC_OFFSET, EH600_NIC_SIZE, EH600_ASIC_OFFSET, EH600_ASIC_SIZE,
EH600_CONTROL_OFFSET, EH600_CONTROL_SIZE, NE_SPACE_FAST,
NE_SPACE_FAST, NE_SPACE_FAST, 0,
"EtherLan 600A", eh600_ea , eh600_preattach, eh600_postattach,
dp83905_mediachange, dp83905_mediastatus, dp83905_init_card,
eh600_init_media
},
{
PODULE_ETHERN, EN_REGSHIFT,
EN_NIC_OFFSET, EN_NIC_SIZE, EN_ASIC_OFFSET, EN_ASIC_SIZE,
0,0, NE_SPACE_EASI,
NE_SPACE_EASI, NE_SPACE_EASI, 0,
"EtherN", em_ea, NULL, en_postattach,
dp83905_mediachange, dp83905_mediastatus, dp83905_init_card,
en_init_media
},
{
PODULE_ETHERI, EN_REGSHIFT,
EN_NIC_OFFSET, EN_NIC_SIZE, EN_ASIC_OFFSET, EN_ASIC_SIZE,
0,0, NE_SPACE_EASI,
NE_SPACE_EASI, NE_SPACE_EASI, 0,
"EtherI", em_ea, NULL, en_postattach,
dp83905_mediachange, dp83905_mediastatus, dp83905_init_card,
en_init_media
},
};
static int
ne_pbus_probe(device_t parent, cfdata_t cf, void *aux)
{
struct podule_attach_args *pa = (void *)aux;
int loop;
for (loop = 0; loop < sizeof(ne_clones) / sizeof(struct ne_clone);
++loop) {
if (pa->pa_product == ne_clones[loop].product)
return 1;
}
return 0;
}
static void
ne_pbus_attach(device_t parent, device_t self, void *aux)
{
struct podule_attach_args *pa = (void *)aux;
struct ne_pbus_softc *npsc = device_private(self);
struct ne2000_softc *nsc = &npsc->sc_ne2000;
struct dp8390_softc *dsc = &nsc->sc_dp8390;
struct ne_clone *ne = NULL;
uint8_t buffer[6];
uint8_t *myea;
int loop;
dsc->sc_dev = self;
if (pa->pa_podule_number == -1)
panic("Podule has disappeared !");
npsc->sc_podule_number = pa->pa_podule_number;
npsc->sc_podule = pa->pa_podule;
podules[npsc->sc_podule_number].attached = 1;
for (loop = 0; loop < sizeof(ne_clones) / sizeof(struct ne_clone);
++loop) {
if (pa->pa_product == ne_clones[loop].product) {
ne = &ne_clones[loop];
break;
}
}
#ifdef DIAGNOSTIC
if (ne == NULL)
panic("Podule has vanished");
#endif
switch (ne->nicspace) {
case NE_SPACE_EASI:
ne->nicbase += npsc->sc_podule->easi_base;
break;
case NE_SPACE_MOD:
ne->nicbase += npsc->sc_podule->mod_base;
break;
case NE_SPACE_FAST:
default:
ne->nicbase += npsc->sc_podule->fast_base;
break;
}
switch (ne->asicspace) {
case NE_SPACE_EASI:
ne->asicbase += npsc->sc_podule->easi_base;
break;
case NE_SPACE_MOD:
ne->asicbase += npsc->sc_podule->mod_base;
break;
case NE_SPACE_FAST:
default:
ne->asicbase += npsc->sc_podule->fast_base;
break;
}
switch (ne->extraspace) {
case NE_SPACE_EASI:
ne->extrabase += npsc->sc_podule->easi_base;
break;
case NE_SPACE_MOD:
ne->extrabase += npsc->sc_podule->mod_base;
break;
case NE_SPACE_FAST:
default:
ne->extrabase += npsc->sc_podule->fast_base;
break;
}
aprint_normal(": %s ethernet\n", ne->name);
npsc->sc_tag = *pa->pa_iot;
npsc->sc_tag.bs_cookie = (void *) ne->cookie;
dsc->sc_regt = &npsc->sc_tag;
nsc->sc_asict = dsc->sc_regt;
if (bus_space_map(dsc->sc_regt, ne->nicbase, ne->nicsize,
0, &dsc->sc_regh)) {
aprint_error_dev(self, "cannot map i/o space\n");
return;
}
if (bus_space_map(nsc->sc_asict, ne->asicbase, ne->asicsize,
0, &nsc->sc_asich)) {
aprint_error_dev(self, "cannot map i/o space\n");
return;
}
if (ne->extrasize > 0) {
if (bus_space_map(&npsc->sc_tag, ne->extrabase, ne->extrasize,
0, &npsc->sc_extrah)) {
aprint_error_dev(self, "cannot map extra space\n");
return;
}
}
dsc->sc_enabled = 1;
if (ne->getea)
myea = ne->getea(npsc, buffer);
else
myea = NULL;
if (ne->preattach)
ne->preattach(npsc);
if (ne->init_media) {
dsc->sc_mediachange = ne->mediachange;
dsc->sc_mediastatus = ne->mediastatus;
dsc->init_card = ne->init_card;
dsc->sc_media_init = ne->init_media;
#if 0
int *media = NULL, nmedia = 0, defmedia = 0;
ne->init_media(dsc, &media, &nmedia, &defmedia);
#endif
}
ne2000_attach(nsc, myea);
aprint_normal_dev(self, "");
switch (nsc->sc_type) {
case NE2000_TYPE_NE1000:
aprint_normal("NE1000");
break;
case NE2000_TYPE_NE2000:
aprint_normal("NE2000");
break;
case NE2000_TYPE_AX88190:
aprint_normal("AX88190");
break;
case NE2000_TYPE_DL10019:
aprint_normal("DL10019");
break;
case NE2000_TYPE_DL10022:
aprint_normal("DL10022");
break;
default:
printf("??");
};
aprint_normal(" chipset, %d Kb memory\n", dsc->mem_start/1024);
if (ne->postattach)
ne->postattach(npsc);
evcnt_attach_dynamic(&npsc->sc_intrcnt, EVCNT_TYPE_INTR, NULL,
device_xname(self), "intr");
npsc->sc_ih = podulebus_irq_establish(pa->pa_ih, IPL_NET, dp8390_intr,
dsc, &npsc->sc_intrcnt);
if (npsc->sc_ih == NULL)
panic("%s: Cannot install interrupt handler",
device_xname(self));
npsc->sc_ih->ih_maskaddr = npsc->sc_podule->irq_addr;
npsc->sc_ih->ih_maskbits = npsc->sc_podule->irq_mask;
}
static uint8_t *
em_ea(struct ne_pbus_softc *sc, uint8_t *buffer)
{
netslot_ea(buffer);
return buffer;
}
static void
em_postattach(struct ne_pbus_softc *sc)
{
int dsr;
aprint_normal_dev(sc->sc_ne2000.sc_dp8390.sc_dev,
"16KB buffer memory");
dsr = bus_space_read_1(sc->sc_ne2000.sc_asict,
sc->sc_ne2000.sc_asich, EM_DSR_REG);
if (!(dsr & EM_DSR_20M))
aprint_normal(", VCO faulty");
if (!(dsr & EM_DSR_TCOK))
aprint_normal(", TxClk faulty");
if (dsr & EM_DSR_POL)
aprint_normal(", UTP reverse polarity");
if (dsr & EM_DSR_JAB)
aprint_normal(", jabber");
if (dsr & EM_DSR_LNK)
aprint_normal(", link OK");
if (dsr & EM_DSR_LBK)
aprint_normal(", loopback");
if (dsr & EM_DSR_UTP)
aprint_normal(", UTP");
aprint_normal("\n");
}
static void
eh600_preattach(struct ne_pbus_softc *sc)
{
u_char tmp;
struct ne2000_softc *nsc = &sc->sc_ne2000;
struct dp8390_softc *dsc = &nsc->sc_dp8390;
bus_space_tag_t nict = dsc->sc_regt;
bus_space_handle_t nich = dsc->sc_regh;
bus_space_read_1(nict, nich, DP83905_MCRA);
bus_space_write_1(nict, nich, DP83905_MCRA, DP83905_MCRA_INT3);
tmp = bus_space_read_1(&sc->sc_tag, sc->sc_extrah, 0);
tmp |= EH_INTR_MASK;
bus_space_write_1(&sc->sc_tag, sc->sc_extrah, 0, tmp);
}
static void
eh600_postattach(struct ne_pbus_softc *sc)
{
struct ne2000_softc *nsc = &sc->sc_ne2000;
struct dp8390_softc *dsc = &nsc->sc_dp8390;
dsc->mem_start = EH600_MEM_START;
dsc->mem_size = EH600_MEM_END - EH600_MEM_START;
dsc->mem_end = EH600_MEM_END;
dsc->txb_cnt = 3;
dsc->mem_ring =
dsc->mem_start +
(((dsc->txb_cnt + 1) * ED_TXBUF_SIZE ) <<
ED_PAGE_SHIFT);
dsc->tx_page_start = dsc->mem_start >> ED_PAGE_SHIFT;
dsc->rec_page_start = dsc->tx_page_start +
(dsc->txb_cnt + 1) * ED_TXBUF_SIZE;
dsc->rec_page_stop = dsc->tx_page_start +
(dsc->mem_size >> ED_PAGE_SHIFT);
aprint_normal_dev(dsc->sc_dev, "32KB buffer memory\n");
}
void eh600_init_media(struct dp8390_softc *sc)
{
static int eh600_media[] = {
IFM_ETHER | IFM_AUTO,
IFM_ETHER | IFM_10_T,
IFM_ETHER | IFM_10_2,
};
int i, defmedia = IFM_ETHER | IFM_AUTO;
static const int eh600_nmedia = __arraycount(eh600_media);
aprint_normal_dev(sc->sc_dev,
"10base2, 10baseT, auto, default auto\n");
ifmedia_init(&sc->sc_media, 0, dp8390_mediachange, dp8390_mediastatus);
for (i = 0; i < eh600_nmedia; i++)
ifmedia_add(&sc->sc_media, eh600_media[i], 0, NULL);
ifmedia_set(&sc->sc_media, defmedia);
}
void
en_postattach(struct ne_pbus_softc *sc)
{
mx98905_attach(&sc->sc_ne2000.sc_dp8390);
}
void
en_init_media(struct dp8390_softc *sc)
{
static int en_media[] = {
IFM_ETHER | IFM_10_T
};
aprint_normal_dev(sc->sc_dev, "10baseT, default 10baseT\n");
ifmedia_init(&sc->sc_media, 0, dp8390_mediachange, dp8390_mediastatus);
ifmedia_add(&sc->sc_media, en_media[0], 0, NULL);
ifmedia_set(&sc->sc_media, en_media[0]);
}
#define POD_READ(addr) \
podule->read_rom(podule->sync_base, addr)
static uint8_t *
eh600_ea(struct ne_pbus_softc *sc, uint8_t *buffer)
{
podule_t *podule = sc->sc_podule;
u_int address;
u_int id;
address = 0x40;
memset(buffer, 0, 6);
do {
id = POD_READ(address);
if (id == 0xf5) {
u_int size;
u_int pod_addr;
int loop;
size = POD_READ(address + 4);
size |= (POD_READ(address + 8) << 8);
size |= (POD_READ(address + 12) << 16);
pod_addr = POD_READ(address + 16);
pod_addr |= (POD_READ(address + 20) << 8);
pod_addr |= (POD_READ(address + 24) << 16);
pod_addr |= (POD_READ(address + 28) << 24);
if (pod_addr < 0x800) {
uint8_t tmp;
int addr_index = 0;
int found_ether = 0;
for (loop = 0; loop < size; ++loop) {
if (found_ether) {
tmp = POD_READ((pod_addr + loop) * 4);
if (tmp >= '0' && tmp <= '9') {
buffer[addr_index >> 1] |= (tmp - '0') << ((addr_index & 1) ? 0 : 4);
++addr_index;
}
else if (tmp >= 'a' && tmp <= 'f'){
buffer[addr_index >> 1] |= (10 + (tmp - 'a')) << ((addr_index & 1) ? 0 : 4);
++addr_index;
}
else if (tmp >= 'A' && tmp <= 'F'){
buffer[addr_index >> 1] |= (10 + (tmp - 'A')) << ((addr_index & 1) ? 0 : 4);
++addr_index;
}
else if (tmp == ')') {
break;
}
}
if (POD_READ((pod_addr + loop) * 4) == '(') {
found_ether = 1;
}
}
if (!found_ether)
netslot_ea(buffer);
return buffer;
}
}
address += 32;
} while (id != 0 && address < 0x8000);
netslot_ea(buffer);
return buffer;
}