#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_ie_gsc.c,v 1.5 2020/04/15 15:50:15 skrll Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <uvm/uvm_extern.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
#include <net/if_types.h>
#include <net/if_media.h>
#include <netinet/in.h>
#include <sys/bus.h>
#include <machine/intr.h>
#include <machine/iomod.h>
#include <machine/autoconf.h>
#include <hppa/dev/cpudevs.h>
#include <hppa/gsc/gscbusvar.h>
#include <hppa/hppa/machdep.h>
#include <dev/ic/i82586reg.h>
#include <dev/ic/i82586var.h>
#define I82596_DEBUG I82586_DEBUG
#ifdef __for_reference_only
struct ie_gsc_regs {
uint32_t ie_reset;
uint32_t ie_port;
uint32_t ie_attn;
};
#endif
#define IE_GSC_BANK_SZ (12)
#define IE_GSC_REG_RESET (0)
#define IE_GSC_REG_PORT (4)
#define IE_GSC_REG_ATTN (8)
#define IE_GSC_ALIGN(v) ((((u_int) (v)) + 0xf) & ~0xf)
#define IE_GSC_SYSBUS (IE_SYSBUS_596_RSVD_SET | \
IE_SYSBUS_596_82586 | \
IE_SYSBUS_596_INTLOW | \
IE_SYSBUS_596_TRGEXT | \
IE_SYSBUS_596_BE)
#define IE_SIZE 0x8000
struct ie_gsc_softc {
struct ie_softc ie;
bus_space_tag_t iot;
bus_space_handle_t ioh;
bus_dma_tag_t iemt;
void *sc_ih;
int flags;
#define IEGSC_GECKO (1 << 0)
};
int ie_gsc_probe(device_t, cfdata_t, void *);
void ie_gsc_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(ie_gsc, sizeof(struct ie_gsc_softc),
ie_gsc_probe, ie_gsc_attach, NULL, NULL);
static int ie_gsc_media[] = {
IFM_ETHER | IFM_10_2,
};
#define IE_NMEDIA __arraycount(ie_gsc_media)
void ie_gsc_reset(struct ie_softc *, int);
void ie_gsc_attend(struct ie_softc *, int);
void ie_gsc_run(struct ie_softc *);
void ie_gsc_port(struct ie_softc *, u_int);
uint16_t ie_gsc_read16(struct ie_softc *, int);
void ie_gsc_write16(struct ie_softc *, int, uint16_t);
void ie_gsc_write24(struct ie_softc *, int, int);
void ie_gsc_memcopyin(struct ie_softc *, void *, int, size_t);
void ie_gsc_memcopyout(struct ie_softc *, const void *, int, size_t);
void
ie_gsc_reset(struct ie_softc *sc, int what)
{
struct ie_gsc_softc *gsc = (struct ie_gsc_softc *)sc;
int i;
switch (what) {
case CHIP_PROBE:
bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_RESET, 0);
break;
case CARD_RESET:
bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_RESET, 0);
DELAY(1000);
ie_gsc_port(sc, IE_PORT_ALT_SCP);
ie_gsc_attend(sc, what);
for (i = 9000; i-- && ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp));
DELAY(100))
pdcache(0, (vaddr_t)sc->sc_maddr + sc->iscp,
IE_ISCP_SZ);
#if I82596_DEBUG
if (i < 0) {
printf("timeout for PORT command (%x)%s\n",
ie_gsc_read16(sc, IE_ISCP_BUSY(sc->iscp)),
(gsc->flags & IEGSC_GECKO)? " on gecko":"");
return;
}
#endif
break;
}
}
void
ie_gsc_attend(struct ie_softc *sc, int why)
{
struct ie_gsc_softc *gsc = (struct ie_gsc_softc *)sc;
bus_space_write_4(gsc->iot, gsc->ioh, IE_GSC_REG_ATTN, 0);
}
void
ie_gsc_run(struct ie_softc *sc)
{
}
void
ie_gsc_port(struct ie_softc *sc, u_int cmd)
{
struct ie_gsc_softc *gsc = (struct ie_gsc_softc *)sc;
switch (cmd) {
case IE_PORT_RESET:
case IE_PORT_DUMP:
break;
case IE_PORT_SELF_TEST:
cmd |= (sc->sc_dmamap->dm_segs[0].ds_addr + 0);
break;
case IE_PORT_ALT_SCP:
cmd |= (sc->sc_dmamap->dm_segs[0].ds_addr + sc->scp);
break;
}
if (gsc->flags & IEGSC_GECKO) {
bus_space_write_4(gsc->iot, gsc->ioh,
IE_GSC_REG_PORT, (cmd & 0xffff));
DELAY(1000);
bus_space_write_4(gsc->iot, gsc->ioh,
IE_GSC_REG_PORT, (cmd >> 16));
DELAY(1000);
} else {
bus_space_write_4(gsc->iot, gsc->ioh,
IE_GSC_REG_PORT, (cmd >> 16));
DELAY(1000);
bus_space_write_4(gsc->iot, gsc->ioh,
IE_GSC_REG_PORT, (cmd & 0xffff));
DELAY(1000);
}
}
uint16_t
ie_gsc_read16(struct ie_softc *sc, int offset)
{
uint16_t val;
__asm volatile(
" ldh 0(%1), %0 \n"
" fdc %%r0(%1) \n"
: "=&r" (val)
: "r" ((char *)sc->sc_maddr + offset));
return val;
}
void
ie_gsc_write16(struct ie_softc *sc, int offset, uint16_t v)
{
__asm volatile(
" sth %0, 0(%1) \n"
" fdc %%r0(%1) \n"
:
: "r" (v), "r" ((char *)sc->sc_maddr + offset));
}
void
ie_gsc_write24(struct ie_softc *sc, int offset, int addr)
{
addr += sc->sc_dmamap->dm_segs[0].ds_addr;
__asm volatile(
" ldi 2, %%r21 \n"
" extru %0, 15, 16, %%r22 \n"
" sth %0, 0(%1) \n"
" sth %%r22, 2(%1) \n"
" fdc %%r0(%1) \n"
" fdc %%r21(%1) \n"
:
: "r" (addr), "r" ((char *)sc->sc_maddr + offset)
: "r21", "r22");
}
void
ie_gsc_memcopyin(struct ie_softc *sc, void *p, int offset, size_t size)
{
struct ie_gsc_softc *gsc = (struct ie_gsc_softc *)sc;
if (size == 0)
return;
memcpy(p, (char *)sc->sc_maddr + offset, size);
bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, offset, size,
BUS_DMASYNC_PREREAD);
hppa_led_blink(HPPA_LED_NETRCV);
}
void
ie_gsc_memcopyout(struct ie_softc *sc, const void *p, int offset, size_t size)
{
struct ie_gsc_softc *gsc = (struct ie_gsc_softc *)sc;
if (size == 0)
return;
memcpy((char *)sc->sc_maddr + offset, p, size);
bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, offset, size,
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
hppa_led_blink(HPPA_LED_NETSND);
}
int i82596_probe(struct ie_softc *);
int
i82596_probe(struct ie_softc *sc)
{
struct ie_gsc_softc *gsc = (struct ie_gsc_softc *)sc;
int i;
sc->ie_bus_write16(sc, IE_SCP_BUS_USE(sc->scp), IE_GSC_SYSBUS);
sc->ie_bus_write24(sc, IE_SCP_ISCP(sc->scp), sc->iscp);
sc->ie_bus_write16(sc, IE_ISCP_SCB(sc->iscp), sc->scb);
sc->ie_bus_write24(sc, IE_ISCP_BASE(sc->iscp), 0);
sc->ie_bus_write16(sc, IE_ISCP_BUSY(sc->iscp), 1);
bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
sc->hwreset(sc, CARD_RESET);
if (sc->ie_bus_read16(sc, IE_ISCP_BUSY(sc->iscp))) {
#if I82596_DEBUG
printf ("%s: ISCP set failed\n", device_xname(sc->sc_dev));
#endif
return 0;
}
sc->ie_bus_write24(sc, 0, -sc->sc_dmamap->dm_segs[0].ds_addr);
sc->ie_bus_write24(sc, 4, -(sc->sc_dmamap->dm_segs[0].ds_addr + 1));
bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
ie_gsc_port(sc, IE_PORT_SELF_TEST);
for (i = 9000; i-- && sc->ie_bus_read16(sc, 4); DELAY(100))
pdcache(0, (vaddr_t)sc->sc_maddr, sc->sc_msize);
#if I82596_DEBUG
printf (": test %x:%x\n%s",
*((volatile int32_t *)((char *)sc->sc_maddr + 0)),
*((volatile int32_t *)((char *)sc->sc_maddr + 4)),
device_xname(sc->sc_dev));
#endif
return 1;
}
int
ie_gsc_probe(device_t parent, cfdata_t match, void *aux)
{
struct gsc_attach_args *ga = aux;
if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
(ga->ga_type.iodc_sv_model != HPPA_FIO_LAN &&
ga->ga_type.iodc_sv_model != HPPA_FIO_GLAN))
return 0;
return 1;
}
void
ie_gsc_attach(device_t parent, device_t self, void *aux)
{
struct ie_gsc_softc *gsc = device_private(self);
struct ie_softc *sc = &gsc->ie;
struct gsc_attach_args *ga = aux;
bus_dma_segment_t seg;
int rseg;
int rv;
uint8_t myaddr[ETHER_ADDR_LEN];
if (ga->ga_type.iodc_sv_model == HPPA_FIO_GLAN)
gsc->flags |= IEGSC_GECKO;
if (bus_space_map(ga->ga_iot, ga->ga_hpa,
IE_GSC_BANK_SZ, 0, &gsc->ioh)) {
printf(": can't map i/o space\n");
return;
}
sc->sc_dev = self;
gsc->iot = ga->ga_iot;
gsc->iemt = ga->ga_dmatag;
sc->bt = ga->ga_iot;
sc->sc_msize = IE_SIZE;
if (bus_dmamem_alloc(gsc->iemt, sc->sc_msize, PAGE_SIZE, 0,
&seg, 1, &rseg, BUS_DMA_NOWAIT | BUS_DMA_24BIT)) {
printf (": can't allocate %d bytes of DMA memory\n",
sc->sc_msize);
return;
}
if (bus_dmamem_map(gsc->iemt, &seg, rseg, sc->sc_msize,
(void **)&sc->sc_maddr, BUS_DMA_NOWAIT)) {
printf (": can't map DMA memory\n");
bus_dmamem_free(gsc->iemt, &seg, rseg);
return;
}
if (bus_dmamap_create(gsc->iemt, sc->sc_msize, rseg, sc->sc_msize,
0, BUS_DMA_NOWAIT, &sc->sc_dmamap)) {
printf(": can't create DMA map\n");
bus_dmamem_unmap(gsc->iemt,
(void *)sc->sc_maddr, sc->sc_msize);
bus_dmamem_free(gsc->iemt, &seg, rseg);
return;
}
if (bus_dmamap_load(gsc->iemt, sc->sc_dmamap, sc->sc_maddr,
sc->sc_msize, NULL, BUS_DMA_NOWAIT)) {
printf(": can't load DMA map\n");
bus_dmamap_destroy(gsc->iemt, sc->sc_dmamap);
bus_dmamem_unmap(gsc->iemt,
(void *)sc->sc_maddr, sc->sc_msize);
bus_dmamem_free(gsc->iemt, &seg, rseg);
return;
}
#if 1
sc->bh = (bus_space_handle_t)sc->sc_maddr;
#endif
#if I82596_DEBUG
printf(" mem %x[%p]/%x\n%s",
(u_int)sc->sc_dmamap->dm_segs[0].ds_addr,
sc->sc_maddr,
sc->sc_msize,
device_xname(self));
sc->sc_debug = IED_ALL;
#endif
sc->hwreset = ie_gsc_reset;
sc->chan_attn = ie_gsc_attend;
sc->hwinit = ie_gsc_run;
sc->memcopyout = ie_gsc_memcopyout;
sc->memcopyin = ie_gsc_memcopyin;
sc->ie_bus_read16 = ie_gsc_read16;
sc->ie_bus_write16 = ie_gsc_write16;
sc->ie_bus_write24 = ie_gsc_write24;
sc->intrhook = NULL;
memset(sc->sc_maddr, 0, sc->sc_msize);
bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
sc->scp = IE_GSC_ALIGN(8);
sc->iscp = IE_GSC_ALIGN(sc->scp + IE_SCP_SZ);
sc->scb = IE_GSC_ALIGN(sc->iscp + IE_ISCP_SZ);
sc->buf_area = IE_GSC_ALIGN(sc->scb + IE_SCB_SZ);
sc->buf_area_sz = sc->sc_msize - sc->buf_area;
rv = i82596_probe(sc);
if (!rv) {
bus_dmamap_destroy(gsc->iemt, sc->sc_dmamap);
bus_dmamem_unmap(gsc->iemt,
(void *)sc->sc_maddr, sc->sc_msize);
bus_dmamem_free(gsc->iemt, &seg, rseg);
return;
}
if (!rv)
return;
memcpy(myaddr, ga->ga_ether_address, ETHER_ADDR_LEN);
sc->ie_bus_write16(sc, IE_SCP_BUS_USE(sc->scp), IE_GSC_SYSBUS);
sc->ie_bus_write24(sc, IE_SCP_ISCP(sc->scp), sc->iscp);
sc->ie_bus_write16(sc, IE_ISCP_SCB(sc->iscp), sc->scb);
sc->ie_bus_write24(sc, IE_ISCP_BASE(sc->iscp), 0);
sc->ie_bus_write16(sc, IE_ISCP_BUSY(sc->iscp), 1);
bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
sc->hwreset(sc, CARD_RESET);
bus_dmamap_sync(gsc->iemt, sc->sc_dmamap, 0, sc->sc_msize,
BUS_DMASYNC_PREREAD);
printf(": v%d.%d", ga->ga_type.iodc_model, ga->ga_type.iodc_sv_rev);
i82586_attach(sc,
(gsc->flags & IEGSC_GECKO) ?
"LASI/i82596CA" :
"i82596DX",
myaddr, ie_gsc_media, IE_NMEDIA, ie_gsc_media[0]);
gsc->sc_ih = hppa_intr_establish(IPL_NET, i82586_intr, sc,
ga->ga_ir, ga->ga_irq);
}