Created
September 9, 2019 15:53
-
-
Save tsutsui/9fdae0d4e940496f68ffe6815cf888c7 to your computer and use it in GitHub Desktop.
WIP NetBSD/dreamcast driver for the NE2000 in 8bit mode over G2bus simple bus mode bridge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $NetBSD$ */ | |
/* | |
* Copyright (c) 2016 Izumi Tsutsui. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
/* | |
* Device driver for the NE2000 in 8bit mode over G2bus simple bus mode bridge. | |
*/ | |
#include <sys/cdefs.h> | |
__KERNEL_RCSID(0, "$NetBSD$"); | |
#include <sys/param.h> | |
#include <sys/systm.h> | |
#include <sys/device.h> | |
#include <sys/kernel.h> | |
#include <sys/bus.h> | |
#include <sys/intr.h> | |
#include <net/if.h> | |
#include <net/if_dl.h> | |
#include <net/if_ether.h> | |
#include <net/if_media.h> | |
#include <dev/ic/dp8390reg.h> | |
#include <dev/ic/dp8390var.h> | |
#include <dev/ic/ne2000reg.h> | |
#include <dev/ic/ne2000var.h> | |
#include <machine/sysasicvar.h> | |
#include <dreamcast/dev/g2/g2busvar.h> | |
#define G2_SIMPLE_BUS_STRIDE 2 | |
#define G2_SIMPLE_BUS_ADDR 0x00600000 | |
#define G2_NE2000_ADDR(isaio) \ | |
(G2_SIMPLE_BUS_ADDR + (((isaio) & 0x1ff) << G2_SIMPLE_BUS_STRIDE)) | |
#define G2_NE2000_ISA_ADDR 0x280 | |
#define G2_NE2000_BASE G2_NE2000_ADDR(G2_NE2000_ISA_ADDR) | |
struct ne_g2_softc { | |
struct ne2000_softc sc_ne2000; /* MI ne2000 softc */ | |
struct dreamcast_bus_space sc_bs; | |
}; | |
static int ne_g2_probe(device_t, cfdata_t, void *); | |
static void ne_g2_attach(device_t, device_t, void *); | |
static void g2bus_ne_set_bus_mem(bus_space_tag_t); | |
static uint8_t g2bus_ne_bus_mem_read_1(void *, bus_space_handle_t, | |
bus_size_t); | |
static void g2bus_ne_bus_mem_write_1(void *, bus_space_handle_t, | |
bus_size_t, uint8_t); | |
static void g2bus_ne_bus_mem_read_multi_1(void *, bus_space_handle_t, | |
bus_size_t, uint8_t *, bus_size_t); | |
static void g2bus_ne_bus_mem_write_multi_1(void *, bus_space_handle_t, | |
bus_size_t, const uint8_t *, bus_size_t); | |
CFATTACH_DECL_NEW(ne_g2bus, sizeof(struct ne_g2_softc), | |
ne_g2_probe, ne_g2_attach, NULL, NULL); | |
static int | |
ne_g2_probe(device_t parent, cfdata_t cf, void *aux) | |
{ | |
struct g2bus_attach_args *ga = aux; | |
static bool ne_matched = false; | |
struct dreamcast_bus_space bs; | |
bus_space_tag_t iot, asict; | |
bus_space_handle_t ioh, asich; | |
int netype, rv; | |
rv = 0; | |
if (ne_matched) | |
goto out; | |
*(volatile uint32_t *)0xa05f7898 = 0xff; | |
*(volatile uint32_t *)0xa05f789c = 0x16; | |
#if 0 | |
for (;;){ | |
volatile uint8_t *p; | |
int i; | |
for (i = 0; i < 0x200; i++) { | |
p = (void *)(0xa0600000 + i * 4); | |
if (i % 16 == 0) | |
printf("%04x: ", i); | |
printf("%02x ", *p); | |
*p = 0; | |
if (i % 16 == 15) | |
printf("\n"); | |
} | |
} | |
#endif | |
iot = &bs; | |
memcpy(iot, ga->ga_memt, sizeof(*iot)); | |
g2bus_ne_set_bus_mem(iot); | |
/* map I/O space */ | |
if (bus_space_map(iot, G2_NE2000_BASE, | |
NE2000_NPORTS << G2_SIMPLE_BUS_STRIDE, 0, &ioh) != 0) | |
goto out; | |
asict = iot; | |
if (bus_space_subregion(iot, ioh, | |
NE2000_ASIC_OFFSET << G2_SIMPLE_BUS_STRIDE, | |
NE2000_ASIC_NPORTS << G2_SIMPLE_BUS_STRIDE, &asich)) | |
goto out1; | |
/* Look for an NE2000 compatible card */ | |
netype = ne2000_detect(iot, ioh, asict, asich); | |
switch (netype) { | |
/* XXX should we reject non RTL8019 variants? */ | |
case NE2000_TYPE_NE1000: | |
case NE2000_TYPE_NE2000: | |
case NE2000_TYPE_RTL8019: | |
ne_matched = true; | |
rv = 1; | |
break; | |
default: | |
break; | |
} | |
out1: | |
bus_space_unmap(iot, ioh, NE2000_NPORTS << G2_SIMPLE_BUS_STRIDE); | |
out: | |
return rv; | |
} | |
static void | |
ne_g2_attach(device_t parent, device_t self, void *aux) | |
{ | |
struct g2bus_attach_args *ga = aux; | |
struct ne_g2_softc *sc = device_private(self); | |
struct ne2000_softc *nsc = &sc->sc_ne2000; | |
struct dp8390_softc *dsc = &nsc->sc_dp8390; | |
bus_space_tag_t iot, asict; | |
bus_space_handle_t ioh, asich; | |
const char *typestr; | |
int netype; | |
dsc->sc_dev = self; | |
aprint_normal(": NE2000 via G2bus simple bus bridge\n"); | |
iot = &sc->sc_bs; | |
memcpy(iot, ga->ga_memt, sizeof(sc->sc_bs)); | |
g2bus_ne_set_bus_mem(iot); | |
/* map I/O space */ | |
if (bus_space_map(iot, G2_NE2000_BASE, | |
NE2000_NPORTS << G2_SIMPLE_BUS_STRIDE, 0, &ioh) != 0) | |
goto out; | |
asict = iot; | |
if (bus_space_subregion(iot, ioh, | |
NE2000_ASIC_OFFSET << G2_SIMPLE_BUS_STRIDE, | |
NE2000_ASIC_NPORTS << G2_SIMPLE_BUS_STRIDE, &asich)) | |
goto out1; | |
dsc->sc_regt = iot; | |
dsc->sc_regh = ioh; | |
nsc->sc_asict = asict; | |
nsc->sc_asich = asich; | |
/* G2bus simple bus mode is 8-bit bus */ | |
nsc->sc_quirk = NE2000_QUIRK_8BIT; | |
/* | |
* detect it again, so we can print some information about | |
* the interface. | |
* XXX: Should we accept only RTL8019? | |
*/ | |
netype = ne2000_detect(iot, ioh, asict, asich); | |
switch (netype) { | |
case NE2000_TYPE_NE1000: | |
typestr = "NE1000"; | |
break; | |
case NE2000_TYPE_NE2000: | |
typestr = "NE2000"; | |
break; | |
case NE2000_TYPE_RTL8019: | |
typestr = "NE2000 (RTL8019)"; | |
break; | |
default: | |
aprint_error_dev(self, "where did the card go?!\n"); | |
goto out1; | |
} | |
aprint_normal_dev(self, "%s Ethernet (8-bit)\n", typestr); | |
/* this interface is always enabled */ | |
dsc->sc_enabled = 1; | |
/* call MI ne2000 attach function */ | |
ne2000_attach(nsc, NULL); | |
sysasic_intr_establish(SYSASIC_EVENT_8BIT, IPL_NET, SYSASIC_IRL11, | |
dp8390_intr, dsc); | |
return; | |
out1: | |
bus_space_unmap(iot, ioh, NE2000_NPORTS << G2_SIMPLE_BUS_STRIDE); | |
out: | |
return; | |
} | |
/* | |
* G2bus write access functions for slow ISA NE2000 | |
*/ | |
#define G2LOCK_DECL \ | |
int __s | |
#define G2_LOCK() \ | |
do { \ | |
__s = _cpu_intr_suspend(); \ | |
} while (/*CONSTCOND*/0) | |
#define G2_DRAIN() \ | |
do { \ | |
while ((*(volatile uint32_t *)0xa05f688c) & 0x30) \ | |
continue; \ | |
} while (/*CONSTCOND*/0) | |
#define G2_UNLOCK() \ | |
do { \ | |
_cpu_intr_resume(__s); \ | |
} while (/*CONSTCOND*/0) | |
static void | |
g2bus_ne_set_bus_mem(bus_space_tag_t memt) | |
{ | |
g2bus_set_bus_mem_sparse(memt); | |
/* overwrite write functions used by ne2000.c in 8 bit mode */ | |
memt->dbs_r_1 = g2bus_ne_bus_mem_read_1; | |
memt->dbs_w_1 = g2bus_ne_bus_mem_write_1; | |
memt->dbs_rm_1 = g2bus_ne_bus_mem_read_multi_1; | |
memt->dbs_wm_1 = g2bus_ne_bus_mem_write_multi_1; | |
} | |
static uint8_t | |
g2bus_ne_bus_mem_read_1(void *v, bus_space_handle_t sh, bus_size_t off) | |
{ | |
G2LOCK_DECL; | |
uint8_t rv; | |
G2_LOCK(); | |
G2_DRAIN(); | |
rv = *(volatile uint8_t *)(sh + (off * 4)); | |
G2_UNLOCK(); | |
return rv; | |
} | |
static void | |
g2bus_ne_bus_mem_write_1(void *v, bus_space_handle_t sh, bus_size_t off, | |
uint8_t val) | |
{ | |
G2LOCK_DECL; | |
G2_LOCK(); | |
G2_DRAIN(); | |
*(volatile uint8_t *)(sh + (off * 4)) = val; | |
G2_UNLOCK(); | |
} | |
static void | |
g2bus_ne_bus_mem_read_multi_1(void *v, bus_space_handle_t sh, | |
bus_size_t off, uint8_t *addr, bus_size_t len) | |
{ | |
G2LOCK_DECL; | |
volatile uint8_t * const baddr = (uint8_t *)(sh + (off * 4)); | |
G2_LOCK(); | |
while (len--) { | |
G2_DRAIN(); | |
*addr++ = *baddr; | |
} | |
G2_UNLOCK(); | |
} | |
static void | |
g2bus_ne_bus_mem_write_multi_1(void *v, bus_space_handle_t sh, | |
bus_size_t off, const uint8_t *addr, bus_size_t len) | |
{ | |
G2LOCK_DECL; | |
volatile uint8_t * baddr = (uint8_t *)(sh + (off * 4)); | |
G2_LOCK(); | |
while (len--) { | |
G2_DRAIN(); | |
*baddr = *addr++; | |
} | |
G2_UNLOCK(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment