313 lines
7.5 KiB
C
313 lines
7.5 KiB
C
/* $NetBSD: if_mec.c,v 1.12 2003/11/17 10:07:58 keihan Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2000 Soren S. Jorvang
|
|
* 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 for the
|
|
* NetBSD Project. See http://www.NetBSD.org/ for
|
|
* information about NetBSD.
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* MACE MAC-110 ethernet driver
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: if_mec.c,v 1.12 2003/11/17 10:07:58 keihan Exp $");
|
|
|
|
#include "opt_inet.h"
|
|
#include "opt_ns.h"
|
|
#include "bpfilter.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/errno.h>
|
|
|
|
#include <machine/endian.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_ether.h>
|
|
|
|
#if NBPFILTER > 0
|
|
#include <net/bpf.h>
|
|
#endif
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_inarp.h>
|
|
#endif
|
|
|
|
#ifdef NS
|
|
#include <netns/ns.h>
|
|
#include <netns/ns_if.h>
|
|
#endif
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <dev/mii/mii.h>
|
|
#include <dev/mii/miivar.h>
|
|
|
|
#include <sgimips/dev/macevar.h>
|
|
|
|
#include <sgimips/dev/if_mecreg.h>
|
|
|
|
struct mec_softc {
|
|
struct device sc_dev;
|
|
|
|
bus_space_tag_t sc_st;
|
|
bus_space_handle_t sc_sh;
|
|
bus_dma_tag_t sc_dmat;
|
|
|
|
struct ethercom sc_ethercom;
|
|
|
|
unsigned char sc_enaddr[ETHER_ADDR_LEN];
|
|
|
|
void *sc_sdhook;
|
|
|
|
struct mii_data sc_mii;
|
|
struct callout sc_callout;
|
|
|
|
#if NRND > 0
|
|
rndsource_element_t rnd_source; /* random source */
|
|
#endif
|
|
};
|
|
|
|
static int mec_match(struct device *, struct cfdata *, void *);
|
|
static void mec_attach(struct device *, struct device *, void *);
|
|
#if 0
|
|
static void mec_start(struct ifnet *);
|
|
static void mec_watchdog(struct ifnet *);
|
|
static int mec_ioctl(struct ifnet *, u_long, caddr_t);
|
|
#endif
|
|
static int mec_mii_readreg(struct device *, int, int);
|
|
static void mec_mii_writereg(struct device *, int, int, int);
|
|
static int mec_mii_wait(struct mec_softc *);
|
|
static void mec_statchg(struct device *);
|
|
static int mec_mediachange(struct ifnet *);
|
|
static void mec_mediastatus(struct ifnet *, struct ifmediareq *);
|
|
|
|
CFATTACH_DECL(mec, sizeof(struct mec_softc),
|
|
mec_match, mec_attach, NULL, NULL);
|
|
|
|
static int
|
|
mec_match(parent, match, aux)
|
|
struct device *parent;
|
|
struct cfdata *match;
|
|
void *aux;
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
mec_attach(parent, self, aux)
|
|
struct device *parent;
|
|
struct device *self;
|
|
void *aux;
|
|
{
|
|
struct mec_softc *sc = (void *)self;
|
|
struct mace_attach_args *maa = aux;
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
u_int64_t address, command;
|
|
int i;
|
|
|
|
sc->sc_st = maa->maa_st;
|
|
sc->sc_sh = maa->maa_sh;
|
|
|
|
printf(": MAC-110 Ethernet, ");
|
|
command = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_MAC_CONTROL);
|
|
printf("rev %lld\n",
|
|
(command & MEC_MAC_REVISION) >> MEC_MAC_REVISION_SHIFT);
|
|
|
|
/*
|
|
* The firmware has left us the station address.
|
|
*/
|
|
address = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_STATION);
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
sc->sc_enaddr[ETHER_ADDR_LEN - 1 - i] = address & 0xff;
|
|
address >>= 8;
|
|
}
|
|
|
|
printf("%s: station address %s\n", sc->sc_dev.dv_xname,
|
|
ether_sprintf(sc->sc_enaddr));
|
|
|
|
/*
|
|
* Reset device.
|
|
*/
|
|
bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_MAC_CONTROL, 0);
|
|
delay(1000);
|
|
|
|
printf("%s: sorry, this is not a real driver\n", sc->sc_dev.dv_xname);
|
|
|
|
#if 0
|
|
strcpy(ifp->if_xname, sc->sc_dev.dv_xname);
|
|
ifp->if_softc = sc;
|
|
ifp->if_mtu = ETHERMTU;
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
ifp->if_ioctl = mec_ioctl;
|
|
ifp->if_start = mec_start;
|
|
ifp->if_watchdog = mec_watchdog;
|
|
#endif
|
|
|
|
sc->sc_mii.mii_ifp = ifp;
|
|
sc->sc_mii.mii_readreg = mec_mii_readreg;
|
|
sc->sc_mii.mii_writereg = mec_mii_writereg;
|
|
sc->sc_mii.mii_statchg = mec_statchg;
|
|
|
|
ifmedia_init(&sc->sc_mii.mii_media, 0, mec_mediachange,
|
|
mec_mediastatus);
|
|
mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, MII_PHY_ANY,
|
|
MII_OFFSET_ANY, 0);
|
|
if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
|
|
ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
|
|
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE);
|
|
} else
|
|
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);
|
|
|
|
return; /* XXX */
|
|
|
|
if_attach(ifp);
|
|
ether_ifattach(ifp, sc->sc_enaddr);
|
|
}
|
|
|
|
int
|
|
mec_mii_readreg(self, phy, reg)
|
|
struct device *self;
|
|
int phy;
|
|
int reg;
|
|
{
|
|
struct mec_softc *sc = (struct mec_softc *)self;
|
|
u_int64_t val;
|
|
int i;
|
|
|
|
if (mec_mii_wait(sc) != 0)
|
|
return 0;
|
|
|
|
bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_PHY_ADDRESS,
|
|
phy << MEC_PHY_ADDR_DEVSHIFT | reg);
|
|
|
|
bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_PHY_READ_INITIATE, 1);
|
|
|
|
for (i = 0; i < 20; i++) {
|
|
delay(30);
|
|
|
|
val = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_PHY_DATA);
|
|
|
|
if ((val & MEC_PHY_DATA_BUSY) == 0)
|
|
return (int)val & MEC_PHY_DATA_VALUE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
mec_mii_writereg(self, phy, reg, val)
|
|
struct device *self;
|
|
int phy, reg, val;
|
|
{
|
|
struct mec_softc *sc = (struct mec_softc *)self;
|
|
|
|
if (mec_mii_wait(sc) != 0)
|
|
return;
|
|
|
|
bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_PHY_ADDRESS,
|
|
phy << MEC_PHY_ADDR_DEVSHIFT | reg);
|
|
|
|
bus_space_write_8(sc->sc_st, sc->sc_sh, MEC_PHY_DATA,
|
|
val & MEC_PHY_DATA_VALUE);
|
|
|
|
(void)mec_mii_wait(sc);
|
|
|
|
return;
|
|
}
|
|
|
|
int
|
|
mec_mii_wait(sc)
|
|
struct mec_softc *sc;
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
u_int64_t busy;
|
|
|
|
delay(30);
|
|
|
|
busy = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_PHY_DATA);
|
|
|
|
if ((busy & MEC_PHY_DATA_BUSY) == 0)
|
|
return 0;
|
|
if (busy == 0xffff)
|
|
return 0;
|
|
}
|
|
|
|
printf("%s: MII timed out\n", sc->sc_dev.dv_xname);
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
mec_statchg(self)
|
|
struct device *self;
|
|
{
|
|
#if 0
|
|
struct mec_softc *sc = (void *)self;
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
mec_mediastatus(ifp, ifmr)
|
|
struct ifnet *ifp;
|
|
struct ifmediareq *ifmr;
|
|
{
|
|
struct mec_softc *sc = ifp->if_softc;
|
|
|
|
mii_pollstat(&sc->sc_mii);
|
|
ifmr->ifm_status = sc->sc_mii.mii_media_status;
|
|
ifmr->ifm_active = sc->sc_mii.mii_media_active;
|
|
}
|
|
|
|
int
|
|
mec_mediachange(ifp)
|
|
struct ifnet *ifp;
|
|
{
|
|
struct mec_softc *sc = ifp->if_softc;
|
|
|
|
if (ifp->if_flags & IFF_UP)
|
|
mii_mediachg(&sc->sc_mii);
|
|
return 0;
|
|
}
|