59310c6a98
support which use National Semiconductor DP8393X (SONIC) as ethernet controller. Currently, this driver is used on only PC-98. Submitted by: Motomichi Matsuzaki <mzaki@e-mail.ne.jp> Obtained from: NetBSD/pc98
903 lines
21 KiB
C
903 lines
21 KiB
C
/* $FreeBSD$ */
|
|
/* $NecBSD: dp83932subr.c,v 1.5.6.2 1999/10/09 05:47:23 kmatsuda Exp $ */
|
|
/* $NetBSD$ */
|
|
|
|
/*
|
|
* Copyright (c) 1997, 1998, 1999
|
|
* Kouichi Matsuda. 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.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Kouichi Matsuda for
|
|
* NetBSD/pc98.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission
|
|
*
|
|
* 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.
|
|
*/
|
|
/*
|
|
* Routines of NEC PC-9801-83, 84, 103, 104, PC-9801N-25 and PC-9801N-J02, J02R
|
|
* Ethernet interface for NetBSD/pc98, ported by Kouichi Matsuda.
|
|
*
|
|
* These cards use National Semiconductor DP83934AVQB as Ethernet Controller
|
|
* and National Semiconductor NS46C46 as (64 * 16 bits) Microwire Serial EEPROM.
|
|
*/
|
|
|
|
/*
|
|
* Modified for FreeBSD(98) 4.0 from NetBSD/pc98 1.4.2 by Motomichi Matsuzaki.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/errno.h>
|
|
|
|
#include <net/ethernet.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_types.h>
|
|
#include <net/if_media.h>
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/if_inarp.h>
|
|
#endif
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#include <sys/bus.h>
|
|
#include <machine/clock.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/bus_pio.h>
|
|
#include <machine/bus_memio.h>
|
|
#include <machine/bus.h>
|
|
|
|
#include <dev/snc/dp83932reg.h>
|
|
#include <dev/snc/dp83932var.h>
|
|
#include <dev/snc/if_sncreg.h>
|
|
#include <dev/snc/dp83932subr.h>
|
|
|
|
integrate u_int16_t snc_nec16_select_bank
|
|
__P((struct snc_softc *, u_int32_t, u_int32_t));
|
|
|
|
/*
|
|
* Interface exists: make available by filling in network interface
|
|
* record. System will initialize the interface when it is ready
|
|
* to accept packets.
|
|
*/
|
|
int
|
|
sncsetup(sc, lladdr)
|
|
struct snc_softc *sc;
|
|
u_int8_t *lladdr;
|
|
{
|
|
u_int32_t p, pp;
|
|
int i;
|
|
int offset;
|
|
|
|
/*
|
|
* Put the pup in reset mode (sncinit() will fix it later),
|
|
* stop the timer, disable all interrupts and clear any interrupts.
|
|
*/
|
|
NIC_PUT(sc, SNCR_CR, CR_STP);
|
|
wbflush();
|
|
NIC_PUT(sc, SNCR_CR, CR_RST);
|
|
wbflush();
|
|
NIC_PUT(sc, SNCR_IMR, 0);
|
|
wbflush();
|
|
NIC_PUT(sc, SNCR_ISR, ISR_ALL);
|
|
wbflush();
|
|
|
|
/*
|
|
* because the SONIC is basically 16bit device it 'concatenates'
|
|
* a higher buffer address to a 16 bit offset--this will cause wrap
|
|
* around problems near the end of 64k !!
|
|
*/
|
|
p = pp = 0;
|
|
|
|
for (i = 0; i < NRRA; i++) {
|
|
sc->v_rra[i] = SONIC_GETDMA(p);
|
|
p += RXRSRC_SIZE(sc);
|
|
}
|
|
sc->v_rea = SONIC_GETDMA(p);
|
|
|
|
p = SOALIGN(sc, p);
|
|
|
|
sc->v_cda = SONIC_GETDMA(p);
|
|
p += CDA_SIZE(sc);
|
|
|
|
p = SOALIGN(sc, p);
|
|
|
|
for (i = 0; i < NTDA; i++) {
|
|
struct mtd *mtdp = &sc->mtda[i];
|
|
mtdp->mtd_vtxp = SONIC_GETDMA(p);
|
|
p += TXP_SIZE(sc);
|
|
}
|
|
|
|
p = SOALIGN(sc, p);
|
|
|
|
if ((p - pp) > NBPG) {
|
|
device_printf (sc->sc_dev, "sizeof RRA (%ld) + CDA (%ld) +"
|
|
"TDA (%ld) > NBPG (%d). Punt!\n",
|
|
(ulong)sc->v_cda - (ulong)sc->v_rra[0],
|
|
(ulong)sc->mtda[0].mtd_vtxp - (ulong)sc->v_cda,
|
|
(ulong)p - (ulong)sc->mtda[0].mtd_vtxp,
|
|
NBPG);
|
|
return(1);
|
|
}
|
|
|
|
p = pp + NBPG;
|
|
pp = p;
|
|
|
|
sc->sc_nrda = NBPG / RXPKT_SIZE(sc);
|
|
sc->v_rda = SONIC_GETDMA(p);
|
|
|
|
p = pp + NBPG;
|
|
|
|
for (i = 0; i < NRBA; i++) {
|
|
sc->rbuf[i] = p;
|
|
p += NBPG;
|
|
}
|
|
|
|
pp = p;
|
|
offset = TXBSIZE;
|
|
for (i = 0; i < NTDA; i++) {
|
|
struct mtd *mtdp = &sc->mtda[i];
|
|
|
|
mtdp->mtd_vbuf = SONIC_GETDMA(p);
|
|
offset += TXBSIZE;
|
|
if (offset < NBPG) {
|
|
p += TXBSIZE;
|
|
} else {
|
|
p = pp + NBPG;
|
|
pp = p;
|
|
offset = TXBSIZE;
|
|
}
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* miscellaneous NEC/SONIC detect functions.
|
|
*/
|
|
|
|
/*
|
|
* check if a specified irq is acceptable.
|
|
*/
|
|
u_int8_t
|
|
snc_nec16_validate_irq(irq)
|
|
int irq;
|
|
{
|
|
const u_int8_t encoded_irq[16] = {
|
|
-1, -1, -1, 0, -1, 1, 2, -1, -1, 3, 4, -1, 5, 6, -1, -1
|
|
};
|
|
|
|
return encoded_irq[irq];
|
|
}
|
|
|
|
/*
|
|
* specify irq to board.
|
|
*/
|
|
int
|
|
snc_nec16_register_irq(sc, irq)
|
|
struct snc_softc *sc;
|
|
int irq;
|
|
{
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
u_int8_t encoded_irq;
|
|
|
|
encoded_irq = snc_nec16_validate_irq(irq);
|
|
if (encoded_irq == (u_int8_t) -1) {
|
|
printf("snc_nec16_register_irq: unsupported irq (%d)\n", irq);
|
|
return 0;
|
|
}
|
|
|
|
/* select SNECR_IRQSEL register */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_IRQSEL);
|
|
/* write encoded irq value */
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, encoded_irq);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* check if a specified memory base address is acceptable.
|
|
*/
|
|
int
|
|
snc_nec16_validate_mem(maddr)
|
|
int maddr;
|
|
{
|
|
|
|
/* Check on Normal mode with max range, only */
|
|
if ((maddr & ~0x1E000) != 0xC0000) {
|
|
printf("snc_nec16_validate_mem: "
|
|
"unsupported window base (0x%x)\n", maddr);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* specify memory base address to board and map to first bank.
|
|
*/
|
|
int
|
|
snc_nec16_register_mem(sc, maddr)
|
|
struct snc_softc *sc;
|
|
int maddr;
|
|
{
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
if (snc_nec16_validate_mem(maddr) == 0)
|
|
return 0;
|
|
|
|
/* select SNECR_MEMSEL register */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_MEMSEL);
|
|
/* write encoded memory base select value */
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, SNECR_MEMSEL_PHYS2EN(maddr));
|
|
|
|
/*
|
|
* set current bank to 0 (bottom) and map
|
|
*/
|
|
/* select SNECR_MEMBS register */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_MEMBS);
|
|
/* select new bank */
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_MEMBS_B2EB(0) | SNECR_MEMBS_BSEN);
|
|
/* set current bank to 0 */
|
|
sc->curbank = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
snc_nec16_check_memory(iot, ioh, memt, memh)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
bus_space_tag_t memt;
|
|
bus_space_handle_t memh;
|
|
{
|
|
u_int16_t val;
|
|
int i, j;
|
|
|
|
val = 0;
|
|
for (i = 0; i < SNEC_NBANK; i++) {
|
|
/* select new bank */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_MEMBS);
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_MEMBS_B2EB(i) | SNECR_MEMBS_BSEN);
|
|
|
|
/* write test pattern */
|
|
for (j = 0; j < SNEC_NMEMS / 2; j++) {
|
|
bus_space_write_2(memt, memh, j * 2, val + j);
|
|
}
|
|
val += 0x1000;
|
|
}
|
|
|
|
val = 0;
|
|
for (i = 0; i < SNEC_NBANK; i++) {
|
|
/* select new bank */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_MEMBS);
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_MEMBS_B2EB(i) | SNECR_MEMBS_BSEN);
|
|
|
|
/* read test pattern */
|
|
for (j = 0; j < SNEC_NMEMS / 2; j++) {
|
|
if (bus_space_read_2(memt, memh, j * 2) != val + j)
|
|
break;
|
|
}
|
|
|
|
if (j < SNEC_NMEMS / 2) {
|
|
printf("snc_nec16_check_memory: "
|
|
"memory check failed at 0x%04x%04x"
|
|
"val 0x%04x != expected 0x%04x\n", i, j,
|
|
bus_space_read_2(memt, memh, j * 2),
|
|
val + j);
|
|
return 0;
|
|
}
|
|
val += 0x1000;
|
|
}
|
|
|
|
/* zero clear mem */
|
|
for (i = 0; i < SNEC_NBANK; i++) {
|
|
/* select new bank */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_MEMBS);
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_MEMBS_B2EB(i) | SNECR_MEMBS_BSEN);
|
|
|
|
bus_space_set_region_4(memt, memh, 0, 0, SNEC_NMEMS >> 2);
|
|
}
|
|
|
|
/* again read test if these are 0 */
|
|
for (i = 0; i < SNEC_NBANK; i++) {
|
|
/* select new bank */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_MEMBS);
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_MEMBS_B2EB(i) | SNECR_MEMBS_BSEN);
|
|
|
|
/* check if cleared */
|
|
for (j = 0; j < SNEC_NMEMS; j += 2) {
|
|
if (bus_space_read_2(memt, memh, j) != 0)
|
|
break;
|
|
}
|
|
|
|
if (j != SNEC_NMEMS) {
|
|
printf("snc_nec16_check_memory: "
|
|
"memory zero clear failed at 0x%04x%04x\n", i, j);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
snc_nec16_detectsubr(iot, ioh, memt, memh, irq, maddr, type)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
bus_space_tag_t memt;
|
|
bus_space_handle_t memh;
|
|
int irq;
|
|
int maddr;
|
|
u_int8_t type;
|
|
{
|
|
u_int16_t cr;
|
|
u_int8_t ident;
|
|
int rv = 0;
|
|
|
|
if (snc_nec16_validate_irq(irq) == (u_int8_t) -1)
|
|
return 0;
|
|
/* XXX: maddr already checked */
|
|
if (snc_nec16_validate_mem(maddr) == 0)
|
|
return 0;
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_IDENT);
|
|
ident = bus_space_read_1(iot, ioh, SNEC_CTRLB);
|
|
if (ident == 0xff || ident == 0x00) {
|
|
/* not found */
|
|
return 0;
|
|
}
|
|
|
|
switch (type) {
|
|
case SNEC_TYPE_LEGACY:
|
|
rv = (ident == SNECR_IDENT_LEGACY_CBUS);
|
|
break;
|
|
case SNEC_TYPE_PNP:
|
|
rv = ((ident == SNECR_IDENT_PNP_CBUS) ||
|
|
(ident == SNECR_IDENT_PNP_PCMCIABUS));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (rv == 0) {
|
|
printf("snc_nec16_detectsubr: parent bus mismatch\n");
|
|
return 0;
|
|
}
|
|
|
|
/* select SONIC register SNCR_CR */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNCR_CR);
|
|
bus_space_write_2(iot, ioh, SNEC_CTRL, CR_RXDIS | CR_STP | CR_RST);
|
|
delay(400);
|
|
|
|
cr = bus_space_read_2(iot, ioh, SNEC_CTRL);
|
|
if (cr != (CR_RXDIS | CR_STP | CR_RST)) {
|
|
#ifdef DIAGNOSTIC
|
|
printf("snc_nec16_detectsubr: card reset failed, cr = 0x%04x\n",
|
|
cr);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (snc_nec16_check_memory(iot, ioh, memt, memh) == 0)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* XXX */
|
|
#define SNC_VENDOR_NEC 0x00004c
|
|
#define SNC_NEC_SERIES_LEGACY_CBUS 0xa5
|
|
#define SNC_NEC_SERIES_PNP_PCMCIA 0xd5
|
|
#define SNC_NEC_SERIES_PNP_PCMCIA2 0x6d /* XXX */
|
|
#define SNC_NEC_SERIES_PNP_CBUS 0x0d
|
|
#define SNC_NEC_SERIES_PNP_CBUS2 0x3d
|
|
|
|
u_int8_t *
|
|
snc_nec16_detect_type(myea)
|
|
u_int8_t *myea;
|
|
{
|
|
u_int32_t vendor = (myea[0] << 16) | (myea[1] << 8) | myea[2];
|
|
u_int8_t series = myea[3];
|
|
u_int8_t type = myea[4] & 0x80;
|
|
u_int8_t *typestr;
|
|
|
|
switch (vendor) {
|
|
case SNC_VENDOR_NEC:
|
|
switch (series) {
|
|
case SNC_NEC_SERIES_LEGACY_CBUS:
|
|
if (type)
|
|
typestr = "NEC PC-9801-84";
|
|
else
|
|
typestr = "NEC PC-9801-83";
|
|
break;
|
|
case SNC_NEC_SERIES_PNP_CBUS:
|
|
case SNC_NEC_SERIES_PNP_CBUS2:
|
|
if (type)
|
|
typestr = "NEC PC-9801-104";
|
|
else
|
|
typestr = "NEC PC-9801-103";
|
|
break;
|
|
case SNC_NEC_SERIES_PNP_PCMCIA:
|
|
case SNC_NEC_SERIES_PNP_PCMCIA2:
|
|
/* XXX: right ? */
|
|
if (type)
|
|
typestr = "NEC PC-9801N-J02R";
|
|
else
|
|
typestr = "NEC PC-9801N-J02";
|
|
break;
|
|
default:
|
|
typestr = "NEC unknown (PC-9801N-25?)";
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
typestr = "unknown (3rd vendor?)";
|
|
break;
|
|
}
|
|
|
|
return typestr;
|
|
}
|
|
|
|
int
|
|
snc_nec16_get_enaddr(iot, ioh, myea)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
u_int8_t *myea;
|
|
{
|
|
u_int8_t eeprom[SNEC_EEPROM_SIZE];
|
|
u_int8_t rom_sum, sum = 0x00;
|
|
int i;
|
|
|
|
snc_nec16_read_eeprom(iot, ioh, eeprom);
|
|
|
|
for (i = SNEC_EEPROM_KEY0; i < SNEC_EEPROM_CKSUM; i++) {
|
|
sum = sum ^ eeprom[i];
|
|
}
|
|
|
|
rom_sum = eeprom[SNEC_EEPROM_CKSUM];
|
|
|
|
if (sum != rom_sum) {
|
|
printf("snc_nec16_get_enaddr: "
|
|
"checksum mismatch; calculated %02x != read %02x",
|
|
sum, rom_sum);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
myea[i] = eeprom[SNEC_EEPROM_SA0 + i];
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* read from NEC/SONIC NIC register.
|
|
*/
|
|
u_int16_t
|
|
snc_nec16_nic_get(sc, reg)
|
|
struct snc_softc *sc;
|
|
u_int8_t reg;
|
|
{
|
|
u_int16_t val;
|
|
|
|
/* select SONIC register */
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, SNEC_ADDR, reg);
|
|
val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, SNEC_CTRL);
|
|
|
|
return val;
|
|
}
|
|
|
|
/*
|
|
* write to NEC/SONIC NIC register.
|
|
*/
|
|
void
|
|
snc_nec16_nic_put(sc, reg, val)
|
|
struct snc_softc *sc;
|
|
u_int8_t reg;
|
|
u_int16_t val;
|
|
{
|
|
|
|
/* select SONIC register */
|
|
bus_space_write_1(sc->sc_iot, sc->sc_ioh, SNEC_ADDR, reg);
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh, SNEC_CTRL, val);
|
|
}
|
|
|
|
|
|
/*
|
|
* select memory bank and map
|
|
* where exists specified (internal buffer memory) offset.
|
|
*/
|
|
integrate u_int16_t
|
|
snc_nec16_select_bank(sc, base, offset)
|
|
struct snc_softc *sc;
|
|
u_int32_t base;
|
|
u_int32_t offset;
|
|
{
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
u_int8_t bank;
|
|
u_int16_t noffset;
|
|
|
|
/* bitmode is fixed to 16 bit. */
|
|
bank = (base + offset * 2) >> 13;
|
|
noffset = (base + offset * 2) & (SNEC_NMEMS - 1);
|
|
|
|
#ifdef SNCDEBUG
|
|
if (noffset % 2) {
|
|
device_printf(sc->sc_dev, "noffset is odd (0x%04x)\n",
|
|
noffset);
|
|
}
|
|
#endif /* SNCDEBUG */
|
|
|
|
if (sc->curbank != bank) {
|
|
/* select SNECR_MEMBS register */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_MEMBS);
|
|
/* select new bank */
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_MEMBS_B2EB(bank) | SNECR_MEMBS_BSEN);
|
|
/* update current bank */
|
|
sc->curbank = bank;
|
|
}
|
|
|
|
return noffset;
|
|
}
|
|
|
|
/*
|
|
* write to SONIC descriptors.
|
|
*/
|
|
void
|
|
snc_nec16_writetodesc(sc, base, offset, val)
|
|
struct snc_softc *sc;
|
|
u_int32_t base;
|
|
u_int32_t offset;
|
|
u_int16_t val;
|
|
{
|
|
bus_space_tag_t memt = sc->sc_memt;
|
|
bus_space_handle_t memh = sc->sc_memh;
|
|
u_int16_t noffset;
|
|
|
|
noffset = snc_nec16_select_bank(sc, base, offset);
|
|
|
|
bus_space_write_2(memt, memh, noffset, val);
|
|
}
|
|
|
|
/*
|
|
* read from SONIC descriptors.
|
|
*/
|
|
u_int16_t
|
|
snc_nec16_readfromdesc(sc, base, offset)
|
|
struct snc_softc *sc;
|
|
u_int32_t base;
|
|
u_int32_t offset;
|
|
{
|
|
bus_space_tag_t memt = sc->sc_memt;
|
|
bus_space_handle_t memh = sc->sc_memh;
|
|
u_int16_t noffset;
|
|
|
|
noffset = snc_nec16_select_bank(sc, base, offset);
|
|
|
|
return bus_space_read_2(memt, memh, noffset);
|
|
}
|
|
|
|
/*
|
|
* read from SONIC data buffer.
|
|
*/
|
|
void
|
|
snc_nec16_copyfrombuf(sc, dst, offset, size)
|
|
struct snc_softc *sc;
|
|
void *dst;
|
|
u_int32_t offset;
|
|
size_t size;
|
|
{
|
|
bus_space_tag_t memt = sc->sc_memt;
|
|
bus_space_handle_t memh = sc->sc_memh;
|
|
u_int16_t noffset;
|
|
u_int8_t* bptr = dst;
|
|
|
|
noffset = snc_nec16_select_bank(sc, offset, 0);
|
|
|
|
/* XXX: should check if offset + size < 0x2000. */
|
|
|
|
bus_space_barrier(memt, memh, noffset, size,
|
|
BUS_SPACE_BARRIER_READ);
|
|
|
|
if (size > 3) {
|
|
if (noffset & 3) {
|
|
size_t asize = (~noffset & 3);
|
|
|
|
bus_space_read_region_1(memt, memh, noffset,
|
|
bptr, asize);
|
|
bptr += asize;
|
|
noffset += asize;
|
|
size -= asize;
|
|
}
|
|
bus_space_read_region_4(memt, memh, noffset,
|
|
(u_int32_t *) bptr, size >> 2);
|
|
bptr += size & ~3;
|
|
noffset += size & ~3;
|
|
size &= 3;
|
|
}
|
|
if (size)
|
|
bus_space_read_region_1(memt, memh, noffset, bptr, size);
|
|
}
|
|
|
|
/*
|
|
* write to SONIC data buffer.
|
|
*/
|
|
void
|
|
snc_nec16_copytobuf(sc, src, offset, size)
|
|
struct snc_softc *sc;
|
|
void *src;
|
|
u_int32_t offset;
|
|
size_t size;
|
|
{
|
|
bus_space_tag_t memt = sc->sc_memt;
|
|
bus_space_handle_t memh = sc->sc_memh;
|
|
u_int16_t noffset, onoffset;
|
|
size_t osize = size;
|
|
u_int8_t* bptr = src;
|
|
|
|
noffset = snc_nec16_select_bank(sc, offset, 0);
|
|
onoffset = noffset;
|
|
|
|
/* XXX: should check if offset + size < 0x2000. */
|
|
|
|
if (size > 3) {
|
|
if (noffset & 3) {
|
|
size_t asize = (~noffset & 3);
|
|
|
|
bus_space_write_region_1(memt, memh, noffset,
|
|
bptr, asize);
|
|
bptr += asize;
|
|
noffset += asize;
|
|
size -= asize;
|
|
}
|
|
bus_space_write_region_4(memt, memh, noffset,
|
|
(u_int32_t *)bptr, size >> 2);
|
|
bptr += size & ~3;
|
|
noffset += size & ~3;
|
|
size -= size & ~3;
|
|
}
|
|
if (size)
|
|
bus_space_write_region_1(memt, memh, noffset, bptr, size);
|
|
|
|
bus_space_barrier(memt, memh, onoffset, osize,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
}
|
|
|
|
/*
|
|
* write (fill) 0 to SONIC data buffer.
|
|
*/
|
|
void
|
|
snc_nec16_zerobuf(sc, offset, size)
|
|
struct snc_softc *sc;
|
|
u_int32_t offset;
|
|
size_t size;
|
|
{
|
|
bus_space_tag_t memt = sc->sc_memt;
|
|
bus_space_handle_t memh = sc->sc_memh;
|
|
u_int16_t noffset, onoffset;
|
|
size_t osize = size;
|
|
|
|
noffset = snc_nec16_select_bank(sc, offset, 0);
|
|
onoffset = noffset;
|
|
|
|
/* XXX: should check if offset + size < 0x2000. */
|
|
|
|
if (size > 3) {
|
|
if (noffset & 3) {
|
|
size_t asize = (~noffset & 3);
|
|
|
|
bus_space_set_region_1(memt, memh, noffset, 0, asize);
|
|
noffset += asize;
|
|
size -= asize;
|
|
}
|
|
bus_space_set_region_4(memt, memh, noffset, 0, size >> 2);
|
|
noffset += size & ~3;
|
|
size -= size & ~3;
|
|
}
|
|
if (size)
|
|
bus_space_set_region_1(memt, memh, noffset, 0, size);
|
|
|
|
bus_space_barrier(memt, memh, onoffset, osize,
|
|
BUS_SPACE_BARRIER_WRITE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Routines to read bytes sequentially from EEPROM through NEC PC-9801-83,
|
|
* 84, 103, 104, PC-9801N-25 and PC-9801N-J02, J02R for NetBSD/pc98.
|
|
* Ported by Kouichi Matsuda.
|
|
*
|
|
* This algorism is generic to read data sequentially from 4-Wire
|
|
* Microwire Serial EEPROM.
|
|
*/
|
|
|
|
#define SNEC_EEP_DELAY 1000
|
|
|
|
void
|
|
snc_nec16_read_eeprom(iot, ioh, data)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
u_int8_t *data;
|
|
{
|
|
u_int8_t n, val, bit;
|
|
|
|
/* Read bytes from EEPROM; two bytes per an iteration. */
|
|
for (n = 0; n < SNEC_EEPROM_SIZE / 2; n++) {
|
|
/* select SNECR_EEP */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, SNECR_EEP);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, 0x00);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
/* Start EEPROM access. */
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, SNECR_EEP_CS);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_SK);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_DI);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_SK | SNECR_EEP_DI);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_DI);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_SK | SNECR_EEP_DI);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, SNECR_EEP_CS);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_SK);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
/* Pass the iteration count to the chip. */
|
|
for (bit = 0x20; bit != 0x00; bit >>= 1) {
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, SNECR_EEP_CS |
|
|
((n & bit) ? SNECR_EEP_DI : 0x00));
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_SK |
|
|
((n & bit) ? SNECR_EEP_DI : 0x00));
|
|
delay(SNEC_EEP_DELAY);
|
|
}
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, SNECR_EEP_CS);
|
|
(void) bus_space_read_1(iot, ioh, SNEC_CTRLB); /* ACK */
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
/* Read a byte. */
|
|
val = 0;
|
|
for (bit = 0x80; bit != 0x00; bit >>= 1) {
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_SK);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, SNECR_EEP_CS);
|
|
|
|
if (bus_space_read_1(iot, ioh, SNEC_CTRLB) & SNECR_EEP_DO)
|
|
val |= bit;
|
|
}
|
|
*data++ = val;
|
|
|
|
/* Read one more byte. */
|
|
val = 0;
|
|
for (bit = 0x80; bit != 0x00; bit >>= 1) {
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB,
|
|
SNECR_EEP_CS | SNECR_EEP_SK);
|
|
delay(SNEC_EEP_DELAY);
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, SNECR_EEP_CS);
|
|
|
|
if (bus_space_read_1(iot, ioh, SNEC_CTRLB) & SNECR_EEP_DO)
|
|
val |= bit;
|
|
}
|
|
*data++ = val;
|
|
|
|
bus_space_write_1(iot, ioh, SNEC_CTRLB, 0x00);
|
|
delay(SNEC_EEP_DELAY);
|
|
}
|
|
|
|
#ifdef SNCDEBUG
|
|
/* Report what we got. */
|
|
data -= SNEC_EEPROM_SIZE;
|
|
log(LOG_INFO, "%s: EEPROM:"
|
|
" %02x%02x%02x%02x %02x%02x%02x%02x -"
|
|
" %02x%02x%02x%02x %02x%02x%02x%02x -"
|
|
" %02x%02x%02x%02x %02x%02x%02x%02x -"
|
|
" %02x%02x%02x%02x %02x%02x%02x%02x\n",
|
|
"snc_nec16_read_eeprom",
|
|
data[ 0], data[ 1], data[ 2], data[ 3],
|
|
data[ 4], data[ 5], data[ 6], data[ 7],
|
|
data[ 8], data[ 9], data[10], data[11],
|
|
data[12], data[13], data[14], data[15],
|
|
data[16], data[17], data[18], data[19],
|
|
data[20], data[21], data[22], data[23],
|
|
data[24], data[25], data[26], data[27],
|
|
data[28], data[29], data[30], data[31]);
|
|
#endif
|
|
}
|
|
|
|
#ifdef SNCDEBUG
|
|
void
|
|
snc_nec16_dump_reg(iot, ioh)
|
|
bus_space_tag_t iot;
|
|
bus_space_handle_t ioh;
|
|
{
|
|
u_int8_t n;
|
|
u_int16_t val;
|
|
|
|
printf("SONIC registers (word):");
|
|
for (n = 0; n < SNC_NREGS; n++) {
|
|
/* select required SONIC register */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, n);
|
|
delay(10);
|
|
val = bus_space_read_2(iot, ioh, SNEC_CTRL);
|
|
if ((n % 0x10) == 0)
|
|
printf("\n%04x ", val);
|
|
else
|
|
printf("%04x ", val);
|
|
}
|
|
printf("\n");
|
|
|
|
printf("NEC/SONIC registers (byte):\n");
|
|
for (n = SNECR_MEMBS; n <= SNECR_IDENT; n += 2) {
|
|
/* select required SONIC register */
|
|
bus_space_write_1(iot, ioh, SNEC_ADDR, n);
|
|
delay(10);
|
|
val = (u_int16_t) bus_space_read_1(iot, ioh, SNEC_CTRLB);
|
|
printf("%04x ", val);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
#endif /* SNCDEBUG */
|