#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_xi.c,v 1.96 2022/09/25 21:53:54 thorpej Exp $");
#include "opt_inet.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/ioctl.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_ether.h>
#include <net/bpf.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
#define MAX_BYTES_INTR 12000
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/pcmcia/pcmciareg.h>
#include <dev/pcmcia/pcmciavar.h>
#include <dev/pcmcia/pcmciadevs.h>
#include <dev/pcmcia/if_xireg.h>
#include <dev/pcmcia/if_xivar.h>
#ifdef __GNUC__
#define INLINE inline
#else
#define INLINE
#endif
#define XIDEBUG
#define XIDEBUG_VALUE 0
#ifdef XIDEBUG
#define DPRINTF(cat, x) if (xidebug & (cat)) printf x
#define XID_CONFIG 0x01
#define XID_MII 0x02
#define XID_INTR 0x04
#define XID_FIFO 0x08
#define XID_MCAST 0x10
#ifdef XIDEBUG_VALUE
int xidebug = XIDEBUG_VALUE;
#else
int xidebug = 0;
#endif
#else
#define DPRINTF(cat, x) (void)0
#endif
#define STATIC static
STATIC int xi_enable(struct xi_softc *);
STATIC void xi_disable(struct xi_softc *);
STATIC void xi_cycle_power(struct xi_softc *);
STATIC int xi_ether_ioctl(struct ifnet *, u_long, void *);
STATIC void xi_full_reset(struct xi_softc *);
STATIC void xi_init(struct xi_softc *);
STATIC int xi_ioctl(struct ifnet *, u_long, void *);
STATIC int xi_mdi_read(device_t, int, int, uint16_t *);
STATIC int xi_mdi_write(device_t, int, int, uint16_t);
STATIC int xi_mediachange(struct ifnet *);
STATIC uint16_t xi_get(struct xi_softc *);
STATIC void xi_reset(struct xi_softc *);
STATIC void xi_set_address(struct xi_softc *);
STATIC void xi_start(struct ifnet *);
STATIC void xi_statchg(struct ifnet *);
STATIC void xi_stop(struct xi_softc *);
STATIC void xi_watchdog(struct ifnet *);
void
xi_attach(struct xi_softc *sc, uint8_t *myea)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct mii_data * const mii = &sc->sc_mii;
#ifdef XIDEBUG
uint16_t bmsr;
#endif
#if 0
if (sc->sc_chipset >= XI_CHIPSET_DINGO) {
struct pcmcia_mem_handle pcmh;
int ccr_window;
bus_size_t ccr_offset;
if (pcmcia_mem_alloc(psc->sc_pf, PCMCIA_CCR_SIZE_DINGO,
&pcmh)) {
DPRINTF(XID_CONFIG, ("xi: bad mem alloc\n"));
goto fail;
}
if (pcmcia_mem_map(psc->sc_pf, PCMCIA_MEM_ATTR,
psc->sc_pf->ccr_base, PCMCIA_CCR_SIZE_DINGO,
&pcmh, &ccr_offset, &ccr_window)) {
DPRINTF(XID_CONFIG, ("xi: bad mem map\n"));
pcmcia_mem_free(psc->sc_pf, &pcmh);
goto fail;
}
bus_space_write_1(pcmh.memt, pcmh.memh,
ccr_offset + PCMCIA_CCR_DCOR0, PCMCIA_CCR_DCOR0_SFINT);
bus_space_write_1(pcmh.memt, pcmh.memh,
ccr_offset + PCMCIA_CCR_DCOR1,
PCMCIA_CCR_DCOR1_FORCE_LEVIREQ | PCMCIA_CCR_DCOR1_D6);
bus_space_write_1(pcmh.memt, pcmh.memh,
ccr_offset + PCMCIA_CCR_DCOR2, 0);
bus_space_write_1(pcmh.memt, pcmh.memh,
ccr_offset + PCMCIA_CCR_DCOR3, 0);
bus_space_write_1(pcmh.memt, pcmh.memh,
ccr_offset + PCMCIA_CCR_DCOR4, 0);
pcmcia_mem_unmap(psc->sc_pf, ccr_window);
pcmcia_mem_free(psc->sc_pf, &pcmh);
}
#endif
xi_full_reset(sc);
device_printf(sc->sc_dev, "MAC address %s\n", ether_sprintf(myea));
ifp = &sc->sc_ethercom.ec_if;
strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_start = xi_start;
ifp->if_ioctl = xi_ioctl;
ifp->if_watchdog = xi_watchdog;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
IFQ_SET_READY(&ifp->if_snd);
sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
if_attach(ifp);
if_deferred_start_init(ifp, NULL);
ether_ifattach(ifp, myea);
mii->mii_ifp = ifp;
mii->mii_readreg = xi_mdi_read;
mii->mii_writereg = xi_mdi_write;
mii->mii_statchg = xi_statchg;
sc->sc_ethercom.ec_mii = mii;
ifmedia_init(&mii->mii_media, 0, xi_mediachange, ether_mediastatus);
#ifdef XIDEBUG
xi_mdi_read(sc->sc_dev, 0, 1, &bmsr);
DPRINTF(XID_MII | XID_CONFIG, ("xi: bmsr %x\n", bmsr));
#endif
mii_attach(sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY,
MII_OFFSET_ANY, 0);
if (LIST_FIRST(&mii->mii_phys) == NULL)
ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_AUTO, 0, NULL);
ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
rnd_attach_source(&sc->sc_rnd_source, device_xname(sc->sc_dev),
RND_TYPE_NET, RND_FLAG_DEFAULT);
}
int
xi_detach(device_t self, int flags)
{
struct xi_softc *sc = device_private(self);
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
DPRINTF(XID_CONFIG, ("xi_detach()\n"));
xi_disable(sc);
rnd_detach_source(&sc->sc_rnd_source);
mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
ether_ifdetach(ifp);
if_detach(ifp);
ifmedia_fini(&sc->sc_mii.mii_media);
return 0;
}
int
xi_intr(void *arg)
{
struct xi_softc *sc = arg;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
uint8_t esr, rsr, isr, rx_status;
uint16_t tx_status, recvcount = 0, tempint;
DPRINTF(XID_CONFIG, ("xi_intr()\n"));
if (sc->sc_enabled == 0 || !device_is_active(sc->sc_dev))
return (0);
ifp->if_timer = 0;
PAGE(sc, 0);
if (sc->sc_chipset >= XI_CHIPSET_MOHAWK) {
bus_space_write_1(sc->sc_bst, sc->sc_bsh, CR, 0);
}
esr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, ESR);
isr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, ISR0);
rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, RSR);
if (isr == 0xff) {
#ifdef DIAGNOSTIC
printf("%s: interrupt for dead card\n",
device_xname(sc->sc_dev));
#endif
goto end;
}
DPRINTF(XID_INTR, ("xi: isr=%02x\n", isr));
PAGE(sc, 0x40);
rx_status =
bus_space_read_1(sc->sc_bst, sc->sc_bsh, RXST0);
bus_space_write_1(sc->sc_bst, sc->sc_bsh, RXST0, ~rx_status & 0xff);
tx_status =
bus_space_read_1(sc->sc_bst, sc->sc_bsh, TXST0);
tx_status |=
bus_space_read_1(sc->sc_bst, sc->sc_bsh, TXST1) << 8;
bus_space_write_1(sc->sc_bst, sc->sc_bsh, TXST0, 0);
bus_space_write_1(sc->sc_bst, sc->sc_bsh, TXST1, 0);
DPRINTF(XID_INTR, ("xi: rx_status=%02x tx_status=%04x\n", rx_status,
tx_status));
PAGE(sc, 0);
while (esr & FULL_PKT_RCV) {
if (!(rsr & RSR_RX_OK))
break;
if (recvcount > MAX_BYTES_INTR) {
DPRINTF(XID_INTR,
("xi: too many bytes this interrupt\n"));
if_statinc(ifp, if_iqdrops);
bus_space_write_2(sc->sc_bst, sc->sc_bsh, DO0,
DO_SKIP_RX_PKT);
}
tempint = xi_get(sc);
recvcount += tempint;
esr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, ESR);
rsr = bus_space_read_1(sc->sc_bst, sc->sc_bsh, RSR);
}
if (rsr & RSR_TOO_LONG) {
if_statinc(ifp, if_ierrors);
DPRINTF(XID_INTR, ("xi: packet too long\n"));
}
if (rsr & RSR_CRCERR) {
if_statinc(ifp, if_ierrors);
DPRINTF(XID_INTR, ("xi: CRC error detected\n"));
}
if (rsr & RSR_ALIGNERR) {
if_statinc(ifp, if_ierrors);
DPRINTF(XID_INTR, ("xi: alignment error detected\n"));
}
if (rx_status & RX_OVERRUN) {
if_statinc(ifp, if_ierrors);
bus_space_write_1(sc->sc_bst, sc->sc_bsh, CR, CLR_RX_OVERRUN);
DPRINTF(XID_INTR, ("xi: overrun cleared\n"));
}
if_schedule_deferred_start(ifp);
if ((tx_status & EXCESSIVE_COLL) ) {
DPRINTF(XID_INTR, ("xi: excessive collisions\n"));
bus_space_write_1(sc->sc_bst, sc->sc_bsh, CR, RESTART_TX);
if_statinc(ifp, if_oerrors);
}
if ((tx_status & TX_ABORT) )
if_statinc(ifp, if_oerrors);
rnd_add_uint32(&sc->sc_rnd_source, tx_status);
end:
PAGE(sc, 0);
bus_space_write_1(sc->sc_bst, sc->sc_bsh, CR, ENABLE_INT);
return (1);
}
STATIC uint16_t
xi_get(struct xi_softc *sc)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct mbuf *top, **mp, *m;
uint16_t pktlen, len, recvcount = 0;
uint8_t *data;
DPRINTF(XID_CONFIG, ("xi_get()\n"));
PAGE(sc, 0);
pktlen =
bus_space_read_2(sc->sc_bst, sc->sc_bsh, RBC0) & RBC_COUNT_MASK;
DPRINTF(XID_CONFIG, ("xi_get: pktlen=%d\n", pktlen));
if (pktlen == 0) {
return (0);
}
recvcount += pktlen;
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == NULL)
return (recvcount);
m_set_rcvif(m, ifp);
m->m_pkthdr.len = pktlen;
len = MHLEN;
top = NULL;
mp = ⊤
while (pktlen > 0) {
if (top) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == NULL) {
m_freem(top);
return (recvcount);
}
len = MLEN;
}
if (pktlen >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if (!(m->m_flags & M_EXT)) {
m_freem(m);
m_freem(top);
return (recvcount);
}
len = MCLBYTES;
}
if (top == NULL) {
char *newdata = (char *)ALIGN(m->m_data +
sizeof(struct ether_header)) -
sizeof(struct ether_header);
len -= newdata - m->m_data;
m->m_data = newdata;
}
len = uimin(pktlen, len);
data = mtod(m, uint8_t *);
if (len > 1) {
len &= ~1;
bus_space_read_multi_2(sc->sc_bst, sc->sc_bsh, EDP,
(uint16_t *)data, len>>1);
} else
*data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, EDP);
m->m_len = len;
pktlen -= len;
*mp = m;
mp = &m->m_next;
}
bus_space_write_2(sc->sc_bst, sc->sc_bsh, DO0, DO_SKIP_RX_PKT);
if (top == NULL)
return recvcount;
m_adj(top, -ETHER_CRC_LEN);
if_percpuq_enqueue(ifp->if_percpuq, top);
return (recvcount);
}
static INLINE void xi_mdi_idle(struct xi_softc *);
static INLINE void
xi_mdi_idle(struct xi_softc *sc)
{
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
bus_space_write_1(bst, bsh, GP2, MDC_LOW);
DELAY(1);
bus_space_write_1(bst, bsh, GP2, MDC_HIGH);
DELAY(1);
}
static INLINE void xi_mdi_pulse(struct xi_softc *, int);
static INLINE void
xi_mdi_pulse(struct xi_softc *sc, int data)
{
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
uint8_t bit = data ? MDIO_HIGH : MDIO_LOW;
bus_space_write_1(bst, bsh, GP2, bit | MDC_LOW);
DELAY(1);
bus_space_write_1(bst, bsh, GP2, bit | MDC_HIGH);
DELAY(1);
}
static INLINE int xi_mdi_probe(struct xi_softc *sc);
static INLINE int
xi_mdi_probe(struct xi_softc *sc)
{
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
uint8_t x;
bus_space_write_1(bst, bsh, GP2, MDC_LOW);
DELAY(1);
x = bus_space_read_1(bst, bsh, GP2);
bus_space_write_1(bst, bsh, GP2, MDC_HIGH);
DELAY(1);
return (x & MDIO);
}
static INLINE void xi_mdi_pulse_bits(struct xi_softc *, uint32_t, int);
static INLINE void
xi_mdi_pulse_bits(struct xi_softc *sc, uint32_t data, int len)
{
uint32_t mask;
for (mask = 1 << (len - 1); mask; mask >>= 1)
xi_mdi_pulse(sc, data & mask);
}
STATIC int
xi_mdi_read(device_t self, int phy, int reg, uint16_t *val)
{
struct xi_softc *sc = device_private(self);
int i;
uint32_t mask;
uint16_t data = 0;
PAGE(sc, 2);
for (i = 0; i < 32; i++)
xi_mdi_pulse(sc, 1);
xi_mdi_pulse_bits(sc, 0x06, 4);
xi_mdi_pulse_bits(sc, phy, 5);
xi_mdi_pulse_bits(sc, reg, 5);
xi_mdi_idle(sc);
xi_mdi_probe(sc);
for (mask = 1 << 15; mask; mask >>= 1) {
if (xi_mdi_probe(sc))
data |= mask;
}
xi_mdi_idle(sc);
DPRINTF(XID_MII,
("xi_mdi_read: phy %d reg %d -> %04hx\n", phy, reg, data));
*val = data;
return 0;
}
STATIC int
xi_mdi_write(device_t self, int phy, int reg, uint16_t val)
{
struct xi_softc *sc = device_private(self);
int i;
PAGE(sc, 2);
for (i = 0; i < 32; i++)
xi_mdi_pulse(sc, 1);
xi_mdi_pulse_bits(sc, 0x05, 4);
xi_mdi_pulse_bits(sc, phy, 5);
xi_mdi_pulse_bits(sc, reg, 5);
xi_mdi_pulse_bits(sc, 0x02, 2);
xi_mdi_pulse_bits(sc, val, 16);
xi_mdi_idle(sc);
DPRINTF(XID_MII,
("xi_mdi_write: phy %d reg %d val %04hx\n", phy, reg, val));
return 0;
}
STATIC void
xi_statchg(struct ifnet *ifp)
{
}
STATIC int
xi_mediachange(struct ifnet *ifp)
{
int s;
DPRINTF(XID_CONFIG, ("xi_mediachange()\n"));
if (ifp->if_flags & IFF_UP) {
s = splnet();
xi_init(ifp->if_softc);
splx(s);
}
return (0);
}
STATIC void
xi_reset(struct xi_softc *sc)
{
int s;
DPRINTF(XID_CONFIG, ("xi_reset()\n"));
s = splnet();
xi_stop(sc);
xi_init(sc);
splx(s);
}
STATIC void
xi_watchdog(struct ifnet *ifp)
{
struct xi_softc *sc = ifp->if_softc;
printf("%s: device timeout\n", device_xname(sc->sc_dev));
if_statinc(ifp, if_oerrors);
xi_reset(sc);
}
STATIC void
xi_stop(register struct xi_softc *sc)
{
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
DPRINTF(XID_CONFIG, ("xi_stop()\n"));
PAGE(sc, 0x40);
bus_space_write_1(bst, bsh, CMD0, DISABLE_RX);
PAGE(sc, 0);
bus_space_write_1(bst, bsh, CR, 0);
PAGE(sc, 1);
bus_space_write_1(bst, bsh, IMR0, 0);
sc->sc_ethercom.ec_if.if_timer = 0;
}
STATIC int
xi_enable(struct xi_softc *sc)
{
int error;
if (!sc->sc_enabled) {
error = (*sc->sc_enable)(sc);
if (error)
return (error);
sc->sc_enabled = 1;
xi_full_reset(sc);
}
return (0);
}
STATIC void
xi_disable(struct xi_softc *sc)
{
if (sc->sc_enabled) {
sc->sc_enabled = 0;
(*sc->sc_disable)(sc);
}
}
STATIC void
xi_init(struct xi_softc *sc)
{
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
DPRINTF(XID_CONFIG, ("xi_init()\n"));
PAGE(sc, 1);
bus_space_write_1(bst, bsh, IMR0,
ISR_TX_OFLOW | ISR_PKT_TX | ISR_MAC_INT |
ISR_RX_FULL | ISR_RX_PKT_REJ | ISR_FORCED_INT);
if (sc->sc_chipset < XI_CHIPSET_DINGO) {
bus_space_write_1(bst, bsh, IMR1, 1);
}
PAGE(sc, 0);
bus_space_write_1(bst, bsh, CR, ENABLE_INT);
xi_set_address(sc);
PAGE(sc, 0x40);
bus_space_write_1(bst, bsh, CMD0, ENABLE_RX | ONLINE);
PAGE(sc, 0);
mii_mediachg(&sc->sc_mii);
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
xi_start(ifp);
}
STATIC void
xi_start(struct ifnet *ifp)
{
struct xi_softc *sc = ifp->if_softc;
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
unsigned int s, len, pad = 0;
struct mbuf *m0, *m;
uint16_t space;
DPRINTF(XID_CONFIG, ("xi_start()\n"));
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) {
DPRINTF(XID_CONFIG, ("xi: interface busy or not running\n"));
return;
}
IFQ_POLL(&ifp->if_snd, m0);
if (m0 == 0)
return;
if (!(m0->m_flags & M_PKTHDR))
panic("xi_start: no header mbuf");
len = m0->m_pkthdr.len;
#if 1
if (len < ETHER_MIN_LEN - ETHER_CRC_LEN)
pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len;
#else
pad = 0;
#endif
PAGE(sc, 0);
bus_space_write_2(bst, bsh, TRS, (uint16_t)len + pad + 2);
space = bus_space_read_2(bst, bsh, TSO) & 0x7fff;
if (len + pad + 2 > space) {
DPRINTF(XID_FIFO,
("xi: not enough space in output FIFO (%d > %d)\n",
len + pad + 2, space));
return;
}
IFQ_DEQUEUE(&ifp->if_snd, m0);
bpf_mtap(ifp, m0, BPF_D_OUT);
s = splhigh();
bus_space_write_2(bst, bsh, EDP, (uint16_t)len + pad);
for (m = m0; m; ) {
if (m->m_len > 1)
bus_space_write_multi_2(bst, bsh, EDP,
mtod(m, uint16_t *), m->m_len>>1);
if (m->m_len & 1) {
DPRINTF(XID_CONFIG, ("xi: XXX odd!\n"));
bus_space_write_1(bst, bsh, EDP,
*(mtod(m, uint8_t *) + m->m_len - 1));
}
m = m0 = m_free(m);
}
DPRINTF(XID_CONFIG, ("xi: len=%d pad=%d total=%d\n", len, pad, len+pad+4));
if (sc->sc_chipset >= XI_CHIPSET_MOHAWK)
bus_space_write_1(bst, bsh, CR, TX_PKT | ENABLE_INT);
else {
for (; pad > 1; pad -= 2)
bus_space_write_2(bst, bsh, EDP, 0);
if (pad == 1)
bus_space_write_1(bst, bsh, EDP, 0);
}
splx(s);
ifp->if_timer = 5;
if_statinc(ifp, if_opackets);
}
STATIC int
xi_ether_ioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct ifaddr *ifa = (struct ifaddr *)data;
struct xi_softc *sc = ifp->if_softc;
int error;
DPRINTF(XID_CONFIG, ("xi_ether_ioctl()\n"));
switch (cmd) {
case SIOCINITIFADDR:
if ((error = xi_enable(sc)) != 0)
break;
ifp->if_flags |= IFF_UP;
xi_init(sc);
switch (ifa->ifa_addr->sa_family) {
#ifdef INET
case AF_INET:
arp_ifinit(ifp, ifa);
break;
#endif
default:
break;
}
break;
default:
return (EINVAL);
}
return (0);
}
STATIC int
xi_ioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct xi_softc *sc = ifp->if_softc;
int s, error = 0;
DPRINTF(XID_CONFIG, ("xi_ioctl()\n"));
s = splnet();
switch (cmd) {
case SIOCINITIFADDR:
error = xi_ether_ioctl(ifp, cmd, data);
break;
case SIOCSIFFLAGS:
if ((error = ifioctl_common(ifp, cmd, data)) != 0)
break;
switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) {
case IFF_RUNNING:
xi_stop(sc);
ifp->if_flags &= ~IFF_RUNNING;
xi_disable(sc);
break;
case IFF_UP:
if ((error = xi_enable(sc)) != 0)
break;
xi_init(sc);
break;
case IFF_UP | IFF_RUNNING:
xi_set_address(sc);
break;
case 0:
break;
}
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
if (sc->sc_enabled == 0) {
error = EIO;
break;
}
default:
if ((error = ether_ioctl(ifp, cmd, data)) == ENETRESET) {
if (ifp->if_flags & IFF_RUNNING)
xi_set_address(sc);
error = 0;
}
break;
}
splx(s);
return (error);
}
STATIC void
xi_set_address(struct xi_softc *sc)
{
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
struct ethercom *ec = &sc->sc_ethercom;
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
struct ether_multistep step;
struct ether_multi *enm;
int page, num;
int i;
uint8_t x;
const uint8_t *enaddr;
uint8_t indaddr[64];
DPRINTF(XID_CONFIG, ("xi_set_address()\n"));
enaddr = (const uint8_t *)CLLADDR(ifp->if_sadl);
if (sc->sc_chipset >= XI_CHIPSET_MOHAWK)
for (i = 0; i < 6; i++)
indaddr[i] = enaddr[5 - i];
else
for (i = 0; i < 6; i++)
indaddr[i] = enaddr[i];
num = 1;
ETHER_LOCK(ec);
if (ec->ec_multicnt > 9) {
ifp->if_flags |= IFF_ALLMULTI;
goto done;
}
ETHER_FIRST_MULTI(step, ec, enm);
for (; enm; num++) {
if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
sizeof(enm->enm_addrlo)) != 0) {
ifp->if_flags |= IFF_ALLMULTI;
goto done;
}
if (sc->sc_chipset >= XI_CHIPSET_MOHAWK)
for (i = 0; i < 6; i++)
indaddr[num * 6 + i] = enm->enm_addrlo[5 - i];
else
for (i = 0; i < 6; i++)
indaddr[num * 6 + i] = enm->enm_addrlo[i];
ETHER_NEXT_MULTI(step, enm);
}
ifp->if_flags &= ~IFF_ALLMULTI;
done:
ETHER_UNLOCK(ec);
if (num < 10)
memset(&indaddr[num * 6], 0xff, 6 * (10 - num));
for (page = 0; page < 8; page++) {
#ifdef XIDEBUG
if (xidebug & XID_MCAST) {
printf("page %d before:", page);
for (i = 0; i < 8; i++)
printf(" %02x", indaddr[page * 8 + i]);
printf("\n");
}
#endif
PAGE(sc, 0x50 + page);
bus_space_write_region_1(bst, bsh, IA, &indaddr[page * 8],
page == 7 ? 4 : 8);
DELAY(50);
#ifdef XIDEBUG
if (xidebug & XID_MCAST) {
bus_space_read_region_1(bst, bsh, IA,
&indaddr[page * 8], page == 7 ? 4 : 8);
printf("page %d after: ", page);
for (i = 0; i < 8; i++)
printf(" %02x", indaddr[page * 8 + i]);
printf("\n");
}
#endif
}
PAGE(sc, 0x42);
x = SWC1_IND_ADDR;
if (ifp->if_flags & IFF_PROMISC)
x |= SWC1_PROMISC;
if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))
x |= SWC1_MCAST_PROM;
if (!LIST_FIRST(&sc->sc_mii.mii_phys))
x |= SWC1_AUTO_MEDIA;
bus_space_write_1(sc->sc_bst, sc->sc_bsh, SWC1, x);
}
STATIC void
xi_cycle_power(struct xi_softc *sc)
{
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
DPRINTF(XID_CONFIG, ("xi_cycle_power()\n"));
PAGE(sc, 4);
DELAY(1);
bus_space_write_1(bst, bsh, GP1, 0);
tsleep(&xi_cycle_power, PWAIT, "xipwr1", hz * 40 / 1000);
if (sc->sc_chipset >= XI_CHIPSET_MOHAWK)
bus_space_write_1(bst, bsh, GP1, POWER_UP);
else
bus_space_write_1(bst, bsh, GP1, POWER_UP | 4);
tsleep(&xi_cycle_power, PWAIT, "xipwr2", hz * 20 / 1000);
}
STATIC void
xi_full_reset(struct xi_softc *sc)
{
bus_space_tag_t bst = sc->sc_bst;
bus_space_handle_t bsh = sc->sc_bsh;
uint8_t x;
DPRINTF(XID_CONFIG, ("xi_full_reset()\n"));
xi_cycle_power(sc);
bus_space_write_1(bst, bsh, CR, SOFT_RESET);
tsleep(&xi_full_reset, PWAIT, "xirst1", hz * 20 / 1000);
bus_space_write_1(bst, bsh, CR, 0);
tsleep(&xi_full_reset, PWAIT, "xirst2", hz * 20 / 1000);
PAGE(sc, 4);
if (sc->sc_chipset >= XI_CHIPSET_MOHAWK) {
bus_space_write_1(bst, bsh, GP0, GP1_OUT | GP2_OUT | GP2_WR);
}
tsleep(&xi_full_reset, PWAIT, "xirst3", hz * 500 / 1000);
sc->sc_rev = bus_space_read_1(bst, bsh, BV) &
((sc->sc_chipset >= XI_CHIPSET_MOHAWK) ? 0x70 : 0x30) >> 4;
DPRINTF(XID_CONFIG, ("xi: rev=%02x\n", sc->sc_rev));
if (sc->sc_chipset < XI_CHIPSET_MOHAWK) {
bus_space_write_1(bst, bsh, GP0, GP1_OUT);
}
tsleep(&xi_full_reset, PWAIT, "xirst4", hz * 40 / 1000);
if (sc->sc_chipset < XI_CHIPSET_DINGO) {
PAGE(sc, 0x42);
bus_space_write_1(bst, bsh, SWC0, 0x20);
}
if (sc->sc_rev != 1) {
PAGE(sc, 2);
bus_space_write_2(bst, bsh, RBS0, 0x2000);
}
PAGE(sc, 0);
bus_space_write_2(bst, bsh, DO0, DO_CHG_OFFSET);
PAGE(sc, 0x40);
bus_space_write_1(bst, bsh, RX0MSK,
PKT_TOO_LONG | CRC_ERR | RX_OVERRUN | RX_ABORT | RX_OK);
bus_space_write_1(bst, bsh, TX0MSK,
CARRIER_LOST | EXCESSIVE_COLL | TX_UNDERRUN | LATE_COLLISION |
SQE | TX_ABORT | TX_OK);
if (sc->sc_chipset < XI_CHIPSET_DINGO)
bus_space_write_1(bst, bsh, TX1MSK, 0xb0);
bus_space_write_1(bst, bsh, RXST0, 0);
bus_space_write_1(bst, bsh, TXST0, 0);
bus_space_write_1(bst, bsh, TXST1, 0);
PAGE(sc, 2);
x = 0;
if (LIST_FIRST(&sc->sc_mii.mii_phys))
x |= SELECT_MII;
bus_space_write_1(bst, bsh, MSR, x);
tsleep(&xi_full_reset, PWAIT, "xirst5", hz * 20 / 1000);
bus_space_write_1(bst, bsh, LED,
(LED_TX_ACT << LED1_SHIFT) | (LED_10MB_LINK << LED0_SHIFT));
if (sc->sc_chipset >= XI_CHIPSET_DINGO)
bus_space_write_1(bst, bsh, LED3, LED_100MB_LINK << LED3_SHIFT);
PAGE(sc, 0);
}