diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2008-09-26 10:35:16 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2008-09-26 10:35:16 +0000 |
commit | 17fa253b80cce2fd92c8f1af0f69aff3ee1eadd7 (patch) | |
tree | 40490af44b8a4f9e7beb12df07d6a67a20922a44 /sys/dev | |
parent | 3cfa51bd587e168a001ea6c5e1d0cfb55710cd87 (diff) |
Add drivers for the JMicron JMC250/JMC260 Ethernet controllers
and JMicron JMP202/JMP211 Ethernet PHYs.
Written by Pyun YongHyeon for FreeBSD, ported to DragonFlyBSD
by Sepherosa Ziehau and then ported to OpenBSD by me.
Thanks once again to JMicron for supplying hardware and
information which made this possible.
Some cleanup still needs to be done, and checksum offload
needs to be sorted out, but the driver otherwise seems
to work great. Comitted over a JMC250 card.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/mii/files.mii | 6 | ||||
-rw-r--r-- | sys/dev/mii/jmphy.c | 356 | ||||
-rw-r--r-- | sys/dev/mii/jmphyreg.h | 115 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 7 | ||||
-rw-r--r-- | sys/dev/pci/if_jme.c | 2391 | ||||
-rw-r--r-- | sys/dev/pci/if_jmereg.h | 992 | ||||
-rw-r--r-- | sys/dev/pci/if_jmevar.h | 244 |
7 files changed, 4109 insertions, 2 deletions
diff --git a/sys/dev/mii/files.mii b/sys/dev/mii/files.mii index 87b67536c9d..866728f4b3e 100644 --- a/sys/dev/mii/files.mii +++ b/sys/dev/mii/files.mii @@ -1,4 +1,4 @@ -# $OpenBSD: files.mii,v 1.28 2008/09/25 20:47:16 brad Exp $ +# $OpenBSD: files.mii,v 1.29 2008/09/26 10:35:15 jsg Exp $ # $NetBSD: files.mii,v 1.13 1998/11/05 00:36:48 thorpej Exp $ file dev/mii/mii.c mii @@ -129,6 +129,10 @@ device etphy: mii_phy attach etphy at mii file dev/mii/etphy.c etphy +device jmphy: mii_phy +attach jmphy at mii +file dev/mii/jmphy.c jmphy + device atphy: mii_phy attach atphy at mii file dev/mii/atphy.c atphy diff --git a/sys/dev/mii/jmphy.c b/sys/dev/mii/jmphy.c new file mode 100644 index 00000000000..261106d64bf --- /dev/null +++ b/sys/dev/mii/jmphy.c @@ -0,0 +1,356 @@ +/* $OpenBSD: jmphy.c,v 1.1 2008/09/26 10:35:15 jsg Exp $ */ +/*- + * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/mii/jmphy.c,v 1.1 2008/05/27 01:16:40 yongari Exp $ + * $DragonFly: src/sys/dev/netif/mii_layer/jmphy.c,v 1.1 2008/07/22 11:28:49 sephe Exp $ + */ + +/* + * Driver for the JMicron JMP211 10/100/1000, JMP202 10/100 PHY. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/miidevs.h> +#include <dev/mii/jmphyreg.h> + +int jmphy_service(struct mii_softc *, struct mii_data *, int); +void jmphy_status(struct mii_softc *); +int jmphy_match(struct device *, void *, void *); +void jmphy_attach(struct device *, struct device *, void *); +void jmphy_reset(struct mii_softc *); +uint16_t jmphy_anar(struct ifmedia_entry *); +int jmphy_auto(struct mii_softc *, struct ifmedia_entry *); + +const struct mii_phy_funcs jmphy_funcs = { + jmphy_service, jmphy_status, jmphy_reset, +}; + +struct cfattach jmphy_ca = { + sizeof (struct mii_softc), jmphy_match, jmphy_attach, + mii_phy_detach, mii_phy_activate +}; + +struct cfdriver jmphy_cd = { + NULL, "jmphy", DV_DULL +}; + +static const struct mii_phydesc jmphys[] = { + { MII_OUI_JMICRON, MII_MODEL_JMICRON_JMP202, + MII_STR_JMICRON_JMP202 }, + { MII_OUI_JMICRON, MII_MODEL_JMICRON_JMP211, + MII_STR_JMICRON_JMP211 }, + { 0, 0, + NULL }, +}; + +int +jmphy_match(struct device *parent, void *match, void *aux) +{ + struct mii_attach_args *ma = aux; + + if (mii_phy_match(ma, jmphys) != NULL) + return (10); + + return (0); +} + +void +jmphy_attach(struct device *parent, struct device *self, void *aux) +{ + struct mii_softc *sc = (struct mii_softc *)self; + struct mii_attach_args *ma = aux; + struct mii_data *mii = ma->mii_data; + const struct mii_phydesc *mpd; + + mpd = mii_phy_match(ma, jmphys); + printf(": %s, rev. %d\n", mpd->mpd_name, MII_REV(ma->mii_id2)); + + sc->mii_inst = mii->mii_instance; + sc->mii_phy = ma->mii_phyno; + sc->mii_funcs = &jmphy_funcs; + sc->mii_model = MII_MODEL(ma->mii_id2); + sc->mii_pdata = mii; + sc->mii_flags = ma->mii_flags; + + sc->mii_flags |= MIIF_NOISOLATE | MIIF_NOLOOP; + + jmphy_reset(sc); + + sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; + if (sc->mii_capabilities & BMSR_EXTSTAT) + sc->mii_extcapabilities = PHY_READ(sc, MII_EXTSR); + + if ((sc->mii_capabilities & BMSR_MEDIAMASK) == 0 && + (sc->mii_extcapabilities & EXTSR_MEDIAMASK) == 0) + ; + else + mii_phy_add_media(sc); +} + +int +jmphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) +{ + struct ifmedia_entry *ife = mii->mii_media.ifm_cur; + uint16_t bmcr; + + switch (cmd) { + case MII_POLLSTAT: + /* + * If we're not polling our PHY instance, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + break; + + case MII_MEDIACHG: + /* + * If the media indicates a different PHY instance, + * isolate ourselves. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) { + bmcr = PHY_READ(sc, MII_BMCR); + PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO); + return (0); + } + + /* + * If the interface is not up, don't do anything. + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + break; + + if (jmphy_auto(sc, ife) != EJUSTRETURN) + return (EINVAL); + break; + + case MII_TICK: + /* + * If we're not currently selected, just return. + */ + if (IFM_INST(ife->ifm_media) != sc->mii_inst) + return (0); + + /* + * Is the interface even up? + */ + if ((mii->mii_ifp->if_flags & IFF_UP) == 0) + return (0); + + /* + * Only used for autonegotiation. + */ + if (IFM_SUBTYPE(ife->ifm_media) != IFM_AUTO) + break; + + /* Check for link. */ + if (PHY_READ(sc, JMPHY_SSR) & JMPHY_SSR_LINK_UP) { + sc->mii_ticks = 0; + break; + } + + /* Announce link loss right after it happens. */ + if (sc->mii_ticks++ == 0) + break; + if (sc->mii_ticks <= sc->mii_anegticks) + return (0); + + sc->mii_ticks = 0; + jmphy_auto(sc, ife); + break; + } + + /* Update the media status. */ + jmphy_status(sc); + + /* Callback if something changed. */ + mii_phy_update(sc, cmd); + return (0); +} + +void +jmphy_status(struct mii_softc *sc) +{ + struct mii_data *mii = sc->mii_pdata; + int bmcr, ssr; + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + ssr = PHY_READ(sc, JMPHY_SSR); + if ((ssr & JMPHY_SSR_LINK_UP) != 0) + mii->mii_media_status |= IFM_ACTIVE; + + bmcr = PHY_READ(sc, MII_BMCR); + if ((bmcr & BMCR_ISO) != 0) { + mii->mii_media_active |= IFM_NONE; + mii->mii_media_status = 0; + return; + } + + if ((bmcr & BMCR_LOOP) != 0) + mii->mii_media_active |= IFM_LOOP; + + if ((ssr & JMPHY_SSR_SPD_DPLX_RESOLVED) == 0) { + /* Erg, still trying, I guess... */ + mii->mii_media_active |= IFM_NONE; + return; + } + + switch ((ssr & JMPHY_SSR_SPEED_MASK)) { + case JMPHY_SSR_SPEED_1000: + mii->mii_media_active |= IFM_1000_T; + /* + * jmphy(4) got a valid link so reset mii_ticks. + * Resetting mii_ticks is needed in order to + * detect link loss after auto-negotiation. + */ + sc->mii_ticks = 0; + break; + case JMPHY_SSR_SPEED_100: + mii->mii_media_active |= IFM_100_TX; + sc->mii_ticks = 0; + break; + case JMPHY_SSR_SPEED_10: + mii->mii_media_active |= IFM_10_T; + sc->mii_ticks = 0; + break; + default: + mii->mii_media_active |= IFM_NONE; + return; + } + + if ((ssr & JMPHY_SSR_DUPLEX) != 0) + mii->mii_media_active |= IFM_FDX; + else + mii->mii_media_active |= IFM_HDX; + /* XXX Flow-control. */ +#ifdef notyet + if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T) { + if ((PHY_READ(sc, MII_100T2SR) & GTSR_MS_RES) != 0) + mii->mii_media_active |= IFM_ETH_MASTER; + } +#endif +} + +void +jmphy_reset(struct mii_softc *sc) +{ + int i; + + /* Disable sleep mode. */ + PHY_WRITE(sc, JMPHY_TMCTL, + PHY_READ(sc, JMPHY_TMCTL) & ~JMPHY_TMCTL_SLEEP_ENB); + PHY_WRITE(sc, MII_BMCR, BMCR_RESET | BMCR_AUTOEN); + + for (i = 0; i < 1000; i++) { + DELAY(1); + if ((PHY_READ(sc, MII_BMCR) & BMCR_RESET) == 0) + break; + } +} + +uint16_t +jmphy_anar(struct ifmedia_entry *ife) +{ + uint16_t anar; + + anar = 0; + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_AUTO: + anar |= ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10; + break; + case IFM_1000_T: + break; + case IFM_100_TX: + anar |= ANAR_TX | ANAR_TX_FD; + break; + case IFM_10_T: + anar |= ANAR_10 | ANAR_10_FD; + break; + default: + break; + } + + return (anar); +} + +int +jmphy_auto(struct mii_softc *sc, struct ifmedia_entry *ife) +{ + uint16_t anar, bmcr, gig; + + gig = 0; + bmcr = PHY_READ(sc, MII_BMCR); + switch (IFM_SUBTYPE(ife->ifm_media)) { + case IFM_AUTO: + gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; + break; + case IFM_1000_T: + gig |= GTCR_ADV_1000TFDX | GTCR_ADV_1000THDX; + break; + case IFM_100_TX: + case IFM_10_T: + break; + case IFM_NONE: + PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_ISO | BMCR_PDOWN); + return (EJUSTRETURN); + default: + return (EINVAL); + } + + if ((ife->ifm_media & IFM_LOOP) != 0) + bmcr |= BMCR_LOOP; + + anar = jmphy_anar(ife); + /* XXX Always advertise pause capability. */ + anar |= (3 << 10); + + if ((sc->mii_flags & MIIF_HAVE_GTCR) != 0) { +#ifdef notyet + struct mii_data *mii; + + mii = sc->mii_pdata; + if ((mii->mii_media.ifm_media & IFM_ETH_MASTER) != 0) + gig |= GTCR_MAN_MS | GTCR_MAN_ADV; +#endif + PHY_WRITE(sc, MII_100T2CR, gig); + } + PHY_WRITE(sc, MII_ANAR, anar | ANAR_CSMA); + PHY_WRITE(sc, MII_BMCR, bmcr | BMCR_AUTOEN | BMCR_STARTNEG); + + return (EJUSTRETURN); +} diff --git a/sys/dev/mii/jmphyreg.h b/sys/dev/mii/jmphyreg.h new file mode 100644 index 00000000000..d8f58366f25 --- /dev/null +++ b/sys/dev/mii/jmphyreg.h @@ -0,0 +1,115 @@ +/* $OpenBSD: jmphyreg.h,v 1.1 2008/09/26 10:35:15 jsg Exp $ */ +/*- + * Copyright (c) 2008, Pyun YongHyeon + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/mii/jmphyreg.h,v 1.1 2008/05/27 01:16:40 yongari Exp $ + * $DragonFly: src/sys/dev/netif/mii_layer/jmphyreg.h,v 1.2 2008/09/13 04:04:39 sephe Exp $ + */ + +#ifndef _DEV_MII_JMPHYREG_H_ +#define _DEV_MII_JMPHYREG_H_ + +/* + * Registers for the JMicron JMC250 Gigabit PHY. + */ + +/* PHY specific status register. */ +#define JMPHY_SSR 0x11 +#define JMPHY_SSR_SPEED_1000 0x8000 +#define JMPHY_SSR_SPEED_100 0x4000 +#define JMPHY_SSR_SPEED_10 0x0000 +#define JMPHY_SSR_SPEED_MASK 0xC000 +#define JMPHY_SSR_DUPLEX 0x2000 +#define JMPHY_SSR_SPD_DPLX_RESOLVED 0x0800 +#define JMPHY_SSR_LINK_UP 0x0400 +#define JMPHY_SSR_MDI_XOVER 0x0040 +#define JMPHY_SSR_INV_POLARITY 0x0002 + +/* PHY specific cable length status register. */ +#define JMPHY_SCL 0x17 +#define JMPHY_SCL_CHAN_D_MASK 0xF000 +#define JMPHY_SCL_CHAN_C_MASK 0x0F00 +#define JMPHY_SCL_CHAN_B_MASK 0x00F0 +#define JMPHY_SCL_CHAN_A_MASK 0x000F +#define JMPHY_SCL_LEN_35 0 +#define JMPHY_SCL_LEN_40 1 +#define JMPHY_SCL_LEN_50 2 +#define JMPHY_SCL_LEN_60 3 +#define JMPHY_SCL_LEN_70 4 +#define JMPHY_SCL_LEN_80 5 +#define JMPHY_SCL_LEN_90 6 +#define JMPHY_SCL_LEN_100 7 +#define JMPHY_SCL_LEN_110 8 +#define JMPHY_SCL_LEN_120 9 +#define JMPHY_SCL_LEN_130 10 +#define JMPHY_SCL_LEN_140 11 +#define JMPHY_SCL_LEN_150 12 +#define JMPHY_SCL_LEN_160 13 +#define JMPHY_SCL_LEN_170 14 +#define JMPHY_SCL_RSVD 15 + +/* PHY specific LED control register 1. */ +#define JMPHY_LED_CTL1 0x18 +#define JMPHY_LED_BLINK_42MS 0x0000 +#define JMPHY_LED_BLINK_84MS 0x2000 +#define JMPHY_LED_BLINK_170MS 0x4000 +#define JMPHY_LED_BLINK_340MS 0x6000 +#define JMPHY_LED_BLINK_670MS 0x8000 +#define JMPHY_LED_BLINK_MASK 0xE000 +#define JMPHY_LED_FLP_GAP_MASK 0x1F00 +#define JMPHY_LED_FLP_GAP_DEFULT 0x1000 +#define JMPHY_LED2_POLARITY_MASK 0x0030 +#define JMPHY_LED1_POLARITY_MASK 0x000C +#define JMPHY_LED0_POLARITY_MASK 0x0003 +#define JMPHY_LED_ON_LO_OFF_HI 0 +#define JMPHY_LED_ON_HI_OFF_HI 1 +#define JMPHY_LED_ON_LO_OFF_TS 2 +#define JMPHY_LED_ON_HI_OFF_TS 3 + +/* PHY specific LED control register 2. */ +#define JMPHY_LED_CTL2 0x19 +#define JMPHY_LED_NO_STRETCH 0x0000 +#define JMPHY_LED_STRETCH_42MS 0x2000 +#define JMPHY_LED_STRETCH_84MS 0x4000 +#define JMPHY_LED_STRETCH_170MS 0x6000 +#define JMPHY_LED_STRETCH_340MS 0x8000 +#define JMPHY_LED_STRETCH_670MS 0xB000 +#define JMPHY_LED_STRETCH_1300MS 0xC000 +#define JMPHY_LED_STRETCH_2700MS 0xE000 +#define JMPHY_LED2_MODE_MASK 0x0F00 +#define JMPHY_LED1_MODE_MASK 0x00F0 +#define JMPHY_LED0_MODE_MASK 0x000F + +/* PHY specific test mode control register. */ +#define JMPHY_TMCTL 0x1A +#define JMPHY_TMCTL_SLEEP_ENB 0x1000 + +/* PHY specific configuration */ +#define JMPHY_CONF 0x1B +#define JMPHY_CONF_EXTFIFO 0x0000 /* use extended fifo */ +#define JMPHY_CONF_DEFFIFO 0x0004 /* use default fifo */ + +#endif /* _DEV_MII_JMPHYREG_H_ */ diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index c7d16768808..c5b254d04b6 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $OpenBSD: files.pci,v 1.256 2008/09/17 06:18:45 brad Exp $ +# $OpenBSD: files.pci,v 1.257 2008/09/26 10:35:15 jsg Exp $ # $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $ # # Config file and device description for machine-independent PCI code. @@ -627,6 +627,11 @@ device et: ether, ifnet, mii, ifmedia, mii_phy attach et at pci file dev/pci/if_et.c et +# JMicron JMC250/JMC260 Ethernet +device jme: ether, ifnet, mii, ifmedia, mii_phy +attach jme at pci +file dev/pci/if_jme.c jme + # AMD-76x PM and SMBus controller device amdpm: i2cbus attach amdpm at pci diff --git a/sys/dev/pci/if_jme.c b/sys/dev/pci/if_jme.c new file mode 100644 index 00000000000..493fcdf8396 --- /dev/null +++ b/sys/dev/pci/if_jme.c @@ -0,0 +1,2391 @@ +/* $OpenBSD: if_jme.c,v 1.1 2008/09/26 10:35:15 jsg Exp $ */ +/*- + * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/jme/if_jme.c,v 1.2 2008/07/18 04:20:48 yongari Exp $ + * $DragonFly: src/sys/dev/netif/jme/if_jme.c,v 1.7 2008/09/13 04:04:39 sephe Exp $ + */ + +#include "bpfilter.h" +#include "vlan.h" + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/queue.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/timeout.h> +#include <sys/socket.h> + +#include <machine/bus.h> + +#include <net/if.h> +#include <net/if_dl.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_ether.h> +#endif + +#if NVLAN > 0 +#include <net/if_types.h> +#include <net/if_vlan_var.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <dev/rndvar.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/mii/jmphyreg.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/pci/if_jmereg.h> +#include <dev/pci/if_jmevar.h> + +/* Define the following to disable printing Rx errors. */ +#undef JME_SHOW_ERRORS + +//#define JME_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) + +int jme_match(struct device *, void *, void *); +void jme_attach(struct device *, struct device *, void *); +int jme_detach(struct device *, int); + +int jme_miibus_readreg(struct device *, int, int); +void jme_miibus_writereg(struct device *, int, int, int); +void jme_miibus_statchg(struct device *); + +int jme_init(struct ifnet *); +int jme_ioctl(struct ifnet *, u_long, caddr_t); + +void jme_start(struct ifnet *); +void jme_watchdog(struct ifnet *); +void jme_mediastatus(struct ifnet *, struct ifmediareq *); +int jme_mediachange(struct ifnet *); + +int jme_intr(void *); +void jme_txeof(struct jme_softc *); +void jme_rxeof(struct jme_softc *); + +int jme_dma_alloc(struct jme_softc *); +void jme_dma_free(struct jme_softc *); +int jme_init_rx_ring(struct jme_softc *); +void jme_init_tx_ring(struct jme_softc *); +void jme_init_ssb(struct jme_softc *); +int jme_newbuf(struct jme_softc *, struct jme_rxdesc *, int); +int jme_encap(struct jme_softc *, struct mbuf **); +void jme_rxpkt(struct jme_softc *); + +void jme_tick(void *); +void jme_stop(struct jme_softc *); +void jme_reset(struct jme_softc *); +void jme_set_vlan(struct jme_softc *); +void jme_set_filter(struct jme_softc *); +void jme_stop_tx(struct jme_softc *); +void jme_stop_rx(struct jme_softc *); +void jme_mac_config(struct jme_softc *); +void jme_reg_macaddr(struct jme_softc *, uint8_t[]); +int jme_eeprom_macaddr(struct jme_softc *, uint8_t[]); +int jme_eeprom_read_byte(struct jme_softc *, uint8_t, uint8_t *); +void jme_discard_rxbufs(struct jme_softc *, int, int); +#ifdef notyet +void jme_setwol(struct jme_softc *); +void jme_setlinkspeed(struct jme_softc *); +#endif + +/* + * Devices supported by this driver. + */ +const struct pci_matchid jme_devices[] = { + { PCI_VENDOR_JMICRON, PCI_PRODUCT_JMICRON_JMC250 }, + { PCI_VENDOR_JMICRON, PCI_PRODUCT_JMICRON_JMC260 } +}; + +struct cfattach jme_ca = { + sizeof (struct jme_softc), jme_match, jme_attach +}; + +struct cfdriver jme_cd = { + NULL, "jme", DV_IFNET +}; + +int jmedebug = 0; +#define DPRINTF(x) do { if (jmedebug) printf x; } while (0) + +/* + * Read a PHY register on the MII of the JMC250. + */ +int +jme_miibus_readreg(struct device *dev, int phy, int reg) +{ + struct jme_softc *sc = (struct jme_softc *)dev; + uint32_t val; + int i; + + /* For FPGA version, PHY address 0 should be ignored. */ + if (sc->jme_caps & JME_CAP_FPGA) { + if (phy == 0) + return (0); + } else { + if (sc->jme_phyaddr != phy) + return (0); + } + + CSR_WRITE_4(sc, JME_SMI, SMI_OP_READ | SMI_OP_EXECUTE | + SMI_PHY_ADDR(phy) | SMI_REG_ADDR(reg)); + + for (i = JME_PHY_TIMEOUT; i > 0; i--) { + DELAY(1); + if (((val = CSR_READ_4(sc, JME_SMI)) & SMI_OP_EXECUTE) == 0) + break; + } + if (i == 0) { + printf("%s: phy read timeout: phy %d, reg %d\n", + sc->sc_dev.dv_xname, phy, reg); + return (0); + } + + return ((val & SMI_DATA_MASK) >> SMI_DATA_SHIFT); +} + +/* + * Write a PHY register on the MII of the JMC250. + */ +void +jme_miibus_writereg(struct device *dev, int phy, int reg, int val) +{ + struct jme_softc *sc = (struct jme_softc *)dev; + int i; + + /* For FPGA version, PHY address 0 should be ignored. */ + if (sc->jme_caps & JME_CAP_FPGA) { + if (phy == 0) + return; + } else { + if (sc->jme_phyaddr != phy) + return; + } + + CSR_WRITE_4(sc, JME_SMI, SMI_OP_WRITE | SMI_OP_EXECUTE | + ((val << SMI_DATA_SHIFT) & SMI_DATA_MASK) | + SMI_PHY_ADDR(phy) | SMI_REG_ADDR(reg)); + + for (i = JME_PHY_TIMEOUT; i > 0; i--) { + DELAY(1); + if (((val = CSR_READ_4(sc, JME_SMI)) & SMI_OP_EXECUTE) == 0) + break; + } + if (i == 0) { + printf("%s: phy write timeout: phy %d, reg %d\n", + sc->sc_dev.dv_xname, phy, reg); + } +} + +/* + * Callback from MII layer when media changes. + */ +void +jme_miibus_statchg(struct device *dev) +{ + struct jme_softc *sc = (struct jme_softc *)dev; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct mii_data *mii; + struct jme_txdesc *txd; + bus_addr_t paddr; + int i; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + mii = &sc->sc_miibus; + + sc->jme_flags &= ~JME_FLAG_LINK; + if ((mii->mii_media_status & IFM_AVALID) != 0) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + sc->jme_flags |= JME_FLAG_LINK; + break; + case IFM_1000_T: + if (sc->jme_caps & JME_CAP_FASTETH) + break; + sc->jme_flags |= JME_FLAG_LINK; + break; + default: + break; + } + } + + /* + * Disabling Rx/Tx MACs have a side-effect of resetting + * JME_TXNDA/JME_RXNDA register to the first address of + * Tx/Rx descriptor address. So driver should reset its + * internal procucer/consumer pointer and reclaim any + * allocated resources. Note, just saving the value of + * JME_TXNDA and JME_RXNDA registers before stopping MAC + * and restoring JME_TXNDA/JME_RXNDA register is not + * sufficient to make sure correct MAC state because + * stopping MAC operation can take a while and hardware + * might have updated JME_TXNDA/JME_RXNDA registers + * during the stop operation. + */ + + /* Disable interrupts */ + CSR_WRITE_4(sc, JME_INTR_MASK_CLR, JME_INTRS); + + /* Stop driver */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + timeout_del(&sc->jme_tick_ch); + + /* Stop receiver/transmitter. */ + jme_stop_rx(sc); + jme_stop_tx(sc); + + jme_rxeof(sc); + if (sc->jme_cdata.jme_rxhead != NULL) + m_freem(sc->jme_cdata.jme_rxhead); + JME_RXCHAIN_RESET(sc); + + jme_txeof(sc); + if (sc->jme_cdata.jme_tx_cnt != 0) { + /* Remove queued packets for transmit. */ + for (i = 0; i < JME_TX_RING_CNT; i++) { + txd = &sc->jme_cdata.jme_txdesc[i]; + if (txd->tx_m != NULL) { + bus_dmamap_unload(sc->sc_dmat, txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; + txd->tx_ndesc = 0; + ifp->if_oerrors++; + } + } + } + + /* + * Reuse configured Rx descriptors and reset + * procuder/consumer index. + */ + sc->jme_cdata.jme_rx_cons = 0; + + jme_init_tx_ring(sc); + + /* Initialize shadow status block. */ + jme_init_ssb(sc); + + /* Program MAC with resolved speed/duplex/flow-control. */ + if (sc->jme_flags & JME_FLAG_LINK) { + jme_mac_config(sc); + + CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr); + CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr); + + /* Set Tx ring address to the hardware. */ + paddr = JME_TX_RING_ADDR(sc, 0); + CSR_WRITE_4(sc, JME_TXDBA_HI, JME_ADDR_HI(paddr)); + CSR_WRITE_4(sc, JME_TXDBA_LO, JME_ADDR_LO(paddr)); + + /* Set Rx ring address to the hardware. */ + paddr = JME_RX_RING_ADDR(sc, 0); + CSR_WRITE_4(sc, JME_RXDBA_HI, JME_ADDR_HI(paddr)); + CSR_WRITE_4(sc, JME_RXDBA_LO, JME_ADDR_LO(paddr)); + + /* Restart receiver/transmitter. */ + CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr | RXCSR_RX_ENB | + RXCSR_RXQ_START); + CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr | TXCSR_TX_ENB); + } + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + timeout_add_sec(&sc->jme_tick_ch, 1); + + /* Reenable interrupts. */ + CSR_WRITE_4(sc, JME_INTR_MASK_SET, JME_INTRS); +} + +/* + * Get the current interface media status. + */ +void +jme_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct jme_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_miibus; + + mii_pollstat(mii); + ifmr->ifm_status = mii->mii_media_status; + ifmr->ifm_active = mii->mii_media_active; +} + +/* + * Set hardware to newly-selected media. + */ +int +jme_mediachange(struct ifnet *ifp) +{ + struct jme_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_miibus; + int error; + + if (mii->mii_instance != 0) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + error = mii_mediachg(mii); + + return (error); +} + +int +jme_match(struct device *dev, void *match, void *aux) +{ + return pci_matchbyid((struct pci_attach_args *)aux, jme_devices, + sizeof (jme_devices) / sizeof (jme_devices[0])); +} + +int +jme_eeprom_read_byte(struct jme_softc *sc, uint8_t addr, uint8_t *val) +{ + uint32_t reg; + int i; + + *val = 0; + for (i = JME_TIMEOUT; i > 0; i--) { + reg = CSR_READ_4(sc, JME_SMBCSR); + if ((reg & SMBCSR_HW_BUSY_MASK) == SMBCSR_HW_IDLE) + break; + DELAY(1); + } + + if (i == 0) { + printf("%s: EEPROM idle timeout!\n", sc->sc_dev.dv_xname); + return (ETIMEDOUT); + } + + reg = ((uint32_t)addr << SMBINTF_ADDR_SHIFT) & SMBINTF_ADDR_MASK; + CSR_WRITE_4(sc, JME_SMBINTF, reg | SMBINTF_RD | SMBINTF_CMD_TRIGGER); + for (i = JME_TIMEOUT; i > 0; i--) { + DELAY(1); + reg = CSR_READ_4(sc, JME_SMBINTF); + if ((reg & SMBINTF_CMD_TRIGGER) == 0) + break; + } + + if (i == 0) { + printf("%s: EEPROM read timeout!\n", sc->sc_dev.dv_xname); + return (ETIMEDOUT); + } + + reg = CSR_READ_4(sc, JME_SMBINTF); + *val = (reg & SMBINTF_RD_DATA_MASK) >> SMBINTF_RD_DATA_SHIFT; + + return (0); +} + +int +jme_eeprom_macaddr(struct jme_softc *sc, uint8_t eaddr[]) +{ + uint8_t fup, reg, val; + uint32_t offset; + int match; + + offset = 0; + if (jme_eeprom_read_byte(sc, offset++, &fup) != 0 || + fup != JME_EEPROM_SIG0) + return (ENOENT); + if (jme_eeprom_read_byte(sc, offset++, &fup) != 0 || + fup != JME_EEPROM_SIG1) + return (ENOENT); + match = 0; + do { + if (jme_eeprom_read_byte(sc, offset, &fup) != 0) + break; + /* Check for the end of EEPROM descriptor. */ + if ((fup & JME_EEPROM_DESC_END) == JME_EEPROM_DESC_END) + break; + if ((uint8_t)JME_EEPROM_MKDESC(JME_EEPROM_FUNC0, + JME_EEPROM_PAGE_BAR1) == fup) { + if (jme_eeprom_read_byte(sc, offset + 1, ®) != 0) + break; + if (reg >= JME_PAR0 && + reg < JME_PAR0 + ETHER_ADDR_LEN) { + if (jme_eeprom_read_byte(sc, offset + 2, + &val) != 0) + break; + eaddr[reg - JME_PAR0] = val; + match++; + } + } + /* Try next eeprom descriptor. */ + offset += JME_EEPROM_DESC_BYTES; + } while (match != ETHER_ADDR_LEN && offset < JME_EEPROM_END); + + if (match == ETHER_ADDR_LEN) + return (0); + + return (ENOENT); +} + +void +jme_reg_macaddr(struct jme_softc *sc, uint8_t eaddr[]) +{ + uint32_t par0, par1; + + /* Read station address. */ + par0 = CSR_READ_4(sc, JME_PAR0); + par1 = CSR_READ_4(sc, JME_PAR1); + par1 &= 0xFFFF; + if ((par0 == 0 && par1 == 0) || (par0 & 0x1)) { + printf("%s: generating fake ethernet address.\n", + sc->sc_dev.dv_xname); + par0 = arc4random(); + /* Set OUI to JMicron. */ + eaddr[0] = 0x00; + eaddr[1] = 0x1B; + eaddr[2] = 0x8C; + eaddr[3] = (par0 >> 16) & 0xff; + eaddr[4] = (par0 >> 8) & 0xff; + eaddr[5] = par0 & 0xff; + } else { + eaddr[0] = (par0 >> 0) & 0xFF; + eaddr[1] = (par0 >> 8) & 0xFF; + eaddr[2] = (par0 >> 16) & 0xFF; + eaddr[3] = (par0 >> 24) & 0xFF; + eaddr[4] = (par1 >> 0) & 0xFF; + eaddr[5] = (par1 >> 8) & 0xFF; + } +} + +void +jme_attach(struct device *parent, struct device *self, void *aux) +{ + struct jme_softc *sc = (struct jme_softc *)self; + struct pci_attach_args *pa = aux; + pci_chipset_tag_t pc = pa->pa_pc; + pci_intr_handle_t ih; + const char *intrstr; + pcireg_t memtype; + + struct ifnet *ifp; + uint32_t reg; +// uint8_t pcie_ptr; + int error = 0; +// uint8_t eaddr[ETHER_ADDR_LEN]; + + /* + * Allocate IO memory + * + * JMC250 supports both memory mapped and I/O register space + * access. Because I/O register access should use different + * BARs to access registers it's waste of time to use I/O + * register spce access. JMC250 uses 16K to map entire memory + * space. + */ + + memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, JME_PCIR_BAR); + if (pci_mapreg_map(pa, JME_PCIR_BAR, memtype, 0, &sc->jme_mem_bt, + &sc->jme_mem_bh, NULL, &sc->jme_mem_size, 0)) { + printf(": could not map mem space\n"); + return; + } + + if (pci_intr_map(pa, &ih) != 0) { + printf(": could not map interrupt\n"); + return; + } + + /* + * Allocate IRQ + */ + intrstr = pci_intr_string(pc, ih); + sc->sc_irq_handle = pci_intr_establish(pc, ih, IPL_NET, jme_intr, sc, + sc->sc_dev.dv_xname); + if (sc->sc_irq_handle == NULL) { + printf(": could not establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + return; + } + printf(": %s", intrstr); + + sc->sc_dmat = pa->pa_dmat; + sc->jme_pct = pa->pa_pc; + sc->jme_pcitag = pa->pa_tag; + + /* + * Extract FPGA revision + */ + reg = CSR_READ_4(sc, JME_CHIPMODE); + if (((reg & CHIPMODE_FPGA_REV_MASK) >> CHIPMODE_FPGA_REV_SHIFT) != + CHIPMODE_NOT_FPGA) { + sc->jme_caps |= JME_CAP_FPGA; + + if (jmedebug) { + printf("%s: FPGA revision : 0x%04x\n", + sc->sc_dev.dv_xname, + (reg & CHIPMODE_FPGA_REV_MASK) >> + CHIPMODE_FPGA_REV_SHIFT); + } + } + + /* Reset the ethernet controller. */ + jme_reset(sc); + + /* Get station address. */ + reg = CSR_READ_4(sc, JME_SMBCSR); + if (reg & SMBCSR_EEPROM_PRESENT) + error = jme_eeprom_macaddr(sc, sc->sc_arpcom.ac_enaddr); + if (error != 0 || (reg & SMBCSR_EEPROM_PRESENT) == 0) { + if (error != 0 && (jmedebug)) { + printf("%s: ethernet hardware address " + "not found in EEPROM.\n", sc->sc_dev.dv_xname); + } + jme_reg_macaddr(sc, sc->sc_arpcom.ac_enaddr); + } + + printf(", address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr)); + + /* + * Save PHY address. + * Integrated JR0211 has fixed PHY address whereas FPGA version + * requires PHY probing to get correct PHY address. + */ + if ((sc->jme_caps & JME_CAP_FPGA) == 0) { + sc->jme_phyaddr = CSR_READ_4(sc, JME_GPREG0) & + GPREG0_PHY_ADDR_MASK; + if (jmedebug) { + printf("%s: PHY is at address %d.\n", + sc->sc_dev.dv_xname, sc->jme_phyaddr); + } + } else { + sc->jme_phyaddr = 0; + } + + /* Set max allowable DMA size. */ + sc->jme_tx_dma_size = TXCSR_DMA_SIZE_512; + sc->jme_rx_dma_size = RXCSR_DMA_SIZE_128; + +#ifdef notyet + if (pci_find_extcap(dev, PCIY_PMG, &pmc) == 0) + sc->jme_caps |= JME_CAP_PMCAP; +#endif + + /* Allocate DMA stuffs */ + error = jme_dma_alloc(sc); + if (error) + goto fail; + + ifp = &sc->sc_arpcom.ac_if; + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = jme_init; + ifp->if_ioctl = jme_ioctl; + ifp->if_start = jme_start; + ifp->if_watchdog = jme_watchdog; + ifp->if_baudrate = IF_Gbps(1); + IFQ_SET_MAXLEN(&ifp->if_snd, JME_TX_RING_CNT - 1); + IFQ_SET_READY(&ifp->if_snd); + strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); + printf("XNAME= '%s'\n", ifp->if_xname); + + /* JMC250 supports Tx/Rx checksum offload and hardware vlan tagging. */ +#if 0 + ifp->if_capabilities = IFCAP_HWCSUM | + IFCAP_VLAN_MTU | + IFCAP_VLAN_HWTAGGING; + ifp->if_hwassist = JME_CSUM_FEATURES; + ifp->if_capenable = ifp->if_capabilities; +#endif + ifp->if_capabilities = IFCAP_VLAN_MTU; + + /* Set up MII bus. */ + sc->sc_miibus.mii_ifp = ifp; + sc->sc_miibus.mii_readreg = jme_miibus_readreg; + sc->sc_miibus.mii_writereg = jme_miibus_writereg; + sc->sc_miibus.mii_statchg = jme_miibus_statchg; + + ifmedia_init(&sc->sc_miibus.mii_media, 0, jme_mediachange, + jme_mediastatus); + mii_attach(self, &sc->sc_miibus, 0xffffffff, MII_PHY_ANY, + MII_OFFSET_ANY, 0); + + if (LIST_FIRST(&sc->sc_miibus.mii_phys) == NULL) { + printf("%s: no PHY found!\n", sc->sc_dev.dv_xname); + ifmedia_add(&sc->sc_miibus.mii_media, IFM_ETHER | IFM_MANUAL, + 0, NULL); + ifmedia_set(&sc->sc_miibus.mii_media, IFM_ETHER | IFM_MANUAL); + } else + ifmedia_set(&sc->sc_miibus.mii_media, IFM_ETHER | IFM_AUTO); + + /* + * Save PHYADDR for FPGA mode PHY not handled, not production hw + */ + + if_attach(ifp); + ether_ifattach(ifp); + + timeout_set(&sc->jme_tick_ch, jme_tick, sc); + + return; +fail: + jme_detach(&sc->sc_dev, 0); +} + +int +jme_detach(struct device *self, int flags) +{ + struct jme_softc *sc = (struct jme_softc *)self; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int s; + + s = splnet(); + jme_stop(sc); + splx(s); + + mii_detach(&sc->sc_miibus, MII_PHY_ANY, MII_OFFSET_ANY); + + /* Delete all remaining media. */ + ifmedia_delete_instance(&sc->sc_miibus.mii_media, IFM_INST_ANY); + + ether_ifdetach(ifp); + if_detach(ifp); + jme_dma_free(sc); + + if (sc->sc_irq_handle != NULL) { + pci_intr_disestablish(sc->jme_pct, sc->sc_irq_handle); + sc->sc_irq_handle = NULL; + } + + return (0); +} + +int +jme_dma_alloc(struct jme_softc *sc) +{ + struct jme_txdesc *txd; + struct jme_rxdesc *rxd; + int error, i, nsegs; + + /* + * Create DMA stuffs for TX ring + */ + + error = bus_dmamap_create(sc->sc_dmat, JME_TX_RING_SIZE, 1, + JME_TX_RING_SIZE, 0, BUS_DMA_NOWAIT, + &sc->jme_cdata.jme_tx_ring_map); + if (error) + return (ENOBUFS); + + /* Allocate DMA'able memory for TX ring */ + error = bus_dmamem_alloc(sc->sc_dmat, JME_TX_RING_SIZE, ETHER_ALIGN, 0, + &sc->jme_rdata.jme_tx_ring_seg, 1, &nsegs, + BUS_DMA_WAITOK); +/* XXX zero */ + if (error) { + printf("%s: could not allocate DMA'able memory for Tx ring.\n", + sc->sc_dev.dv_xname); + return error; + } + + error = bus_dmamem_map(sc->sc_dmat, &sc->jme_rdata.jme_tx_ring_seg, + nsegs, JME_TX_RING_SIZE, (caddr_t *)&sc->jme_rdata.jme_tx_ring, + BUS_DMA_NOWAIT); + if (error) + return (ENOBUFS); + + /* Load the DMA map for Tx ring. */ + error = bus_dmamap_load(sc->sc_dmat, + sc->jme_cdata.jme_tx_ring_map, sc->jme_rdata.jme_tx_ring, + JME_TX_RING_SIZE, NULL, BUS_DMA_NOWAIT); + if (error) { + printf("%s: could not load DMA'able memory for Tx ring.\n", + sc->sc_dev.dv_xname); + bus_dmamem_free(sc->sc_dmat, + (bus_dma_segment_t *)&sc->jme_rdata.jme_tx_ring, 1); + return error; + } + sc->jme_rdata.jme_tx_ring_paddr = + sc->jme_cdata.jme_tx_ring_map->dm_segs[0].ds_addr; + + /* + * Create DMA stuffs for RX ring + */ + + error = bus_dmamap_create(sc->sc_dmat, JME_RX_RING_SIZE, 1, + JME_RX_RING_SIZE, 0, BUS_DMA_NOWAIT, + &sc->jme_cdata.jme_rx_ring_map); + if (error) + return (ENOBUFS); + + /* Allocate DMA'able memory for RX ring */ + error = bus_dmamem_alloc(sc->sc_dmat, JME_RX_RING_SIZE, ETHER_ALIGN, 0, + &sc->jme_rdata.jme_rx_ring_seg, 1, &nsegs, + BUS_DMA_WAITOK); +/* XXX zero */ + if (error) { + printf("%s: could not allocate DMA'able memory for Rx ring.\n", + sc->sc_dev.dv_xname); + return error; + } + + error = bus_dmamem_map(sc->sc_dmat, &sc->jme_rdata.jme_rx_ring_seg, + nsegs, JME_RX_RING_SIZE, (caddr_t *)&sc->jme_rdata.jme_rx_ring, + BUS_DMA_NOWAIT); + if (error) + return (ENOBUFS); + + bzero(sc->jme_rdata.jme_rx_ring, JME_RX_RING_SIZE); + + /* Load the DMA map for Rx ring. */ + error = bus_dmamap_load(sc->sc_dmat, + sc->jme_cdata.jme_rx_ring_map, sc->jme_rdata.jme_rx_ring, + JME_RX_RING_SIZE, NULL, BUS_DMA_NOWAIT); + if (error) { + printf("%s: could not load DMA'able memory for Rx ring.\n", + sc->sc_dev.dv_xname); + bus_dmamem_free(sc->sc_dmat, + (bus_dma_segment_t *)sc->jme_rdata.jme_rx_ring, 1); + return error; + } + sc->jme_rdata.jme_rx_ring_paddr = + sc->jme_cdata.jme_rx_ring_map->dm_segs[0].ds_addr; + +#if 0 + /* Tx/Rx descriptor queue should reside within 4GB boundary. */ + tx_ring_end = sc->jme_rdata.jme_tx_ring_paddr + JME_TX_RING_SIZE; + rx_ring_end = sc->jme_rdata.jme_rx_ring_paddr + JME_RX_RING_SIZE; + if ((JME_ADDR_HI(tx_ring_end) != + JME_ADDR_HI(sc->jme_rdata.jme_tx_ring_paddr)) || + (JME_ADDR_HI(rx_ring_end) != + JME_ADDR_HI(sc->jme_rdata.jme_rx_ring_paddr))) { + printf("%s: 4GB boundary crossed, switching to 32bit " + "DMA address mode.\n", sc->sc_dev.dv_xname); + jme_dma_free(sc); + /* Limit DMA address space to 32bit and try again. */ + lowaddr = BUS_SPACE_MAXADDR_32BIT; + goto again; + } +#endif + + /* + * Create DMA stuffs for shadow status block + */ + + error = bus_dmamap_create(sc->sc_dmat, JME_SSB_SIZE, 1, + JME_SSB_SIZE, 0, BUS_DMA_NOWAIT, &sc->jme_cdata.jme_ssb_map); + if (error) + return (ENOBUFS); + + /* Allocate DMA'able memory for shared status block. */ + error = bus_dmamem_alloc(sc->sc_dmat, JME_SSB_SIZE, 1, 0, + &sc->jme_rdata.jme_ssb_block_seg, 1, &nsegs, BUS_DMA_WAITOK); + if (error) { + printf("%s: could not allocate DMA'able " + "memory for shared status block.\n", sc->sc_dev.dv_xname); + return error; + } + + error = bus_dmamem_map(sc->sc_dmat, &sc->jme_rdata.jme_ssb_block_seg, + nsegs, JME_SSB_SIZE, (caddr_t *)&sc->jme_rdata.jme_ssb_block, + BUS_DMA_NOWAIT); + if (error) + return (ENOBUFS); + + /* Load the DMA map for shared status block */ + error = bus_dmamap_load(sc->sc_dmat, + sc->jme_cdata.jme_ssb_map, sc->jme_rdata.jme_ssb_block, + JME_SSB_SIZE, NULL, BUS_DMA_NOWAIT); + if (error) { + printf("%s: could not load DMA'able memory " + "for shared status block.\n", sc->sc_dev.dv_xname); + bus_dmamem_free(sc->sc_dmat, + (bus_dma_segment_t *)sc->jme_rdata.jme_ssb_block, 1); + return error; + } + sc->jme_rdata.jme_ssb_block_paddr = + sc->jme_cdata.jme_ssb_map->dm_segs[0].ds_addr; + + /* + * Create DMA stuffs for TX buffers + */ + + /* Create DMA maps for Tx buffers. */ + for (i = 0; i < JME_TX_RING_CNT; i++) { + txd = &sc->jme_cdata.jme_txdesc[i]; + error = bus_dmamap_create(sc->sc_dmat, JME_TSO_MAXSIZE, + JME_MAXTXSEGS, JME_TSO_MAXSEGSIZE, 0, BUS_DMA_NOWAIT, + &txd->tx_dmamap); + if (error) { + int j; + + printf("%s: could not create %dth Tx dmamap.\n", + sc->sc_dev.dv_xname, i); + + for (j = 0; j < i; ++j) { + txd = &sc->jme_cdata.jme_txdesc[j]; + bus_dmamap_destroy(sc->sc_dmat, txd->tx_dmamap); + } + sc->jme_cdata.jme_tx_tag = NULL; + return error; + } + + } + + /* + * Create DMA stuffs for RX buffers + */ + + /* Create DMA maps for Rx buffers. */ + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, + 0, BUS_DMA_NOWAIT, &sc->jme_cdata.jme_rx_sparemap); + if (error) { + printf("%s: could not create spare Rx dmamap.\n", + sc->sc_dev.dv_xname); + return error; + } + for (i = 0; i < JME_RX_RING_CNT; i++) { + rxd = &sc->jme_cdata.jme_rxdesc[i]; + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, + 0, BUS_DMA_NOWAIT, &rxd->rx_dmamap); + if (error) { + int j; + + printf("%s: could not create %dth Rx dmamap.\n", + sc->sc_dev.dv_xname, i); + + for (j = 0; j < i; ++j) { + rxd = &sc->jme_cdata.jme_rxdesc[j]; + bus_dmamap_destroy(sc->sc_dmat, rxd->rx_dmamap); + } + bus_dmamap_destroy(sc->sc_dmat, + sc->jme_cdata.jme_rx_sparemap); + sc->jme_cdata.jme_rx_tag = NULL; + return error; + } + } + + return 0; +} + +void +jme_dma_free(struct jme_softc *sc) +{ + struct jme_txdesc *txd; + struct jme_rxdesc *rxd; + int i; + + /* Tx ring */ + bus_dmamap_unload(sc->sc_dmat, + sc->jme_cdata.jme_tx_ring_map); + bus_dmamem_free(sc->sc_dmat, + (bus_dma_segment_t *)sc->jme_rdata.jme_tx_ring, 1); + + /* Rx ring */ + bus_dmamap_unload(sc->sc_dmat, + sc->jme_cdata.jme_rx_ring_map); + bus_dmamem_free(sc->sc_dmat, + (bus_dma_segment_t *)sc->jme_rdata.jme_rx_ring, 1); + + /* Tx buffers */ + for (i = 0; i < JME_TX_RING_CNT; i++) { + txd = &sc->jme_cdata.jme_txdesc[i]; + bus_dmamap_destroy(sc->sc_dmat, txd->tx_dmamap); + } + + /* Rx buffers */ + for (i = 0; i < JME_RX_RING_CNT; i++) { + rxd = &sc->jme_cdata.jme_rxdesc[i]; + bus_dmamap_destroy(sc->sc_dmat, rxd->rx_dmamap); + } + bus_dmamap_destroy(sc->sc_dmat, + sc->jme_cdata.jme_rx_sparemap); + + /* Shadow status block. */ + bus_dmamap_unload(sc->sc_dmat, + sc->jme_cdata.jme_ssb_map); + bus_dmamem_free(sc->sc_dmat, + (bus_dma_segment_t *)sc->jme_rdata.jme_ssb_block, 1); +} + +#ifdef notyet +/* + * Unlike other ethernet controllers, JMC250 requires + * explicit resetting link speed to 10/100Mbps as gigabit + * link will cunsume more power than 375mA. + * Note, we reset the link speed to 10/100Mbps with + * auto-negotiation but we don't know whether that operation + * would succeed or not as we have no control after powering + * off. If the renegotiation fail WOL may not work. Running + * at 1Gbps draws more power than 375mA at 3.3V which is + * specified in PCI specification and that would result in + * complete shutdowning power to ethernet controller. + * + * TODO + * Save current negotiated media speed/duplex/flow-control + * to softc and restore the same link again after resuming. + * PHY handling such as power down/resetting to 100Mbps + * may be better handled in suspend method in phy driver. + */ +void +jme_setlinkspeed(struct jme_softc *sc) +{ + struct mii_data *mii; + int aneg, i; + + JME_LOCK_ASSERT(sc); + + mii = &sc->sc_miibus; + mii_pollstat(mii); + aneg = 0; + if ((mii->mii_media_status & IFM_AVALID) != 0) { + switch IFM_SUBTYPE(mii->mii_media_active) { + case IFM_10_T: + case IFM_100_TX: + return; + case IFM_1000_T: + aneg++; + default: + break; + } + } + jme_miibus_writereg(&sc->sc_dev, sc->jme_phyaddr, MII_100T2CR, 0); + jme_miibus_writereg(&sc->sc_dev, sc->jme_phyaddr, MII_ANAR, + ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA); + jme_miibus_writereg(&sc->sc_dev, sc->jme_phyaddr, MII_BMCR, + BMCR_AUTOEN | BMCR_STARTNEG); + DELAY(1000); + if (aneg != 0) { + /* Poll link state until jme(4) get a 10/100 link. */ + for (i = 0; i < MII_ANEGTICKS_GIGE; i++) { + mii_pollstat(mii); + if ((mii->mii_media_status & IFM_AVALID) != 0) { + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + case IFM_100_TX: + jme_mac_config(sc); + return; + default: + break; + } + } + JME_UNLOCK(sc); + pause("jmelnk", hz); + JME_LOCK(sc); + } + if (i == MII_ANEGTICKS_GIGE) + printf("%s: establishing link failed, " + "WOL may not work!\n", sc->sc_dev.dv_xname); + } + /* + * No link, force MAC to have 100Mbps, full-duplex link. + * This is the last resort and may/may not work. + */ + mii->mii_media_status = IFM_AVALID | IFM_ACTIVE; + mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX; + jme_mac_config(sc); +} + +void +jme_setwol(struct jme_softc *sc) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + uint32_t gpr, pmcs; + uint16_t pmstat; + int pmc; + + if (pci_find_extcap(sc->sc_dev, PCIY_PMG, &pmc) != 0) { + /* No PME capability, PHY power down. */ + jme_miibus_writereg(&sc->sc_dev, sc->jme_phyaddr, + MII_BMCR, BMCR_PDOWN); + return; + } + + gpr = CSR_READ_4(sc, JME_GPREG0) & ~GPREG0_PME_ENB; + pmcs = CSR_READ_4(sc, JME_PMCS); + pmcs &= ~PMCS_WOL_ENB_MASK; + if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0) { + pmcs |= PMCS_MAGIC_FRAME | PMCS_MAGIC_FRAME_ENB; + /* Enable PME message. */ + gpr |= GPREG0_PME_ENB; + /* For gigabit controllers, reset link speed to 10/100. */ + if ((sc->jme_caps & JME_CAP_FASTETH) == 0) + jme_setlinkspeed(sc); + } + + CSR_WRITE_4(sc, JME_PMCS, pmcs); + CSR_WRITE_4(sc, JME_GPREG0, gpr); + + /* Request PME. */ + pmstat = pci_read_config(sc->sc_dev, pmc + PCIR_POWER_STATUS, 2); + pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + if ((ifp->if_capenable & IFCAP_WOL) != 0) + pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + pci_write_config(sc->sc_dev, pmc + PCIR_POWER_STATUS, pmstat, 2); + if ((ifp->if_capenable & IFCAP_WOL) == 0) { + /* No WOL, PHY power down. */ + jme_miibus_writereg(&sc->sc_dev, sc->jme_phyaddr, + MII_BMCR, BMCR_PDOWN); + } +} +#endif + +int +jme_encap(struct jme_softc *sc, struct mbuf **m_head) +{ + struct jme_txdesc *txd; + struct jme_desc *desc; + struct mbuf *m; + int maxsegs; + int error, i, prod; + uint32_t cflags; + + prod = sc->jme_cdata.jme_tx_prod; + txd = &sc->jme_cdata.jme_txdesc[prod]; + + maxsegs = (JME_TX_RING_CNT - sc->jme_cdata.jme_tx_cnt) - + (JME_TXD_RSVD + 1); + if (maxsegs > JME_MAXTXSEGS) + maxsegs = JME_MAXTXSEGS; + if (maxsegs < (sc->jme_txd_spare - 1)) + panic("%s: not enough segments %d\n", sc->sc_dev.dv_xname, + maxsegs); + + error = bus_dmamap_load_mbuf(sc->sc_dmat, txd->tx_dmamap, + *m_head, BUS_DMA_NOWAIT); + if (error != 0) { + bus_dmamap_unload(sc->sc_dmat, txd->tx_dmamap); + error = EFBIG; + } + if (error == EFBIG) { + error = 0; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("%s: can't defrag TX mbuf\n", + sc->sc_dev.dv_xname); + m_freem(*m_head); + *m_head = NULL; + return (ENOBUFS); + } + + M_DUP_PKTHDR(m, *m_head); + if ((*m_head)->m_pkthdr.len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(*m_head); + m_freem(m); + *m_head = NULL; + return (ENOBUFS); + } + } + + m_copydata(*m_head, 0, (*m_head)->m_pkthdr.len, mtod(m, caddr_t)); + m_freem(*m_head); + m->m_len = m->m_pkthdr.len; + *m_head = m; + + error = bus_dmamap_load_mbuf(sc->sc_dmat, + txd->tx_dmamap, *m_head, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load defragged TX mbuf\n", + sc->sc_dev.dv_xname); + if (!error) { + bus_dmamap_unload(sc->sc_dmat, + txd->tx_dmamap); + error = EFBIG; + } + m_freem(*m_head); + *m_head = NULL; + return (error); + } + } else if (error) { + printf("%s: could not load TX mbuf\n", sc->sc_dev.dv_xname); + return (error); + } + + m = *m_head; + cflags = 0; + +#if 0 + /* Configure checksum offload. */ + if (m->m_pkthdr.csum_flags & CSUM_IP) + cflags |= JME_TD_IPCSUM; + if (m->m_pkthdr.csum_flags & CSUM_TCP) + cflags |= JME_TD_TCPCSUM; + if (m->m_pkthdr.csum_flags & CSUM_UDP) + cflags |= JME_TD_UDPCSUM; + + /* Configure VLAN. */ + if (m->m_flags & M_VLANTAG) { + cflags |= (m->m_pkthdr.ether_vlantag & JME_TD_VLAN_MASK); + cflags |= JME_TD_VLAN_TAG; + } +#endif + + desc = &sc->jme_rdata.jme_tx_ring[prod]; + desc->flags = htole32(cflags); + desc->buflen = 0; + desc->addr_hi = htole32(m->m_pkthdr.len); + desc->addr_lo = 0; + sc->jme_cdata.jme_tx_cnt++; + KASSERT(sc->jme_cdata.jme_tx_cnt < JME_TX_RING_CNT - JME_TXD_RSVD); + JME_DESC_INC(prod, JME_TX_RING_CNT); + for (i = 0; i < txd->tx_dmamap->dm_nsegs; i++) { + desc = &sc->jme_rdata.jme_tx_ring[prod]; + desc->flags = htole32(JME_TD_OWN | JME_TD_64BIT); + desc->buflen = htole32(txd->tx_dmamap->dm_segs[i].ds_len); + desc->addr_hi = + htole32(JME_ADDR_HI(txd->tx_dmamap->dm_segs[i].ds_addr)); + desc->addr_lo = + htole32(JME_ADDR_LO(txd->tx_dmamap->dm_segs[i].ds_addr)); + + sc->jme_cdata.jme_tx_cnt++; + KASSERT(sc->jme_cdata.jme_tx_cnt <= + JME_TX_RING_CNT - JME_TXD_RSVD); + JME_DESC_INC(prod, JME_TX_RING_CNT); + } + + /* Update producer index. */ + sc->jme_cdata.jme_tx_prod = prod; + /* + * Finally request interrupt and give the first descriptor + * owenership to hardware. + */ + desc = txd->tx_desc; + desc->flags |= htole32(JME_TD_OWN | JME_TD_INTR); + + txd->tx_m = m; + txd->tx_ndesc = txd->tx_dmamap->dm_nsegs + 1; + + /* Sync descriptors. */ + bus_dmamap_sync(sc->sc_dmat, txd->tx_dmamap, 0, + txd->tx_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_tx_ring_map, 0, + sc->jme_cdata.jme_tx_ring_map->dm_mapsize, BUS_DMASYNC_PREWRITE); + + return (0); +} + +void +jme_start(struct ifnet *ifp) +{ + struct jme_softc *sc = ifp->if_softc; + struct mbuf *m_head; + int enq = 0; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + if (sc->jme_cdata.jme_tx_cnt >= JME_TX_DESC_HIWAT) + jme_txeof(sc); + + for (;;) { + /* + * Check number of available TX descs, always + * leave JME_TXD_RSVD free TX descs. + */ + if (sc->jme_cdata.jme_tx_cnt + sc->jme_txd_spare > + JME_TX_RING_CNT - JME_TXD_RSVD) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT); +#endif + + /* + * Pack the data into the transmit ring. If we + * don't have room, set the OACTIVE flag and wait + * for the NIC to drain the ring. + */ + if (jme_encap(sc, &m_head)) { + if (m_head == NULL) { + ifp->if_oerrors++; + break; + } +// ifq_prepend(&ifp->if_snd, m_head); + ifp->if_flags |= IFF_OACTIVE; + break; + } + enq++; + + /* + * If there's a BPF listener, bounce a copy of this frame + * to him. + */ +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT); +#endif + } + + if (enq > 0) { + /* + * Reading TXCSR takes very long time under heavy load + * so cache TXCSR value and writes the ORed value with + * the kick command to the TXCSR. This saves one register + * access cycle. + */ + CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr | TXCSR_TX_ENB | + TXCSR_TXQ_N_START(TXCSR_TXQ0)); + /* Set a timeout in case the chip goes out to lunch. */ + ifp->if_timer = JME_TX_TIMEOUT; + } +} + +void +jme_watchdog(struct ifnet *ifp) +{ + struct jme_softc *sc = ifp->if_softc; + + if ((sc->jme_flags & JME_FLAG_LINK) == 0) { + printf("%s: watchdog timeout (missed link)\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; + jme_init(ifp); + return; + } + + jme_txeof(sc); + if (sc->jme_cdata.jme_tx_cnt == 0) { + printf("%s: watchdog timeout (missed Tx interrupts) " + "-- recovering\n", sc->sc_dev.dv_xname); + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + jme_start(ifp); + return; + } + + printf("%s: watchdog timeout\n", sc->sc_dev.dv_xname); + ifp->if_oerrors++; + jme_init(ifp); + + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + jme_start(ifp); +} + +int +jme_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct jme_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_miibus; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int error = 0, s; + + s = splnet(); + + if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { + splx(s); + return error; + } + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + if (!(ifp->if_flags & IFF_RUNNING)) + jme_init(ifp); +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&sc->sc_arpcom, ifa); +#endif + break; + case SIOCSIFMTU: +#if 0 + if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > JME_JUMBO_MTU || + (!(sc->jme_caps & JME_CAP_JUMBO) && + ifr->ifr_mtu > JME_MAX_MTU)) { + error = EINVAL; + break; + } + + if (ifp->if_mtu != ifr->ifr_mtu) { + /* + * No special configuration is required when interface + * MTU is changed but availability of Tx checksum + * offload should be chcked against new MTU size as + * FIFO size is just 2K. + */ + if (ifr->ifr_mtu >= JME_TX_FIFO_SIZE) { + ifp->if_capenable &= ~IFCAP_TXCSUM; + ifp->if_hwassist &= ~JME_CSUM_FEATURES; + } + ifp->if_mtu = ifr->ifr_mtu; + if (ifp->if_flags & IFF_RUNNING) + jme_init(ifp); + } +#endif + if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > ifp->if_hardmtu) + error = EINVAL; + else if (ifp->if_mtu != ifr->ifr_mtu) + ifp->if_mtu = ifr->ifr_mtu; + break; + + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) { + if ((ifp->if_flags ^ sc->jme_if_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) + jme_set_filter(sc); + } else { + if (!(ifp->if_flags & IFF_RUNNING)) + jme_init(ifp); + } + } else { + if (ifp->if_flags & IFF_RUNNING) + jme_stop(sc); + } + sc->jme_if_flags = ifp->if_flags; + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_arpcom) : + ether_delmulti(ifr, &sc->sc_arpcom); + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + jme_set_filter(sc); + error = 0; + } + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + default: + error = ENOTTY; + break; + } + + splx(s); + return (error); +} + +void +jme_mac_config(struct jme_softc *sc) +{ + struct mii_data *mii; + uint32_t ghc, rxmac, txmac, txpause; + int phyconf = JMPHY_CONF_DEFFIFO; + + mii = &sc->sc_miibus; + + CSR_WRITE_4(sc, JME_GHC, GHC_RESET); + DELAY(10); + CSR_WRITE_4(sc, JME_GHC, 0); + ghc = 0; + rxmac = CSR_READ_4(sc, JME_RXMAC); + rxmac &= ~RXMAC_FC_ENB; + txmac = CSR_READ_4(sc, JME_TXMAC); + txmac &= ~(TXMAC_CARRIER_EXT | TXMAC_FRAME_BURST); + txpause = CSR_READ_4(sc, JME_TXPFC); + txpause &= ~TXPFC_PAUSE_ENB; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { + ghc |= GHC_FULL_DUPLEX; + rxmac &= ~RXMAC_COLL_DET_ENB; + txmac &= ~(TXMAC_COLL_ENB | TXMAC_CARRIER_SENSE | + TXMAC_BACKOFF | TXMAC_CARRIER_EXT | + TXMAC_FRAME_BURST); +#ifdef notyet + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) + txpause |= TXPFC_PAUSE_ENB; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) + rxmac |= RXMAC_FC_ENB; +#endif + /* Disable retry transmit timer/retry limit. */ + CSR_WRITE_4(sc, JME_TXTRHD, CSR_READ_4(sc, JME_TXTRHD) & + ~(TXTRHD_RT_PERIOD_ENB | TXTRHD_RT_LIMIT_ENB)); + } else { + rxmac |= RXMAC_COLL_DET_ENB; + txmac |= TXMAC_COLL_ENB | TXMAC_CARRIER_SENSE | TXMAC_BACKOFF; + /* Enable retry transmit timer/retry limit. */ + CSR_WRITE_4(sc, JME_TXTRHD, CSR_READ_4(sc, JME_TXTRHD) | + TXTRHD_RT_PERIOD_ENB | TXTRHD_RT_LIMIT_ENB); + } + + /* Reprogram Tx/Rx MACs with resolved speed/duplex. */ + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_10_T: + ghc |= GHC_SPEED_10; + break; + + case IFM_100_TX: + ghc |= GHC_SPEED_100; + + /* + * Use extended FIFO depth to workaround CRC errors + * emitted by chips before JMC250B + */ + phyconf = JMPHY_CONF_EXTFIFO; + break; + + case IFM_1000_T: + if (sc->jme_caps & JME_CAP_FASTETH) + break; + + ghc |= GHC_SPEED_1000; + if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) == 0) + txmac |= TXMAC_CARRIER_EXT | TXMAC_FRAME_BURST; + break; + + default: + break; + } + CSR_WRITE_4(sc, JME_GHC, ghc); + CSR_WRITE_4(sc, JME_RXMAC, rxmac); + CSR_WRITE_4(sc, JME_TXMAC, txmac); + CSR_WRITE_4(sc, JME_TXPFC, txpause); + + if (sc->jme_caps & JME_CAP_EXTFIFO) { + jme_miibus_writereg(&sc->sc_dev, sc->jme_phyaddr, + JMPHY_CONF, phyconf); + } +} + +int +jme_intr(void *xsc) +{ + struct jme_softc *sc = xsc; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + uint32_t status; + int claimed = 0; + + status = CSR_READ_4(sc, JME_INTR_REQ_STATUS); + if (status == 0 || status == 0xFFFFFFFF) + return (0); + + /* Disable interrupts. */ + CSR_WRITE_4(sc, JME_INTR_MASK_CLR, JME_INTRS); + + status = CSR_READ_4(sc, JME_INTR_STATUS); + if ((status & JME_INTRS) == 0 || status == 0xFFFFFFFF) + goto back; + + /* Reset PCC counter/timer and Ack interrupts. */ + status &= ~(INTR_TXQ_COMP | INTR_RXQ_COMP); + if (status & (INTR_TXQ_COAL | INTR_TXQ_COAL_TO)) + status |= INTR_TXQ_COAL | INTR_TXQ_COAL_TO | INTR_TXQ_COMP; + if (status & (INTR_RXQ_COAL | INTR_RXQ_COAL_TO)) + status |= INTR_RXQ_COAL | INTR_RXQ_COAL_TO | INTR_RXQ_COMP; + CSR_WRITE_4(sc, JME_INTR_STATUS, status); + + if (ifp->if_flags & IFF_RUNNING) { + if (status & (INTR_RXQ_COAL | INTR_RXQ_COAL_TO)) + jme_rxeof(sc); + + if (status & INTR_RXQ_DESC_EMPTY) { + /* + * Notify hardware availability of new Rx buffers. + * Reading RXCSR takes very long time under heavy + * load so cache RXCSR value and writes the ORed + * value with the kick command to the RXCSR. This + * saves one register access cycle. + */ + CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr | + RXCSR_RX_ENB | RXCSR_RXQ_START); + } + + if (status & (INTR_TXQ_COAL | INTR_TXQ_COAL_TO)) { + jme_txeof(sc); + if (!IFQ_IS_EMPTY(&ifp->if_snd)) + jme_start(ifp); + } + } + claimed = 1; +back: + /* Reenable interrupts. */ + CSR_WRITE_4(sc, JME_INTR_MASK_SET, JME_INTRS); + + return (claimed); +} + +void +jme_txeof(struct jme_softc *sc) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct jme_txdesc *txd; + uint32_t status; + int cons, nsegs; + + cons = sc->jme_cdata.jme_tx_cons; + if (cons == sc->jme_cdata.jme_tx_prod) + return; + + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_tx_ring_map, 0, + sc->jme_cdata.jme_tx_ring_map->dm_mapsize, BUS_DMASYNC_POSTREAD); + + /* + * Go through our Tx list and free mbufs for those + * frames which have been transmitted. + */ + while (cons != sc->jme_cdata.jme_tx_prod) { + txd = &sc->jme_cdata.jme_txdesc[cons]; + + if (txd->tx_m == NULL) + panic("%s: freeing NULL mbuf!\n", sc->sc_dev.dv_xname); + + status = letoh32(txd->tx_desc->flags); + if ((status & JME_TD_OWN) == JME_TD_OWN) + break; + + if (status & (JME_TD_TMOUT | JME_TD_RETRY_EXP)) { + ifp->if_oerrors++; + } else { + ifp->if_opackets++; + if (status & JME_TD_COLLISION) { + ifp->if_collisions += + letoh32(txd->tx_desc->buflen) & + JME_TD_BUF_LEN_MASK; + } + } + + /* + * Only the first descriptor of multi-descriptor + * transmission is updated so driver have to skip entire + * chained buffers for the transmiited frame. In other + * words, JME_TD_OWN bit is valid only at the first + * descriptor of a multi-descriptor transmission. + */ + for (nsegs = 0; nsegs < txd->tx_ndesc; nsegs++) { + sc->jme_rdata.jme_tx_ring[cons].flags = 0; + JME_DESC_INC(cons, JME_TX_RING_CNT); + } + + /* Reclaim transferred mbufs. */ + bus_dmamap_unload(sc->sc_dmat, txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; + sc->jme_cdata.jme_tx_cnt -= txd->tx_ndesc; + if (sc->jme_cdata.jme_tx_cnt < 0) + panic("%s: Active Tx desc counter was garbled\n", + sc->sc_dev.dv_xname); + txd->tx_ndesc = 0; + } + sc->jme_cdata.jme_tx_cons = cons; + + if (sc->jme_cdata.jme_tx_cnt == 0) + ifp->if_timer = 0; + + if (sc->jme_cdata.jme_tx_cnt + sc->jme_txd_spare <= + JME_TX_RING_CNT - JME_TXD_RSVD) + ifp->if_flags &= ~IFF_OACTIVE; + + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_tx_ring_map, 0, + sc->jme_cdata.jme_tx_ring_map->dm_mapsize, BUS_DMASYNC_PREWRITE); +} + +void +jme_discard_rxbufs(struct jme_softc *sc, int cons, int count) +{ + int i; + + for (i = 0; i < count; ++i) { + struct jme_desc *desc = &sc->jme_rdata.jme_rx_ring[cons]; + + desc->flags = htole32(JME_RD_OWN | JME_RD_INTR | JME_RD_64BIT); + desc->buflen = htole32(MCLBYTES); + JME_DESC_INC(cons, JME_RX_RING_CNT); + } +} + +/* Receive a frame. */ +void +jme_rxpkt(struct jme_softc *sc) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct jme_desc *desc; + struct jme_rxdesc *rxd; + struct mbuf *mp, *m; + uint32_t flags, status; + int cons, count, nsegs; + + cons = sc->jme_cdata.jme_rx_cons; + desc = &sc->jme_rdata.jme_rx_ring[cons]; + flags = letoh32(desc->flags); + status = letoh32(desc->buflen); + nsegs = JME_RX_NSEGS(status); + + if (status & JME_RX_ERR_STAT) { + ifp->if_ierrors++; + jme_discard_rxbufs(sc, cons, nsegs); +#ifdef JME_SHOW_ERRORS + printf("%s : receive error = 0x%b\n", + sc->sc_dev.dv_xname, JME_RX_ERR(status), JME_RX_ERR_BITS); +#endif + sc->jme_cdata.jme_rx_cons += nsegs; + sc->jme_cdata.jme_rx_cons %= JME_RX_RING_CNT; + return; + } + + sc->jme_cdata.jme_rxlen = JME_RX_BYTES(status) - JME_RX_PAD_BYTES; + for (count = 0; count < nsegs; count++, + JME_DESC_INC(cons, JME_RX_RING_CNT)) { + rxd = &sc->jme_cdata.jme_rxdesc[cons]; + mp = rxd->rx_m; + + /* Add a new receive buffer to the ring. */ + if (jme_newbuf(sc, rxd, 0) != 0) { + ifp->if_iqdrops++; + /* Reuse buffer. */ + jme_discard_rxbufs(sc, cons, nsegs - count); + if (sc->jme_cdata.jme_rxhead != NULL) { + m_freem(sc->jme_cdata.jme_rxhead); + JME_RXCHAIN_RESET(sc); + } + break; + } + + /* + * Assume we've received a full sized frame. + * Actual size is fixed when we encounter the end of + * multi-segmented frame. + */ + mp->m_len = MCLBYTES; + + /* Chain received mbufs. */ + if (sc->jme_cdata.jme_rxhead == NULL) { + sc->jme_cdata.jme_rxhead = mp; + sc->jme_cdata.jme_rxtail = mp; + } else { + /* + * Receive processor can receive a maximum frame + * size of 65535 bytes. + */ + mp->m_flags &= ~M_PKTHDR; + sc->jme_cdata.jme_rxtail->m_next = mp; + sc->jme_cdata.jme_rxtail = mp; + } + + if (count == nsegs - 1) { + /* Last desc. for this frame. */ + m = sc->jme_cdata.jme_rxhead; + /* XXX assert PKTHDR? */ + m->m_flags |= M_PKTHDR; + m->m_pkthdr.len = sc->jme_cdata.jme_rxlen; + if (nsegs > 1) { + /* Set first mbuf size. */ + m->m_len = MCLBYTES - JME_RX_PAD_BYTES; + /* Set last mbuf size. */ + mp->m_len = sc->jme_cdata.jme_rxlen - + ((MCLBYTES - JME_RX_PAD_BYTES) + + (MCLBYTES * (nsegs - 2))); + } else { + m->m_len = sc->jme_cdata.jme_rxlen; + } + m->m_pkthdr.rcvif = ifp; + + /* + * Account for 10bytes auto padding which is used + * to align IP header on 32bit boundary. Also note, + * CRC bytes is automatically removed by the + * hardware. + */ + m->m_data += JME_RX_PAD_BYTES; + +#if 0 + /* Set checksum information. */ + if ((ifp->if_capenable & IFCAP_RXCSUM) && + (flags & JME_RD_IPV4)) { + m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; + if (flags & JME_RD_IPCSUM) + m->m_pkthdr.csum_flags |= CSUM_IP_VALID; + if ((flags & JME_RD_MORE_FRAG) == 0 && + ((flags & (JME_RD_TCP | JME_RD_TCPCSUM)) == + (JME_RD_TCP | JME_RD_TCPCSUM) || + (flags & (JME_RD_UDP | JME_RD_UDPCSUM)) == + (JME_RD_UDP | JME_RD_UDPCSUM))) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } + } + + /* Check for VLAN tagged packets. */ + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) && + (flags & JME_RD_VLAN_TAG)) { + m->m_pkthdr.ether_vlantag = + flags & JME_RD_VLAN_MASK; + m->m_flags |= M_VLANTAG; + } +#endif + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); +#endif + + ifp->if_ipackets++; + /* Pass it on. */ + ether_input_mbuf(ifp, m); + + /* Reset mbuf chains. */ + JME_RXCHAIN_RESET(sc); + } + } + + sc->jme_cdata.jme_rx_cons += nsegs; + sc->jme_cdata.jme_rx_cons %= JME_RX_RING_CNT; +} + +void +jme_rxeof(struct jme_softc *sc) +{ + struct jme_desc *desc; + int nsegs, prog, pktlen; + + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_rx_ring_map, 0, + sc->jme_cdata.jme_rx_ring_map->dm_mapsize, BUS_DMASYNC_POSTREAD); + + prog = 0; + for (;;) { + desc = &sc->jme_rdata.jme_rx_ring[sc->jme_cdata.jme_rx_cons]; + if ((letoh32(desc->flags) & JME_RD_OWN) == JME_RD_OWN) + break; + if ((letoh32(desc->buflen) & JME_RD_VALID) == 0) + break; + + /* + * Check number of segments against received bytes. + * Non-matching value would indicate that hardware + * is still trying to update Rx descriptors. I'm not + * sure whether this check is needed. + */ + nsegs = JME_RX_NSEGS(letoh32(desc->buflen)); + pktlen = JME_RX_BYTES(letoh32(desc->buflen)); + if (nsegs != howmany(pktlen, MCLBYTES)) { + printf("%s: RX fragment count(%d) " + "and packet size(%d) mismach\n", + sc->sc_dev.dv_xname, nsegs, pktlen); + break; + } + + /* Received a frame. */ + jme_rxpkt(sc); + prog++; + } + + if (prog > 0) { + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_rx_ring_map, 0, + sc->jme_cdata.jme_rx_ring_map->dm_mapsize, BUS_DMASYNC_PREWRITE); + } +} + +void +jme_tick(void *xsc) +{ + struct jme_softc *sc = xsc; + struct mii_data *mii = &sc->sc_miibus; + int s; + + s = splnet(); + mii_tick(mii); + timeout_add_sec(&sc->jme_tick_ch, 1); + splx(s); +} + +void +jme_reset(struct jme_softc *sc) +{ +#ifdef foo + /* Stop receiver, transmitter. */ + jme_stop_rx(sc); + jme_stop_tx(sc); +#endif + CSR_WRITE_4(sc, JME_GHC, GHC_RESET); + DELAY(10); + CSR_WRITE_4(sc, JME_GHC, 0); +} + +int +jme_init(struct ifnet *ifp) +{ + struct jme_softc *sc = ifp->if_softc; + struct mii_data *mii; + uint8_t eaddr[ETHER_ADDR_LEN]; + bus_addr_t paddr; + uint32_t reg; + int error; + + /* + * Cancel any pending I/O. + */ + jme_stop(sc); + + /* + * Reset the chip to a known state. + */ + jme_reset(sc); + + /* + * Since we always use 64bit address mode for transmitting, + * each Tx request requires one more dummy descriptor. + */ + sc->jme_txd_spare = + howmany(ifp->if_mtu + sizeof(struct ether_vlan_header), MCLBYTES) + 1; + KASSERT(sc->jme_txd_spare >= 2); + + /* Init descriptors. */ + error = jme_init_rx_ring(sc); + if (error != 0) { + printf("%s: initialization failed: no memory for Rx buffers.\n", + sc->sc_dev.dv_xname); + jme_stop(sc); + return (1); + } + jme_init_tx_ring(sc); + + /* Initialize shadow status block. */ + jme_init_ssb(sc); + + /* Reprogram the station address. */ + bcopy(LLADDR(ifp->if_sadl), eaddr, ETHER_ADDR_LEN); + CSR_WRITE_4(sc, JME_PAR0, + eaddr[3] << 24 | eaddr[2] << 16 | eaddr[1] << 8 | eaddr[0]); + CSR_WRITE_4(sc, JME_PAR1, eaddr[5] << 8 | eaddr[4]); + + /* + * Configure Tx queue. + * Tx priority queue weight value : 0 + * Tx FIFO threshold for processing next packet : 16QW + * Maximum Tx DMA length : 512 + * Allow Tx DMA burst. + */ + sc->jme_txcsr = TXCSR_TXQ_N_SEL(TXCSR_TXQ0); + sc->jme_txcsr |= TXCSR_TXQ_WEIGHT(TXCSR_TXQ_WEIGHT_MIN); + sc->jme_txcsr |= TXCSR_FIFO_THRESH_16QW; + sc->jme_txcsr |= sc->jme_tx_dma_size; + sc->jme_txcsr |= TXCSR_DMA_BURST; + CSR_WRITE_4(sc, JME_TXCSR, sc->jme_txcsr); + + /* Set Tx descriptor counter. */ + CSR_WRITE_4(sc, JME_TXQDC, JME_TX_RING_CNT); + + /* Set Tx ring address to the hardware. */ + paddr = JME_TX_RING_ADDR(sc, 0); + CSR_WRITE_4(sc, JME_TXDBA_HI, JME_ADDR_HI(paddr)); + CSR_WRITE_4(sc, JME_TXDBA_LO, JME_ADDR_LO(paddr)); + + /* Configure TxMAC parameters. */ + reg = TXMAC_IFG1_DEFAULT | TXMAC_IFG2_DEFAULT | TXMAC_IFG_ENB; + reg |= TXMAC_THRESH_1_PKT; + reg |= TXMAC_CRC_ENB | TXMAC_PAD_ENB; + CSR_WRITE_4(sc, JME_TXMAC, reg); + + /* + * Configure Rx queue. + * FIFO full threshold for transmitting Tx pause packet : 128T + * FIFO threshold for processing next packet : 128QW + * Rx queue 0 select + * Max Rx DMA length : 128 + * Rx descriptor retry : 32 + * Rx descriptor retry time gap : 256ns + * Don't receive runt/bad frame. + */ + sc->jme_rxcsr = RXCSR_FIFO_FTHRESH_128T; + /* + * Since Rx FIFO size is 4K bytes, receiving frames larger + * than 4K bytes will suffer from Rx FIFO overruns. So + * decrease FIFO threshold to reduce the FIFO overruns for + * frames larger than 4000 bytes. + * For best performance of standard MTU sized frames use + * maximum allowable FIFO threshold, 128QW. + */ + if ((ifp->if_mtu + ETHER_HDR_LEN + EVL_ENCAPLEN + ETHER_CRC_LEN) > + JME_RX_FIFO_SIZE) + sc->jme_rxcsr |= RXCSR_FIFO_THRESH_16QW; + else + sc->jme_rxcsr |= RXCSR_FIFO_THRESH_128QW; + sc->jme_rxcsr |= sc->jme_rx_dma_size | RXCSR_RXQ_N_SEL(RXCSR_RXQ0); + sc->jme_rxcsr |= RXCSR_DESC_RT_CNT(RXCSR_DESC_RT_CNT_DEFAULT); + sc->jme_rxcsr |= RXCSR_DESC_RT_GAP_256 & RXCSR_DESC_RT_GAP_MASK; + /* XXX TODO DROP_BAD */ + CSR_WRITE_4(sc, JME_RXCSR, sc->jme_rxcsr); + + /* Set Rx descriptor counter. */ + CSR_WRITE_4(sc, JME_RXQDC, JME_RX_RING_CNT); + + /* Set Rx ring address to the hardware. */ + paddr = JME_RX_RING_ADDR(sc, 0); + CSR_WRITE_4(sc, JME_RXDBA_HI, JME_ADDR_HI(paddr)); + CSR_WRITE_4(sc, JME_RXDBA_LO, JME_ADDR_LO(paddr)); + + /* Clear receive filter. */ + CSR_WRITE_4(sc, JME_RXMAC, 0); + + /* Set up the receive filter. */ + jme_set_filter(sc); + jme_set_vlan(sc); + + /* + * Disable all WOL bits as WOL can interfere normal Rx + * operation. Also clear WOL detection status bits. + */ + reg = CSR_READ_4(sc, JME_PMCS); + reg &= ~PMCS_WOL_ENB_MASK; + CSR_WRITE_4(sc, JME_PMCS, reg); + + /* + * Pad 10bytes right before received frame. This will greatly + * help Rx performance on strict-alignment architectures as + * it does not need to copy the frame to align the payload. + */ + reg = CSR_READ_4(sc, JME_RXMAC); + reg |= RXMAC_PAD_10BYTES; + +#if 0 + if (ifp->if_capenable & IFCAP_RXCSUM) + reg |= RXMAC_CSUM_ENB; +#endif + CSR_WRITE_4(sc, JME_RXMAC, reg); + + /* Configure general purpose reg0 */ + reg = CSR_READ_4(sc, JME_GPREG0); + reg &= ~GPREG0_PCC_UNIT_MASK; + /* Set PCC timer resolution to micro-seconds unit. */ + reg |= GPREG0_PCC_UNIT_US; + /* + * Disable all shadow register posting as we have to read + * JME_INTR_STATUS register in jme_intr. Also it seems + * that it's hard to synchronize interrupt status between + * hardware and software with shadow posting due to + * requirements of bus_dmamap_sync(9). + */ + reg |= GPREG0_SH_POST_DW7_DIS | GPREG0_SH_POST_DW6_DIS | + GPREG0_SH_POST_DW5_DIS | GPREG0_SH_POST_DW4_DIS | + GPREG0_SH_POST_DW3_DIS | GPREG0_SH_POST_DW2_DIS | + GPREG0_SH_POST_DW1_DIS | GPREG0_SH_POST_DW0_DIS; + /* Disable posting of DW0. */ + reg &= ~GPREG0_POST_DW0_ENB; + /* Clear PME message. */ + reg &= ~GPREG0_PME_ENB; + /* Set PHY address. */ + reg &= ~GPREG0_PHY_ADDR_MASK; + reg |= sc->jme_phyaddr; + CSR_WRITE_4(sc, JME_GPREG0, reg); + + /* Configure Tx queue 0 packet completion coalescing. */ + sc->jme_tx_coal_to = PCCTX_COAL_TO_DEFAULT; + reg = (sc->jme_tx_coal_to << PCCTX_COAL_TO_SHIFT) & + PCCTX_COAL_TO_MASK; + sc->jme_tx_coal_pkt = PCCTX_COAL_PKT_DEFAULT; + reg |= (sc->jme_tx_coal_pkt << PCCTX_COAL_PKT_SHIFT) & + PCCTX_COAL_PKT_MASK; + reg |= PCCTX_COAL_TXQ0; + CSR_WRITE_4(sc, JME_PCCTX, reg); + + /* Configure Rx queue 0 packet completion coalescing. */ + sc->jme_rx_coal_to = PCCRX_COAL_TO_DEFAULT; + reg = (sc->jme_rx_coal_to << PCCRX_COAL_TO_SHIFT) & + PCCRX_COAL_TO_MASK; + sc->jme_rx_coal_pkt = PCCRX_COAL_PKT_DEFAULT; + reg |= (sc->jme_rx_coal_pkt << PCCRX_COAL_PKT_SHIFT) & + PCCRX_COAL_PKT_MASK; + CSR_WRITE_4(sc, JME_PCCRX0, reg); + + /* Configure shadow status block but don't enable posting. */ + paddr = sc->jme_rdata.jme_ssb_block_paddr; + CSR_WRITE_4(sc, JME_SHBASE_ADDR_HI, JME_ADDR_HI(paddr)); + CSR_WRITE_4(sc, JME_SHBASE_ADDR_LO, JME_ADDR_LO(paddr)); + + /* Disable Timer 1 and Timer 2. */ + CSR_WRITE_4(sc, JME_TIMER1, 0); + CSR_WRITE_4(sc, JME_TIMER2, 0); + + /* Configure retry transmit period, retry limit value. */ + CSR_WRITE_4(sc, JME_TXTRHD, + ((TXTRHD_RT_PERIOD_DEFAULT << TXTRHD_RT_PERIOD_SHIFT) & + TXTRHD_RT_PERIOD_MASK) | + ((TXTRHD_RT_LIMIT_DEFAULT << TXTRHD_RT_LIMIT_SHIFT) & + TXTRHD_RT_LIMIT_SHIFT)); + + /* Disable RSS. */ + CSR_WRITE_4(sc, JME_RSSC, RSSC_DIS_RSS); + + /* Initialize the interrupt mask. */ + CSR_WRITE_4(sc, JME_INTR_MASK_SET, JME_INTRS); + CSR_WRITE_4(sc, JME_INTR_STATUS, 0xFFFFFFFF); + + /* + * Enabling Tx/Rx DMA engines and Rx queue processing is + * done after detection of valid link in jme_miibus_statchg. + */ + sc->jme_flags &= ~JME_FLAG_LINK; + + /* Set the current media. */ + mii = &sc->sc_miibus; + mii_mediachg(mii); + + timeout_add_sec(&sc->jme_tick_ch, 1); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + return (0); +} + +void +jme_stop(struct jme_softc *sc) +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct jme_txdesc *txd; + struct jme_rxdesc *rxd; + int i; + + /* + * Mark the interface down and cancel the watchdog timer. + */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; + + timeout_del(&sc->jme_tick_ch); + sc->jme_flags &= ~JME_FLAG_LINK; + + /* + * Disable interrupts. + */ + CSR_WRITE_4(sc, JME_INTR_MASK_CLR, JME_INTRS); + CSR_WRITE_4(sc, JME_INTR_STATUS, 0xFFFFFFFF); + + /* Disable updating shadow status block. */ + CSR_WRITE_4(sc, JME_SHBASE_ADDR_LO, + CSR_READ_4(sc, JME_SHBASE_ADDR_LO) & ~SHBASE_POST_ENB); + + /* Stop receiver, transmitter. */ + jme_stop_rx(sc); + jme_stop_tx(sc); + +#ifdef foo + /* Reclaim Rx/Tx buffers that have been completed. */ + jme_rxeof(sc); + if (sc->jme_cdata.jme_rxhead != NULL) + m_freem(sc->jme_cdata.jme_rxhead); + JME_RXCHAIN_RESET(sc); + jme_txeof(sc); +#endif + + /* + * Free partial finished RX segments + */ + if (sc->jme_cdata.jme_rxhead != NULL) + m_freem(sc->jme_cdata.jme_rxhead); + JME_RXCHAIN_RESET(sc); + + /* + * Free RX and TX mbufs still in the queues. + */ + for (i = 0; i < JME_RX_RING_CNT; i++) { + rxd = &sc->jme_cdata.jme_rxdesc[i]; + if (rxd->rx_m != NULL) { + bus_dmamap_unload(sc->sc_dmat, rxd->rx_dmamap); + m_freem(rxd->rx_m); + rxd->rx_m = NULL; + } + } + for (i = 0; i < JME_TX_RING_CNT; i++) { + txd = &sc->jme_cdata.jme_txdesc[i]; + if (txd->tx_m != NULL) { + bus_dmamap_unload(sc->sc_dmat, txd->tx_dmamap); + m_freem(txd->tx_m); + txd->tx_m = NULL; + txd->tx_ndesc = 0; + } + } +} + +void +jme_stop_tx(struct jme_softc *sc) +{ + uint32_t reg; + int i; + + reg = CSR_READ_4(sc, JME_TXCSR); + if ((reg & TXCSR_TX_ENB) == 0) + return; + reg &= ~TXCSR_TX_ENB; + CSR_WRITE_4(sc, JME_TXCSR, reg); + for (i = JME_TIMEOUT; i > 0; i--) { + DELAY(1); + if ((CSR_READ_4(sc, JME_TXCSR) & TXCSR_TX_ENB) == 0) + break; + } + if (i == 0) + printf("%s: stopping transmitter timeout!\n", + sc->sc_dev.dv_xname); +} + +void +jme_stop_rx(struct jme_softc *sc) +{ + uint32_t reg; + int i; + + reg = CSR_READ_4(sc, JME_RXCSR); + if ((reg & RXCSR_RX_ENB) == 0) + return; + reg &= ~RXCSR_RX_ENB; + CSR_WRITE_4(sc, JME_RXCSR, reg); + for (i = JME_TIMEOUT; i > 0; i--) { + DELAY(1); + if ((CSR_READ_4(sc, JME_RXCSR) & RXCSR_RX_ENB) == 0) + break; + } + if (i == 0) + printf("%s: stopping recevier timeout!\n", sc->sc_dev.dv_xname); +} + +void +jme_init_tx_ring(struct jme_softc *sc) +{ + struct jme_ring_data *rd; + struct jme_txdesc *txd; + int i; + + sc->jme_cdata.jme_tx_prod = 0; + sc->jme_cdata.jme_tx_cons = 0; + sc->jme_cdata.jme_tx_cnt = 0; + + rd = &sc->jme_rdata; + bzero(rd->jme_tx_ring, JME_TX_RING_SIZE); + for (i = 0; i < JME_TX_RING_CNT; i++) { + txd = &sc->jme_cdata.jme_txdesc[i]; + txd->tx_m = NULL; + txd->tx_desc = &rd->jme_tx_ring[i]; + txd->tx_ndesc = 0; + } + + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_tx_ring_map, 0, + sc->jme_cdata.jme_tx_ring_map->dm_mapsize, BUS_DMASYNC_PREWRITE); +} + +void +jme_init_ssb(struct jme_softc *sc) +{ + struct jme_ring_data *rd; + + rd = &sc->jme_rdata; + bzero(rd->jme_ssb_block, JME_SSB_SIZE); + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_ssb_map, 0, + sc->jme_cdata.jme_ssb_map->dm_mapsize, BUS_DMASYNC_PREWRITE); +} + +int +jme_init_rx_ring(struct jme_softc *sc) +{ + struct jme_ring_data *rd; + struct jme_rxdesc *rxd; + int i; + + KASSERT(sc->jme_cdata.jme_rxhead == NULL && + sc->jme_cdata.jme_rxtail == NULL && + sc->jme_cdata.jme_rxlen == 0); + sc->jme_cdata.jme_rx_cons = 0; + + rd = &sc->jme_rdata; + bzero(rd->jme_rx_ring, JME_RX_RING_SIZE); + for (i = 0; i < JME_RX_RING_CNT; i++) { + int error; + + rxd = &sc->jme_cdata.jme_rxdesc[i]; + rxd->rx_m = NULL; + rxd->rx_desc = &rd->jme_rx_ring[i]; + error = jme_newbuf(sc, rxd, 1); + if (error) + return (error); + } + + bus_dmamap_sync(sc->sc_dmat, sc->jme_cdata.jme_rx_ring_map, 0, + sc->jme_cdata.jme_rx_ring_map->dm_mapsize, BUS_DMASYNC_PREWRITE); + + return (0); +} + +int +jme_newbuf(struct jme_softc *sc, struct jme_rxdesc *rxd, int init) +{ + struct jme_desc *desc; + struct mbuf *m; + bus_dmamap_t map; + int error; + + MGETHDR(m, init ? M_WAITOK : M_DONTWAIT, MT_DATA); + if (m == NULL) + return (ENOBUFS); + MCLGET(m, init ? M_WAITOK : M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + return (ENOBUFS); + } + + /* + * JMC250 has 64bit boundary alignment limitation so jme(4) + * takes advantage of 10 bytes padding feature of hardware + * in order not to copy entire frame to align IP header on + * 32bit boundary. + */ + m->m_len = m->m_pkthdr.len = MCLBYTES; + + error = bus_dmamap_load_mbuf(sc->sc_dmat, + sc->jme_cdata.jme_rx_sparemap, + m, BUS_DMA_NOWAIT); + if (error != 0) { + if (!error) { + bus_dmamap_unload(sc->sc_dmat, + sc->jme_cdata.jme_rx_sparemap); + error = EFBIG; + printf("%s: too many segments?!\n", + sc->sc_dev.dv_xname); + } + m_freem(m); + + if (init) + printf("%s: can't load RX mbuf\n", sc->sc_dev.dv_xname); + return (error); + } + + if (rxd->rx_m != NULL) { + bus_dmamap_sync(sc->sc_dmat, rxd->rx_dmamap, 0, + rxd->rx_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, rxd->rx_dmamap); + } + map = rxd->rx_dmamap; + rxd->rx_dmamap = sc->jme_cdata.jme_rx_sparemap; + sc->jme_cdata.jme_rx_sparemap = map; + rxd->rx_m = m; + + desc = rxd->rx_desc; + desc->buflen = htole32(rxd->rx_dmamap->dm_segs[0].ds_len); + desc->addr_lo = + htole32(JME_ADDR_LO(rxd->rx_dmamap->dm_segs[0].ds_addr)); + desc->addr_hi = + htole32(JME_ADDR_HI(rxd->rx_dmamap->dm_segs[0].ds_addr)); + desc->flags = htole32(JME_RD_OWN | JME_RD_INTR | JME_RD_64BIT); + + return (0); +} + +void +jme_set_vlan(struct jme_softc *sc) +{ +// struct ifnet *ifp = &sc->sc_arpcom.ac_if; + uint32_t reg; + + reg = CSR_READ_4(sc, JME_RXMAC); + reg &= ~RXMAC_VLAN_ENB; +#if 0 + if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) + reg |= RXMAC_VLAN_ENB; +#endif + CSR_WRITE_4(sc, JME_RXMAC, reg); +} + +void +jme_set_filter(struct jme_softc *sc) +{ + struct arpcom *ac = &sc->sc_arpcom; + struct ifnet *ifp = &ac->ac_if; + struct ether_multi *enm; + struct ether_multistep step; + uint32_t crc; + uint32_t mchash[2]; + uint32_t rxcfg; + + rxcfg = CSR_READ_4(sc, JME_RXMAC); + rxcfg &= ~(RXMAC_BROADCAST | RXMAC_PROMISC | RXMAC_MULTICAST | + RXMAC_ALLMULTI); + + /* + * Always accept frames destined to our station address. + * Always accept broadcast frames. + */ + rxcfg |= RXMAC_UNICAST | RXMAC_BROADCAST; + + if (ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) { + if (ifp->if_flags & IFF_PROMISC) + rxcfg |= RXMAC_PROMISC; + if (ifp->if_flags & IFF_ALLMULTI) + rxcfg |= RXMAC_ALLMULTI; + CSR_WRITE_4(sc, JME_MAR0, 0xFFFFFFFF); + CSR_WRITE_4(sc, JME_MAR1, 0xFFFFFFFF); + CSR_WRITE_4(sc, JME_RXMAC, rxcfg); + return; + } + + /* + * Set up the multicast address filter by passing all multicast + * addresses through a CRC generator, and then using the low-order + * 6 bits as an index into the 64 bit multicast hash table. The + * high order bits select the register, while the rest of the bits + * select the bit within the register. + */ + rxcfg |= RXMAC_MULTICAST; + bzero(mchash, sizeof(mchash)); + + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + crc = ether_crc32_be(LLADDR((struct sockaddr_dl *) + enm->enm_addrlo), ETHER_ADDR_LEN); + + /* Just want the 6 least significant bits. */ + crc &= 0x3f; + + /* Set the corresponding bit in the hash table. */ + mchash[crc >> 5] |= 1 << (crc & 0x1f); + + ETHER_NEXT_MULTI(step, enm); + } + + CSR_WRITE_4(sc, JME_MAR0, mchash[0]); + CSR_WRITE_4(sc, JME_MAR1, mchash[1]); + CSR_WRITE_4(sc, JME_RXMAC, rxcfg); +} diff --git a/sys/dev/pci/if_jmereg.h b/sys/dev/pci/if_jmereg.h new file mode 100644 index 00000000000..1632ea9a1a2 --- /dev/null +++ b/sys/dev/pci/if_jmereg.h @@ -0,0 +1,992 @@ +/* $OpenBSD: if_jmereg.h,v 1.1 2008/09/26 10:35:15 jsg Exp $ */ +/*- + * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/jme/if_jmereg.h,v 1.1 2008/05/27 01:42:01 yongari Exp $ + * $DragonFly: src/sys/dev/netif/jme/if_jmereg.h,v 1.3 2008/09/13 02:47:03 sephe Exp $ + */ + +#ifndef _IF_JMEREG_H +#define _IF_JMEREG_H + +#define JME_REV_JMC250_A1 0x01 +#define JME_REV_JMC250_A2 0x11 + +/* JMC250 PCI configuration register. */ +#define JME_PCIR_BAR 0x10 + +#define JME_PCI_EROM 0x30 + +#define JME_PCI_DBG 0x9C + +#define JME_PCI_SPI 0xB0 + +#define SPI_ENB 0x00000010 +#define SPI_SO_STATUS 0x00000008 +#define SPI_SI_CTRL 0x00000004 +#define SPI_SCK_CTRL 0x00000002 +#define SPI_CS_N_CTRL 0x00000001 + +#define JME_PCI_PHYCFG0 0xC0 + +#define JME_PCI_PHYCFG1 0xC4 + +#define JME_PCI_PHYCFG2 0xC8 + +#define JME_PCI_PHYCFG3 0xCC + +#define JME_PCI_PIPECTL1 0xD0 + +#define JME_PCI_PIPECTL2 0xD4 + +/* PCIe link error/status. */ +#define JME_PCI_LES 0xD8 + +/* propeietary register 0. */ +#define JME_PCI_PE0 0xE0 +#define PE0_SPI_EXIST 0x00200000 +#define PE0_PME_D0 0x00100000 +#define PE0_PME_D3H 0x00080000 +#define PE0_PME_SPI_PAD 0x00040000 +#define PE0_MASK_ASPM 0x00020000 +#define PE0_EEPROM_RW_DIS 0x00008000 +#define PE0_PCI_INTA 0x00001000 +#define PE0_PCI_INTB 0x00002000 +#define PE0_PCI_INTC 0x00003000 +#define PE0_PCI_INTD 0x00004000 +#define PE0_PCI_SVSSID_WR_ENB 0x00000800 +#define PE0_MSIX_SIZE_8 0x00000700 +#define PE0_MSIX_SIZE_7 0x00000600 +#define PE0_MSIX_SIZE_6 0x00000500 +#define PE0_MSIX_SIZE_5 0x00000400 +#define PE0_MSIX_SIZE_4 0x00000300 +#define PE0_MSIX_SIZE_3 0x00000200 +#define PE0_MSIX_SIZE_2 0x00000100 +#define PE0_MSIX_SIZE_1 0x00000000 +#define PE0_MSIX_SIZE_DEF 0x00000700 +#define PE0_MSIX_CAP_DIS 0x00000080 +#define PE0_MSI_PVMC_ENB 0x00000040 +#define PE0_LCAP_EXIT_LAT_MASK 0x00000038 +#define PE0_LCAP_EXIT_LAT_DEF 0x00000038 +#define PE0_PM_AUXC_MASK 0x00000007 +#define PE0_PM_AUXC_DEF 0x00000007 + +#define JME_PCI_PE1 0xE4 + +#define JME_PCI_PHYTEST 0xF8 + +#define JME_PCI_GPR 0xFC + +/* + * JMC Register Map. + * ----------------------------------------------------------------------- + * Register Size IO space Memory space + * ----------------------------------------------------------------------- + * Tx/Rx MAC registers 128 bytes BAR1 + 0x00 ~ BAR0 + 0x00 ~ + * BAR1 + 0x7F BAR0 + 0x7F + * ----------------------------------------------------------------------- + * PHY registers 128 bytes BAR2 + 0x00 ~ BAR0 + 0x400 ~ + * BAR2 + 0x7F BAR0 + 0x47F + * ----------------------------------------------------------------------- + * Misc registers 128 bytes BAR2 + 0x80 ~ BAR0 + 0x800 ~ + * BAR2 + 0x7F BAR0 + 0x87F + * ----------------------------------------------------------------------- + * To simplify register access fuctions and to get better performance + * this driver doesn't support IO space access. It could be implemented + * as a function which selects appropriate BARs to access requested + * register. + */ + +/* Tx control and status. */ +#define JME_TXCSR 0x0000 +#define TXCSR_QWEIGHT_MASK 0x0F000000 +#define TXCSR_QWEIGHT_SHIFT 24 +#define TXCSR_TXQ_SEL_MASK 0x00070000 +#define TXCSR_TXQ_SEL_SHIFT 16 +#define TXCSR_TXQ_START 0x00000001 +#define TXCSR_TXQ_START_SHIFT 8 +#define TXCSR_FIFO_THRESH_4QW 0x00000000 +#define TXCSR_FIFO_THRESH_8QW 0x00000040 +#define TXCSR_FIFO_THRESH_12QW 0x00000080 +#define TXCSR_FIFO_THRESH_16QW 0x000000C0 +#define TXCSR_DMA_SIZE_64 0x00000000 +#define TXCSR_DMA_SIZE_128 0x00000010 +#define TXCSR_DMA_SIZE_256 0x00000020 +#define TXCSR_DMA_SIZE_512 0x00000030 +#define TXCSR_DMA_BURST 0x00000004 +#define TXCSR_TX_SUSPEND 0x00000002 +#define TXCSR_TX_ENB 0x00000001 +#define TXCSR_TXQ0 0 +#define TXCSR_TXQ1 1 +#define TXCSR_TXQ2 2 +#define TXCSR_TXQ3 3 +#define TXCSR_TXQ4 4 +#define TXCSR_TXQ5 5 +#define TXCSR_TXQ6 6 +#define TXCSR_TXQ7 7 +#define TXCSR_TXQ_WEIGHT(x) \ + (((x) << TXCSR_QWEIGHT_SHIFT) & TXCSR_QWEIGHT_MASK) +#define TXCSR_TXQ_WEIGHT_MIN 0 +#define TXCSR_TXQ_WEIGHT_MAX 15 +#define TXCSR_TXQ_N_SEL(x) \ + (((x) << TXCSR_TXQ_SEL_SHIFT) & TXCSR_TXQ_SEL_MASK) +#define TXCSR_TXQ_N_START(x) \ + (TXCSR_TXQ_START << (TXCSR_TXQ_START_SHIFT + (x))) + +/* Tx queue descriptor base address. 16bytes alignment required. */ +#define JME_TXDBA_LO 0x0004 +#define JME_TXDBA_HI 0x0008 + +/* Tx queue descriptor count. multiple of 16(max = 1024). */ +#define JME_TXQDC 0x000C +#define TXQDC_MASK 0x0000007F0 + +/* Tx queue next descriptor address. */ +#define JME_TXNDA 0x0010 +#define TXNDA_ADDR_MASK 0xFFFFFFF0 +#define TXNDA_DESC_EMPTY 0x00000008 +#define TXNDA_DESC_VALID 0x00000004 +#define TXNDA_DESC_WAIT 0x00000002 +#define TXNDA_DESC_FETCH 0x00000001 + +/* Tx MAC control ans status. */ +#define JME_TXMAC 0x0014 +#define TXMAC_IFG2_MASK 0xC0000000 +#define TXMAC_IFG2_DEFAULT 0x40000000 +#define TXMAC_IFG1_MASK 0x30000000 +#define TXMAC_IFG1_DEFAULT 0x20000000 +#define TXMAC_THRESH_1_PKT 0x00000300 +#define TXMAC_THRESH_1_2_PKT 0x00000200 +#define TXMAC_THRESH_1_4_PKT 0x00000100 +#define TXMAC_THRESH_1_8_PKT 0x00000000 +#define TXMAC_FRAME_BURST 0x00000080 +#define TXMAC_CARRIER_EXT 0x00000040 +#define TXMAC_IFG_ENB 0x00000020 +#define TXMAC_BACKOFF 0x00000010 +#define TXMAC_CARRIER_SENSE 0x00000008 +#define TXMAC_COLL_ENB 0x00000004 +#define TXMAC_CRC_ENB 0x00000002 +#define TXMAC_PAD_ENB 0x00000001 + +/* Tx pause frame control. */ +#define JME_TXPFC 0x0018 +#define TXPFC_VLAN_TAG_MASK 0xFFFF0000 +#define TXPFC_VLAN_TAG_SHIFT 16 +#define TXPFC_VLAN_ENB 0x00008000 +#define TXPFC_PAUSE_ENB 0x00000001 + +/* Tx timer/retry at half duplex. */ +#define JME_TXTRHD 0x001C +#define TXTRHD_RT_PERIOD_ENB 0x80000000 +#define TXTRHD_RT_PERIOD_MASK 0x7FFFFF00 +#define TXTRHD_RT_PERIOD_SHIFT 8 +#define TXTRHD_RT_LIMIT_ENB 0x00000080 +#define TXTRHD_RT_LIMIT_MASK 0x0000007F +#define TXTRHD_RT_LIMIT_SHIFT 0 +#define TXTRHD_RT_PERIOD_DEFAULT 8192 +#define TXTRHD_RT_LIMIT_DEFAULT 8 + +/* Rx control & status. */ +#define JME_RXCSR 0x0020 +#define RXCSR_FIFO_FTHRESH_16T 0x00000000 +#define RXCSR_FIFO_FTHRESH_32T 0x10000000 +#define RXCSR_FIFO_FTHRESH_64T 0x20000000 +#define RXCSR_FIFO_FTHRESH_128T 0x30000000 +#define RXCSR_FIFO_FTHRESH_MASK 0x30000000 +#define RXCSR_FIFO_THRESH_16QW 0x00000000 +#define RXCSR_FIFO_THRESH_32QW 0x04000000 +#define RXCSR_FIFO_THRESH_64QW 0x08000000 +#define RXCSR_FIFO_THRESH_128QW 0x0C000000 +#define RXCSR_FIFO_THRESH_MASK 0x0C000000 +#define RXCSR_DMA_SIZE_16 0x00000000 +#define RXCSR_DMA_SIZE_32 0x01000000 +#define RXCSR_DMA_SIZE_64 0x02000000 +#define RXCSR_DMA_SIZE_128 0x03000000 +#define RXCSR_RXQ_SEL_MASK 0x00030000 +#define RXCSR_RXQ_SEL_SHIFT 16 +#define RXCSR_DESC_RT_GAP_MASK 0x0000F000 +#define RXCSR_DESC_RT_GAP_SHIFT 12 +#define RXCSR_DESC_RT_GAP_256 0x00000000 +#define RXCSR_DESC_RT_GAP_512 0x00001000 +#define RXCSR_DESC_RT_GAP_1024 0x00002000 +#define RXCSR_DESC_RT_GAP_2048 0x00003000 +#define RXCSR_DESC_RT_GAP_4096 0x00004000 +#define RXCSR_DESC_RT_GAP_8192 0x00005000 +#define RXCSR_DESC_RT_GAP_16384 0x00006000 +#define RXCSR_DESC_RT_GAP_32768 0x00007000 +#define RXCSR_DESC_RT_CNT_MASK 0x00000F00 +#define RXCSR_DESC_RT_CNT_SHIFT 8 +#define RXCSR_PASS_WAKEUP_PKT 0x00000040 +#define RXCSR_PASS_MAGIC_PKT 0x00000020 +#define RXCSR_PASS_RUNT_PKT 0x00000010 +#define RXCSR_PASS_BAD_PKT 0x00000008 +#define RXCSR_RXQ_START 0x00000004 +#define RXCSR_RX_SUSPEND 0x00000002 +#define RXCSR_RX_ENB 0x00000001 + +#define RXCSR_RXQ_N_SEL(x) ((x) << RXCSR_RXQ_SEL_SHIFT) +#define RXCSR_RXQ0 0 +#define RXCSR_RXQ1 1 +#define RXCSR_RXQ2 2 +#define RXCSR_RXQ3 3 +#define RXCSR_DESC_RT_CNT(x) \ + ((((x) / 4) << RXCSR_DESC_RT_CNT_SHIFT) & RXCSR_DESC_RT_CNT_MASK) +#define RXCSR_DESC_RT_CNT_DEFAULT 32 + +/* Rx queue descriptor base address. 16bytes alignment needed. */ +#define JME_RXDBA_LO 0x0024 +#define JME_RXDBA_HI 0x0028 + +/* Rx queue descriptor count. multiple of 16(max = 1024). */ +#define JME_RXQDC 0x002C +#define RXQDC_MASK 0x0000007F0 + +/* Rx queue next descriptor address. */ +#define JME_RXNDA 0x0030 +#define RXNDA_ADDR_MASK 0xFFFFFFF0 +#define RXNDA_DESC_EMPTY 0x00000008 +#define RXNDA_DESC_VALID 0x00000004 +#define RXNDA_DESC_WAIT 0x00000002 +#define RXNDA_DESC_FETCH 0x00000001 + +/* Rx MAC control and status. */ +#define JME_RXMAC 0x0034 +#define RXMAC_RSS_UNICAST 0x00000000 +#define RXMAC_RSS_UNI_MULTICAST 0x00010000 +#define RXMAC_RSS_UNI_MULTI_BROADCAST 0x00020000 +#define RXMAC_RSS_ALLFRAME 0x00030000 +#define RXMAC_PROMISC 0x00000800 +#define RXMAC_BROADCAST 0x00000400 +#define RXMAC_MULTICAST 0x00000200 +#define RXMAC_UNICAST 0x00000100 +#define RXMAC_ALLMULTI 0x00000080 +#define RXMAC_MULTICAST_FILTER 0x00000040 +#define RXMAC_COLL_DET_ENB 0x00000020 +#define RXMAC_FC_ENB 0x00000008 +#define RXMAC_VLAN_ENB 0x00000004 +#define RXMAC_PAD_10BYTES 0x00000002 +#define RXMAC_CSUM_ENB 0x00000001 + +/* Rx unicast MAC address. */ +#define JME_PAR0 0x0038 +#define JME_PAR1 0x003C + +/* Rx multicast address hash table. */ +#define JME_MAR0 0x0040 +#define JME_MAR1 0x0044 + +/* Wakeup frame output data port. */ +#define JME_WFODP 0x0048 + +/* Wakeup frame output interface. */ +#define JME_WFOI 0x004C +#define WFOI_MASK_0_31 0x00000000 +#define WFOI_MASK_31_63 0x00000010 +#define WFOI_MASK_64_95 0x00000020 +#define WFOI_MASK_96_127 0x00000030 +#define WFOI_MASK_SEL 0x00000008 +#define WFOI_CRC_SEL 0x00000000 +#define WFOI_WAKEUP_FRAME_MASK 0x00000007 +#define WFOI_WAKEUP_FRAME_SEL(x) ((x) & WFOI_WAKEUP_FRAME_MASK) + +/* Station management interface. */ +#define JME_SMI 0x0050 +#define SMI_DATA_MASK 0xFFFF0000 +#define SMI_DATA_SHIFT 16 +#define SMI_REG_ADDR_MASK 0x0000F800 +#define SMI_REG_ADDR_SHIFT 11 +#define SMI_PHY_ADDR_MASK 0x000007C0 +#define SMI_PHY_ADDR_SHIFT 6 +#define SMI_OP_WRITE 0x00000020 +#define SMI_OP_READ 0x00000000 +#define SMI_OP_EXECUTE 0x00000010 +#define SMI_MDIO 0x00000008 +#define SMI_MDOE 0x00000004 +#define SMI_MDC 0x00000002 +#define SMI_MDEN 0x00000001 +#define SMI_REG_ADDR(x) \ + (((x) << SMI_REG_ADDR_SHIFT) & SMI_REG_ADDR_MASK) +#define SMI_PHY_ADDR(x) \ + (((x) << SMI_PHY_ADDR_SHIFT) & SMI_PHY_ADDR_MASK) + +/* Global host control. */ +#define JME_GHC 0x0054 +#define GHC_LOOPBACK 0x80000000 +#define GHC_RESET 0x40000000 +#define GHC_FULL_DUPLEX 0x00000040 +#define GHC_SPEED_UNKNOWN 0x00000000 +#define GHC_SPEED_10 0x00000010 +#define GHC_SPEED_100 0x00000020 +#define GHC_SPEED_1000 0x00000030 +#define GHC_SPEED_MASK 0x00000030 +#define GHC_LINK_OFF 0x00000004 +#define GHC_LINK_ON 0x00000002 +#define GHC_LINK_STAT_POLLING 0x00000001 + +/* Power management control and status. */ +#define JME_PMCS 0x0060 +#define PMCS_WAKEUP_FRAME_7 0x80000000 +#define PMCS_WAKEUP_FRAME_6 0x40000000 +#define PMCS_WAKEUP_FRAME_5 0x20000000 +#define PMCS_WAKEUP_FRAME_4 0x10000000 +#define PMCS_WAKEUP_FRAME_3 0x08000000 +#define PMCS_WAKEUP_FRAME_2 0x04000000 +#define PMCS_WAKEUP_FRAME_1 0x02000000 +#define PMCS_WAKEUP_FRAME_0 0x01000000 +#define PMCS_LINK_FAIL 0x00040000 +#define PMCS_LINK_RISING 0x00020000 +#define PMCS_MAGIC_FRAME 0x00010000 +#define PMCS_WAKEUP_FRAME_7_ENB 0x00008000 +#define PMCS_WAKEUP_FRAME_6_ENB 0x00004000 +#define PMCS_WAKEUP_FRAME_5_ENB 0x00002000 +#define PMCS_WAKEUP_FRAME_4_ENB 0x00001000 +#define PMCS_WAKEUP_FRAME_3_ENB 0x00000800 +#define PMCS_WAKEUP_FRAME_2_ENB 0x00000400 +#define PMCS_WAKEUP_FRAME_1_ENB 0x00000200 +#define PMCS_WAKEUP_FRAME_0_ENB 0x00000100 +#define PMCS_LINK_FAIL_ENB 0x00000004 +#define PMCS_LINK_RISING_ENB 0x00000002 +#define PMCS_MAGIC_FRAME_ENB 0x00000001 +#define PMCS_WOL_ENB_MASK 0x0000FFFF + +/* Giga PHY & EEPROM registers. */ +#define JME_PHY_EEPROM_BASE_ADDR 0x0400 + +#define JME_GIGAR0LO 0x0400 +#define JME_GIGAR0HI 0x0404 +#define JME_GIGARALO 0x0408 +#define JME_GIGARAHI 0x040C +#define JME_GIGARBLO 0x0410 +#define JME_GIGARBHI 0x0414 +#define JME_GIGARCLO 0x0418 +#define JME_GIGARCHI 0x041C +#define JME_GIGARDLO 0x0420 +#define JME_GIGARDHI 0x0424 + +/* BIST status and control. */ +#define JME_GIGACSR 0x0428 +#define GIGACSR_STATUS 0x40000000 +#define GIGACSR_CTRL_MASK 0x30000000 +#define GIGACSR_CTRL_DEFAULT 0x30000000 +#define GIGACSR_TX_CLK_MASK 0x0F000000 +#define GIGACSR_RX_CLK_MASK 0x00F00000 +#define GIGACSR_TX_CLK_INV 0x00080000 +#define GIGACSR_RX_CLK_INV 0x00040000 +#define GIGACSR_PHY_RST 0x00010000 +#define GIGACSR_IRQ_N_O 0x00001000 +#define GIGACSR_BIST_OK 0x00000200 +#define GIGACSR_BIST_DONE 0x00000100 +#define GIGACSR_BIST_LED_ENB 0x00000010 +#define GIGACSR_BIST_MASK 0x00000003 + +/* PHY Link Status. */ +#define JME_LNKSTS 0x0430 +#define LINKSTS_SPEED_10 0x00000000 +#define LINKSTS_SPEED_100 0x00004000 +#define LINKSTS_SPEED_1000 0x00008000 +#define LINKSTS_FULL_DUPLEX 0x00002000 +#define LINKSTS_PAGE_RCVD 0x00001000 +#define LINKSTS_SPDDPX_RESOLVED 0x00000800 +#define LINKSTS_UP 0x00000400 +#define LINKSTS_ANEG_COMP 0x00000200 +#define LINKSTS_MDI_CROSSOVR 0x00000040 +#define LINKSTS_LPAR_PAUSE_ASYM 0x00000002 +#define LINKSTS_LPAR_PAUSE 0x00000001 + +/* SMB control and status. */ +#define JME_SMBCSR 0x0440 +#define SMBCSR_SLAVE_ADDR_MASK 0x7F000000 +#define SMBCSR_WR_DATA_NACK 0x00040000 +#define SMBCSR_CMD_NACK 0x00020000 +#define SMBCSR_RELOAD 0x00010000 +#define SMBCSR_CMD_ADDR_MASK 0x0000FF00 +#define SMBCSR_SCL_STAT 0x00000080 +#define SMBCSR_SDA_STAT 0x00000040 +#define SMBCSR_EEPROM_PRESENT 0x00000020 +#define SMBCSR_INIT_LD_DONE 0x00000010 +#define SMBCSR_HW_BUSY_MASK 0x0000000F +#define SMBCSR_HW_IDLE 0x00000000 + +/* SMB interface. */ +#define JME_SMBINTF 0x0444 +#define SMBINTF_RD_DATA_MASK 0xFF000000 +#define SMBINTF_RD_DATA_SHIFT 24 +#define SMBINTF_WR_DATA_MASK 0x00FF0000 +#define SMBINTF_WR_DATA_SHIFT 16 +#define SMBINTF_ADDR_MASK 0x0000FF00 +#define SMBINTF_ADDR_SHIFT 8 +#define SMBINTF_RD 0x00000020 +#define SMBINTF_WR 0x00000000 +#define SMBINTF_CMD_TRIGGER 0x00000010 +#define SMBINTF_BUSY 0x00000010 +#define SMBINTF_FAST_MODE 0x00000008 +#define SMBINTF_GPIO_SCL 0x00000004 +#define SMBINTF_GPIO_SDA 0x00000002 +#define SMBINTF_GPIO_ENB 0x00000001 + +#define JME_EEPROM_SIG0 0x55 +#define JME_EEPROM_SIG1 0xAA +#define JME_EEPROM_DESC_BYTES 3 +#define JME_EEPROM_DESC_END 0x80 +#define JME_EEPROM_FUNC_MASK 0x70 +#define JME_EEPROM_FUNC_SHIFT 4 +#define JME_EEPROM_PAGE_MASK 0x0F +#define JME_EEPROM_PAGE_SHIFT 0 + +#define JME_EEPROM_FUNC0 0 +/* PCI configuration space. */ +#define JME_EEPROM_PAGE_BAR0 0 +/* 128 bytes I/O window. */ +#define JME_EEPROM_PAGE_BAR1 1 +/* 256 bytes I/O window. */ +#define JME_EEPROM_PAGE_BAR2 2 + +#define JME_EEPROM_END 0xFF + +#define JME_EEPROM_MKDESC(f, p) \ + ((((f) & JME_EEPROM_FUNC_MASK) << JME_EEPROM_FUNC_SHIFT) | \ + (((p) & JME_EEPROM_PAGE_MASK) << JME_EEPROM_PAGE_SHIFT)) + +/* 3-wire EEPROM interface. Obsolete interface, use SMBCSR. */ +#define JME_EEPINTF 0x0448 +#define EEPINTF_DATA_MASK 0xFFFF0000 +#define EEPINTF_DATA_SHIFT 16 +#define EEPINTF_ADDR_MASK 0x0000FC00 +#define EEPINTF_ADDR_SHIFT 10 +#define EEPRINTF_OP_MASK 0x00000300 +#define EEPINTF_OP_EXECUTE 0x00000080 +#define EEPINTF_DATA_OUT 0x00000008 +#define EEPINTF_DATA_IN 0x00000004 +#define EEPINTF_CLK 0x00000002 +#define EEPINTF_SEL 0x00000001 + +/* 3-wire EEPROM control and status. Obsolete interface, use SMBCSR. */ +#define JME_EEPCSR 0x044C +#define EEPCSR_EEPROM_RELOAD 0x00000002 +#define EEPCSR_EEPROM_PRESENT 0x00000001 + +/* Misc registers. */ +#define JME_MISC_BASE_ADDR 0x800 + +/* Timer control and status. */ +#define JME_TMCSR 0x0800 +#define TMCSR_SW_INTR 0x80000000 +#define TMCSR_TIMER_INTR 0x10000000 +#define TMCSR_TIMER_ENB 0x01000000 +#define TMCSR_TIMER_COUNT_MASK 0x00FFFFFF + +/* GPIO control and status. */ +#define JME_GPIO 0x0804 +#define GPIO_4_SPI_IN 0x80000000 +#define GPIO_3_SPI_IN 0x40000000 +#define GPIO_4_SPI_OUT 0x20000000 +#define GPIO_4_SPI_OUT_ENB 0x10000000 +#define GPIO_3_SPI_OUT 0x08000000 +#define GPIO_3_SPI_OUT_ENB 0x04000000 +#define GPIO_3_4_LED 0x00000000 +#define GPIO_3_4_GPIO 0x02000000 +#define GPIO_2_CLKREQN_IN 0x00100000 +#define GPIO_2_CLKREQN_OUT 0x00040000 +#define GPIO_2_CLKREQN_OUT_ENB 0x00020000 +#define GPIO_1_LED42_IN 0x00001000 +#define GPIO_1_LED42_OUT 0x00000400 +#define GPIO_1_LED42_OUT_ENB 0x00000200 +#define GPIO_1_LED42_ENB 0x00000100 +#define GPIO_0_SDA_IN 0x00000010 +#define GPIO_0_SDA_OUT 0x00000004 +#define GPIO_0_SDA_OUT_ENB 0x00000002 +#define GPIO_0_SDA_ENB 0x00000001 + +/* General purpose register 0. */ +#define JME_GPREG0 0x0808 +#define GPREG0_SH_POST_DW7_DIS 0x80000000 +#define GPREG0_SH_POST_DW6_DIS 0x40000000 +#define GPREG0_SH_POST_DW5_DIS 0x20000000 +#define GPREG0_SH_POST_DW4_DIS 0x10000000 +#define GPREG0_SH_POST_DW3_DIS 0x08000000 +#define GPREG0_SH_POST_DW2_DIS 0x04000000 +#define GPREG0_SH_POST_DW1_DIS 0x02000000 +#define GPREG0_SH_POST_DW0_DIS 0x01000000 +#define GPREG0_DMA_RD_REQ_8 0x00000000 +#define GPREG0_DMA_RD_REQ_6 0x00100000 +#define GPREG0_DMA_RD_REQ_5 0x00200000 +#define GPREG0_DMA_RD_REQ_4 0x00300000 +#define GPREG0_POST_DW0_ENB 0x00040000 +#define GPREG0_PCC_CLR_DIS 0x00020000 +#define GPREG0_FORCE_SCL_OUT 0x00010000 +#define GPREG0_DL_RSTB_DIS 0x00008000 +#define GPREG0_STICKY_RESET 0x00004000 +#define GPREG0_DL_RSTB_CFG_DIS 0x00002000 +#define GPREG0_LINK_CHG_POLL 0x00001000 +#define GPREG0_LINK_CHG_DIRECT 0x00000000 +#define GPREG0_MSI_GEN_SEL 0x00000800 +#define GPREG0_SMB_PAD_PU_DIS 0x00000400 +#define GPREG0_PCC_UNIT_16US 0x00000000 +#define GPREG0_PCC_UNIT_256US 0x00000100 +#define GPREG0_PCC_UNIT_US 0x00000200 +#define GPREG0_PCC_UNIT_MS 0x00000300 +#define GPREG0_PCC_UNIT_MASK 0x00000300 +#define GPREG0_INTR_EVENT_ENB 0x00000080 +#define GPREG0_PME_ENB 0x00000020 +#define GPREG0_PHY_ADDR_MASK 0x0000001F +#define GPREG0_PHY_ADDR_SHIFT 0 +#define GPREG0_PHY_ADDR 1 + +/* General purpose register 1. reserved for future use. */ +#define JME_GPREG1 0x080C + +/* MSIX entry number of interrupt source. */ +#define JME_MSINUM_BASE 0x0810 +#define JME_MSINUM_END 0x081F +#define MSINUM_MASK 0x7FFFFFFF +#define MSINUM_ENTRY_MASK 7 +#define MSINUM_REG_INDEX(x) ((x) / 8) +#define MSINUM_INTR_SOURCE(x, y) \ + (((x) & MSINUM_ENTRY_MASK) << (((y) & 7) * 4)) +#define MSINUM_NUM_INTR_SOURCE 32 + +/* Interrupt event status. */ +#define JME_INTR_STATUS 0x0820 +#define INTR_SW 0x80000000 +#define INTR_TIMER 0x40000000 +#define INTR_LINKCHG 0x20000000 +#define INTR_PAUSE 0x10000000 +#define INTR_MAGIC_PKT 0x08000000 +#define INTR_WAKEUP_PKT 0x04000000 +#define INTR_RXQ0_COAL_TO 0x02000000 +#define INTR_RXQ1_COAL_TO 0x01000000 +#define INTR_RXQ2_COAL_TO 0x00800000 +#define INTR_RXQ3_COAL_TO 0x00400000 +#define INTR_TXQ_COAL_TO 0x00200000 +#define INTR_RXQ0_COAL 0x00100000 +#define INTR_RXQ1_COAL 0x00080000 +#define INTR_RXQ2_COAL 0x00040000 +#define INTR_RXQ3_COAL 0x00020000 +#define INTR_TXQ_COAL 0x00010000 +#define INTR_RXQ3_DESC_EMPTY 0x00008000 +#define INTR_RXQ2_DESC_EMPTY 0x00004000 +#define INTR_RXQ1_DESC_EMPTY 0x00002000 +#define INTR_RXQ0_DESC_EMPTY 0x00001000 +#define INTR_RXQ3_COMP 0x00000800 +#define INTR_RXQ2_COMP 0x00000400 +#define INTR_RXQ1_COMP 0x00000200 +#define INTR_RXQ0_COMP 0x00000100 +#define INTR_TXQ7_COMP 0x00000080 +#define INTR_TXQ6_COMP 0x00000040 +#define INTR_TXQ5_COMP 0x00000020 +#define INTR_TXQ4_COMP 0x00000010 +#define INTR_TXQ3_COMP 0x00000008 +#define INTR_TXQ2_COMP 0x00000004 +#define INTR_TXQ1_COMP 0x00000002 +#define INTR_TXQ0_COMP 0x00000001 + +#define INTR_RXQ_COAL_TO \ + (INTR_RXQ0_COAL_TO | INTR_RXQ1_COAL_TO | \ + INTR_RXQ2_COAL_TO | INTR_RXQ3_COAL_TO) + +#define INTR_RXQ_COAL \ + (INTR_RXQ0_COAL | INTR_RXQ1_COAL | INTR_RXQ2_COAL | \ + INTR_RXQ3_COAL) + +#define INTR_RXQ_COMP \ + (INTR_RXQ0_COMP | INTR_RXQ1_COMP | INTR_RXQ2_COMP | \ + INTR_RXQ3_COMP) + +#define INTR_RXQ_DESC_EMPTY \ + (INTR_RXQ0_DESC_EMPTY | INTR_RXQ1_DESC_EMPTY | \ + INTR_RXQ2_DESC_EMPTY | INTR_RXQ3_DESC_EMPTY) + +#define INTR_RXQ_COMP \ + (INTR_RXQ0_COMP | INTR_RXQ1_COMP | INTR_RXQ2_COMP | \ + INTR_RXQ3_COMP) + +#define INTR_TXQ_COMP \ + (INTR_TXQ0_COMP | INTR_TXQ1_COMP | INTR_TXQ2_COMP | \ + INTR_TXQ3_COMP | INTR_TXQ4_COMP | INTR_TXQ5_COMP | \ + INTR_TXQ6_COMP | INTR_TXQ7_COMP) + +#define JME_INTRS \ + (INTR_RXQ_COAL_TO | INTR_TXQ_COAL_TO | INTR_RXQ_COAL | \ + INTR_TXQ_COAL | INTR_RXQ_DESC_EMPTY) + +#define N_INTR_SW 31 +#define N_INTR_TIMER 30 +#define N_INTR_LINKCHG 29 +#define N_INTR_PAUSE 28 +#define N_INTR_MAGIC_PKT 27 +#define N_INTR_WAKEUP_PKT 26 +#define N_INTR_RXQ0_COAL_TO 25 +#define N_INTR_RXQ1_COAL_TO 24 +#define N_INTR_RXQ2_COAL_TO 23 +#define N_INTR_RXQ3_COAL_TO 22 +#define N_INTR_TXQ_COAL_TO 21 +#define N_INTR_RXQ0_COAL 20 +#define N_INTR_RXQ1_COAL 19 +#define N_INTR_RXQ2_COAL 18 +#define N_INTR_RXQ3_COAL 17 +#define N_INTR_TXQ_COAL 16 +#define N_INTR_RXQ3_DESC_EMPTY 15 +#define N_INTR_RXQ2_DESC_EMPTY 14 +#define N_INTR_RXQ1_DESC_EMPTY 13 +#define N_INTR_RXQ0_DESC_EMPTY 12 +#define N_INTR_RXQ3_COMP 11 +#define N_INTR_RXQ2_COMP 10 +#define N_INTR_RXQ1_COMP 9 +#define N_INTR_RXQ0_COMP 8 +#define N_INTR_TXQ7_COMP 7 +#define N_INTR_TXQ6_COMP 6 +#define N_INTR_TXQ5_COMP 5 +#define N_INTR_TXQ4_COMP 4 +#define N_INTR_TXQ3_COMP 3 +#define N_INTR_TXQ2_COMP 2 +#define N_INTR_TXQ1_COMP 1 +#define N_INTR_TXQ0_COMP 0 + +/* Interrupt request status. */ +#define JME_INTR_REQ_STATUS 0x0824 + +/* Interrupt enable - setting port. */ +#define JME_INTR_MASK_SET 0x0828 + +/* Interrupt enable - clearing port. */ +#define JME_INTR_MASK_CLR 0x082C + +/* Packet completion coalescing control of Rx queue 0, 1, 2 and 3. */ +#define JME_PCCRX0 0x0830 +#define JME_PCCRX1 0x0834 +#define JME_PCCRX2 0x0838 +#define JME_PCCRX3 0x083C +#define PCCRX_COAL_TO_MASK 0xFFFF0000 +#define PCCRX_COAL_TO_SHIFT 16 +#define PCCRX_COAL_PKT_MASK 0x0000FF00 +#define PCCRX_COAL_PKT_SHIFT 8 + +#define PCCRX_COAL_TO_MIN 1 +#define PCCRX_COAL_TO_DEFAULT 100 +#define PCCRX_COAL_TO_MAX 65535 + +#define PCCRX_COAL_PKT_MIN 1 +#define PCCRX_COAL_PKT_DEFAULT 2 +#define PCCRX_COAL_PKT_MAX 255 + +/* Packet completion coalescing control of Tx queue. */ +#define JME_PCCTX 0x0840 +#define PCCTX_COAL_TO_MASK 0xFFFF0000 +#define PCCTX_COAL_TO_SHIFT 16 +#define PCCTX_COAL_PKT_MASK 0x0000FF00 +#define PCCTX_COAL_PKT_SHIFT 8 +#define PCCTX_COAL_TXQ7 0x00000080 +#define PCCTX_COAL_TXQ6 0x00000040 +#define PCCTX_COAL_TXQ5 0x00000020 +#define PCCTX_COAL_TXQ4 0x00000010 +#define PCCTX_COAL_TXQ3 0x00000008 +#define PCCTX_COAL_TXQ2 0x00000004 +#define PCCTX_COAL_TXQ1 0x00000002 +#define PCCTX_COAL_TXQ0 0x00000001 + +#define PCCTX_COAL_TO_MIN 1 +#define PCCTX_COAL_TO_DEFAULT 100 +#define PCCTX_COAL_TO_MAX 65535 + +#define PCCTX_COAL_PKT_MIN 1 +#define PCCTX_COAL_PKT_DEFAULT 8 +#define PCCTX_COAL_PKT_MAX 255 + +/* Chip mode and FPGA version. */ +#define JME_CHIPMODE 0x0844 +#define CHIPMODE_FPGA_REV_MASK 0xFFFF0000 +#define CHIPMODE_FPGA_REV_SHIFT 16 +#define CHIPMODE_NOT_FPGA 0 +#define CHIPMODE_REV_MASK 0x0000FF00 +#define CHIPMODE_REV_SHIFT 8 +#define CHIPMODE_MODE_48P 0x0000000C +#define CHIPMODE_MODE_64P 0x00000004 +#define CHIPMODE_MODE_128P_MAC 0x00000003 +#define CHIPMODE_MODE_128P_DBG 0x00000002 +#define CHIPMODE_MODE_128P_PHY 0x00000000 + +/* Shadow status base address high/low. */ +#define JME_SHBASE_ADDR_HI 0x0848 +#define JME_SHBASE_ADDR_LO 0x084C +#define SHBASE_ADDR_LO_MASK 0xFFFFFFE0 +#define SHBASE_POST_FORCE 0x00000002 +#define SHBASE_POST_ENB 0x00000001 + +/* Timer 1 and 2. */ +#define JME_TIMER1 0x0870 +#define JME_TIMER2 0x0874 +#define TIMER_ENB 0x01000000 +#define TIMER_CNT_MASK 0x00FFFFFF +#define TIMER_CNT_SHIFT 0 +#define TIMER_UNIT 1024 /* 1024us */ + +/* Aggresive power mode control. */ +#define JME_APMC 0x087C +#define APMC_PCIE_SDOWN_STAT 0x80000000 +#define APMC_PCIE_SDOWN_ENB 0x40000000 +#define APMC_PSEUDO_HOT_PLUG 0x20000000 +#define APMC_EXT_PLUGIN_ENB 0x04000000 +#define APMC_EXT_PLUGIN_CTL_MSK 0x03000000 +#define APMC_DIS_SRAM 0x00000004 +#define APMC_DIS_CLKPM 0x00000002 +#define APMC_DIS_CLKTX 0x00000001 + +/* Packet completion coalesing status of Rx queue 0, 1, 2 and 3. */ +#define JME_PCCSRX_BASE 0x0880 +#define JME_PCCSRX_END 0x088F +#define PCCSRX_REG(x) (JME_PCCSRX_BASE + ((x) * 4)) +#define PCCSRX_TO_MASK 0xFFFF0000 +#define PCCSRX_TO_SHIFT 16 +#define PCCSRX_PKT_CNT_MASK 0x0000FF00 +#define PCCSRX_PKT_CNT_SHIFT 8 + +/* Packet completion coalesing status of Tx queue. */ +#define JME_PCCSTX 0x0890 +#define PCCSTX_TO_MASK 0xFFFF0000 +#define PCCSTX_TO_SHIFT 16 +#define PCCSTX_PKT_CNT_MASK 0x0000FF00 +#define PCCSTX_PKT_CNT_SHIFT 8 + +/* Tx queues empty indicator. */ +#define JME_TXQEMPTY 0x0894 +#define TXQEMPTY_TXQ7 0x00000080 +#define TXQEMPTY_TXQ6 0x00000040 +#define TXQEMPTY_TXQ5 0x00000020 +#define TXQEMPTY_TXQ4 0x00000010 +#define TXQEMPTY_TXQ3 0x00000008 +#define TXQEMPTY_TXQ2 0x00000004 +#define TXQEMPTY_TXQ1 0x00000002 +#define TXQEMPTY_TXQ0 0x00000001 +#define TXQEMPTY_N_TXQ(x, y) ((x) & (0x01 << (y))) + +/* RSS control registers. */ +#define JME_RSS_BASE 0x0C00 + +#define JME_RSSC 0x0C00 +#define RSSC_HASH_LEN_MASK 0x0000E000 +#define RSSC_HASH_64_ENTRY 0x0000A000 +#define RSSC_HASH_128_ENTRY 0x0000E000 +#define RSSC_HASH_NONE 0x00001000 +#define RSSC_HASH_IPV6 0x00000800 +#define RSSC_HASH_IPV4 0x00000400 +#define RSSC_HASH_IPV6_TCP 0x00000200 +#define RSSC_HASH_IPV4_TCP 0x00000100 +#define RSSC_NCPU_MASK 0x000000F8 +#define RSSC_NCPU_SHIFT 3 +#define RSSC_DIS_RSS 0x00000000 +#define RSSC_2RXQ_ENB 0x00000001 +#define RSSS_4RXQ_ENB 0x00000002 + +/* CPU vector. */ +#define JME_RSSCPU 0x0C04 +#define RSSCPU_N_SEL(x) ((1 << (x)) + +/* RSS Hash value. */ +#define JME_RSSHASH 0x0C10 + +#define JME_RSSHASH_STAT 0x0C14 + +#define JME_RSS_RDATA0 0x0C18 + +#define JME_RSS_RDATA1 0x0C1C + +/* RSS secret key. */ +#define JME_RSSKEY_BASE 0x0C40 +#define JME_RSSKEY_LAST 0x0C64 +#define JME_RSSKEY_END 0x0C67 +#define HASHKEY_NBYTES 40 +#define RSSKEY_REG(x) (JME_RSSKEY_LAST - (4 * ((x) / 4))) +#define RSSKEY_VALUE(x, y) ((x) << (24 - 8 * ((y) % 4))) + +/* RSS indirection table entries. */ +#define JME_RSSTBL_BASE 0x0C80 +#define JME_RSSTBL_END 0x0CFF +#define RSSTBL_NENTRY 128 +#define RSSTBL_REG(x) (JME_RSSTBL_BASE + ((x) / 4)) +#define RSSTBL_VALUE(x, y) ((x) << (8 * ((y) % 4))) + +/* MSI-X table. */ +#define JME_MSIX_BASE_ADDR 0x2000 + +#define JME_MSIX_BASE 0x2000 +#define JME_MSIX_END 0x207F +#define JME_MSIX_NENTRY 8 +#define MSIX_REG(x) (JME_MSIX_BASE + ((x) * 0x10)) +#define MSIX_ADDR_HI_OFF 0x00 +#define MSIX_ADDR_LO_OFF 0x04 +#define MSIX_ADDR_LO_MASK 0xFFFFFFFC +#define MSIX_DATA_OFF 0x08 +#define MSIX_VECTOR_OFF 0x0C +#define MSIX_VECTOR_RSVD 0x80000000 +#define MSIX_VECTOR_DIS 0x00000001 + +/* MSI-X PBA. */ +#define JME_MSIX_PBA_BASE_ADDR 0x3000 + +#define JME_MSIX_PBA 0x3000 +#define MSIX_PBA_RSVD_MASK 0xFFFFFF00 +#define MSIX_PBA_RSVD_SHIFT 8 +#define MSIX_PBA_PEND_MASK 0x000000FF +#define MSIX_PBA_PEND_SHIFT 0 +#define MSIX_PBA_PEND_ENTRY7 0x00000080 +#define MSIX_PBA_PEND_ENTRY6 0x00000040 +#define MSIX_PBA_PEND_ENTRY5 0x00000020 +#define MSIX_PBA_PEND_ENTRY4 0x00000010 +#define MSIX_PBA_PEND_ENTRY3 0x00000008 +#define MSIX_PBA_PEND_ENTRY2 0x00000004 +#define MSIX_PBA_PEND_ENTRY1 0x00000002 +#define MSIX_PBA_PEND_ENTRY0 0x00000001 + +#define JME_PHY_OUI 0x001B8C +#define JME_PHY_MODEL 0x21 +#define JME_PHY_REV 0x01 +#define JME_PHY_ADDR 1 + +/* JMC250 shadow status block. */ +struct jme_ssb { + uint32_t dw0; + uint32_t dw1; + uint32_t dw2; + uint32_t dw3; + uint32_t dw4; + uint32_t dw5; + uint32_t dw6; + uint32_t dw7; +}; + +/* JMC250 descriptor structures. */ +struct jme_desc { + uint32_t flags; + uint32_t buflen; + uint32_t addr_hi; + uint32_t addr_lo; +}; + +#define JME_TD_OWN 0x80000000 +#define JME_TD_INTR 0x40000000 +#define JME_TD_64BIT 0x20000000 +#define JME_TD_TCPCSUM 0x10000000 +#define JME_TD_UDPCSUM 0x08000000 +#define JME_TD_IPCSUM 0x04000000 +#define JME_TD_TSO 0x02000000 +#define JME_TD_VLAN_TAG 0x01000000 +#define JME_TD_VLAN_MASK 0x0000FFFF + +#define JME_TD_MSS_MASK 0xFFFC0000 +#define JME_TD_MSS_SHIFT 18 +#define JME_TD_BUF_LEN_MASK 0x0000FFFF +#define JME_TD_BUF_LEN_SHIFT 0 + +#define JME_TD_FRAME_LEN_MASK 0x0000FFFF +#define JME_TD_FRAME_LEN_SHIFT 0 + +/* + * Only the first Tx descriptor of a packet is updated + * after packet transmission. + */ +#define JME_TD_TMOUT 0x20000000 +#define JME_TD_RETRY_EXP 0x10000000 +#define JME_TD_COLLISION 0x08000000 +#define JME_TD_UNDERRUN 0x04000000 +#define JME_TD_EHDR_SIZE_MASK 0x000000FF +#define JME_TD_EHDR_SIZE_SHIFT 0 + +#define JME_TD_SEG_CNT_MASK 0xFFFF0000 +#define JME_TD_SEG_CNT_SHIFT 16 +#define JME_TD_RETRY_CNT_MASK 0x0000FFFF +#define JME_TD_RETRY_CNT_SHIFT 0 + +#define JME_RD_OWN 0x80000000 +#define JME_RD_INTR 0x40000000 +#define JME_RD_64BIT 0x20000000 + +#define JME_RD_BUF_LEN_MASK 0x0000FFFF +#define JME_RD_BUF_LEN_SHIFT 0 + +/* + * Only the first Rx descriptor of a packet is updated + * after packet reception. + */ +#define JME_RD_MORE_FRAG 0x20000000 +#define JME_RD_TCP 0x10000000 +#define JME_RD_UDP 0x08000000 +#define JME_RD_IPCSUM 0x04000000 +#define JME_RD_TCPCSUM 0x02000000 +#define JME_RD_UDPCSUM 0x01000000 +#define JME_RD_VLAN_TAG 0x00800000 +#define JME_RD_IPV4 0x00400000 +#define JME_RD_IPV6 0x00200000 +#define JME_RD_PAUSE 0x00100000 +#define JME_RD_MAGIC 0x00080000 +#define JME_RD_WAKEUP 0x00040000 +#define JME_RD_BCAST 0x00030000 +#define JME_RD_MCAST 0x00020000 +#define JME_RD_UCAST 0x00010000 +#define JME_RD_VLAN_MASK 0x0000FFFF +#define JME_RD_VLAN_SHIFT 0 + +#define JME_RD_VALID 0x80000000 +#define JME_RD_CNT_MASK 0x7F000000 +#define JME_RD_CNT_SHIFT 24 +#define JME_RD_GIANT 0x00800000 +#define JME_RD_GMII_ERR 0x00400000 +#define JME_RD_NBL_RCVD 0x00200000 +#define JME_RD_COLL 0x00100000 +#define JME_RD_ABORT 0x00080000 +#define JME_RD_RUNT 0x00040000 +#define JME_RD_FIFO_OVRN 0x00020000 +#define JME_RD_CRC_ERR 0x00010000 +#define JME_RD_FRAME_LEN_MASK 0x0000FFFF + +#define JME_RX_ERR_STAT \ + (JME_RD_GIANT | JME_RD_GMII_ERR | JME_RD_NBL_RCVD | \ + JME_RD_COLL | JME_RD_ABORT | JME_RD_RUNT | \ + JME_RD_FIFO_OVRN | JME_RD_CRC_ERR) + +#define JME_RD_ERR_MASK 0x00FF0000 +#define JME_RD_ERR_SHIFT 16 +#define JME_RX_ERR(x) (((x) & JME_RD_ERR_MASK) >> JME_RD_ERR_SHIFT) +#define JME_RX_ERR_BITS "\20" \ + "\1CRCERR\2FIFOOVRN\3RUNT\4ABORT" \ + "\5COLL\6NBLRCVD\7GMIIERR\10" + +#define JME_RX_NSEGS(x) (((x) & JME_RD_CNT_MASK) >> JME_RD_CNT_SHIFT) +#define JME_RX_BYTES(x) ((x) & JME_RD_FRAME_LEN_MASK) +#define JME_RX_PAD_BYTES 10 + +#define JME_RD_RSS_HASH_VALUE 0xFFFFFFFF + +#define JME_RD_RSS_HASH_MASK 0x00003F00 +#define JME_RD_RSS_HASH_SHIFT 8 +#define JME_RD_RSS_HASH_NONE 0x00000000 +#define JME_RD_RSS_HASH_IPV4 0x00000100 +#define JME_RD_RSS_HASH_IPV4TCP 0x00000200 +#define JME_RD_RSS_HASH_IPV6 0x00000400 +#define JME_RD_RSS_HASH_IPV6TCP 0x00001000 +#define JME_RD_HASH_FN_NONE 0x00000000 +#define JME_RD_HASH_FN_TOEPLITZ 0x00000001 + +#endif diff --git a/sys/dev/pci/if_jmevar.h b/sys/dev/pci/if_jmevar.h new file mode 100644 index 00000000000..23d7fce4e49 --- /dev/null +++ b/sys/dev/pci/if_jmevar.h @@ -0,0 +1,244 @@ +/* $OpenBSD: if_jmevar.h,v 1.1 2008/09/26 10:35:15 jsg Exp $ */ +/*- + * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org> + * 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 unmodified, 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD: src/sys/dev/jme/if_jmevar.h,v 1.1 2008/05/27 01:42:01 yongari Exp $ + * $DragonFly: src/sys/dev/netif/jme/if_jmevar.h,v 1.4 2008/09/13 04:04:39 sephe Exp $ + */ + +#ifndef _IF_JMEVAR_H +#define _IF_JMEVAR_H + +/* + * JMC250 supports upto 1024 descriptors and the number of + * descriptors should be multiple of 16. + */ +#define JME_TX_RING_CNT 384 +#define JME_RX_RING_CNT 256 +/* + * Tx/Rx descriptor queue base should be 16bytes aligned and + * should not cross 4G bytes boundary on the 64bits address + * mode. + */ +#define JME_TX_RING_ALIGN 16 +#define JME_RX_RING_ALIGN 16 +#define JME_TSO_MAXSEGSIZE 4096 +#define JME_TSO_MAXSIZE (65535 + sizeof(struct ether_vlan_header)) +#define JME_MAXTXSEGS 32 +#define JME_RX_BUF_ALIGN sizeof(uint64_t) +#define JME_SSB_ALIGN 16 + +#define JME_ADDR_LO(x) ((uint64_t) (x) & 0xFFFFFFFF) +#define JME_ADDR_HI(x) ((uint64_t) (x) >> 32) + +#define JME_MSI_MESSAGES 8 +#define JME_MSIX_MESSAGES 8 + +/* Water mark to kick reclaiming Tx buffers. */ +#define JME_TX_DESC_HIWAT (JME_TX_RING_CNT - (((JME_TX_RING_CNT) * 3) / 10)) + +/* + * JMC250 can send 9K jumbo frame on Tx path and can receive + * 65535 bytes. + */ +#define JME_JUMBO_FRAMELEN 9216 +#define JME_JUMBO_MTU \ + (JME_JUMBO_FRAMELEN - sizeof(struct ether_vlan_header) - \ + ETHER_HDR_LEN - ETHER_CRC_LEN) +#define JME_MAX_MTU \ + (ETHER_MAX_LEN + sizeof(struct ether_vlan_header) - \ + ETHER_HDR_LEN - ETHER_CRC_LEN) +/* + * JMC250 can't handle Tx checksum offload/TSO if frame length + * is larger than its FIFO size(2K). It's also good idea to not + * use jumbo frame if hardware is running at half-duplex media. + * Because the jumbo frame may not fit into the Tx FIFO, + * collisions make hardware fetch frame from host memory with + * DMA again which in turn slows down Tx performance + * significantly. + */ +#define JME_TX_FIFO_SIZE 2000 +/* + * JMC250 has just 4K Rx FIFO. To support jumbo frame that is + * larger than 4K bytes in length, Rx FIFO threshold should be + * adjusted to minimize Rx FIFO overrun. + */ +#define JME_RX_FIFO_SIZE 4000 + +#define JME_DESC_INC(x, y) ((x) = ((x) + 1) % (y)) + +#define JME_PROC_MIN 10 +#define JME_PROC_DEFAULT (JME_RX_RING_CNT / 2) +#define JME_PROC_MAX (JME_RX_RING_CNT - 1) + +struct jme_txdesc { + struct mbuf *tx_m; + bus_dmamap_t tx_dmamap; + int tx_ndesc; + struct jme_desc *tx_desc; +}; + +struct jme_rxdesc { + struct mbuf *rx_m; + bus_dmamap_t rx_dmamap; + struct jme_desc *rx_desc; +}; + +struct jme_chain_data{ + bus_dma_tag_t jme_ring_tag; + bus_dma_tag_t jme_buffer_tag; + bus_dma_tag_t jme_ssb_tag; + bus_dmamap_t jme_ssb_map; + bus_dma_tag_t jme_tx_tag; + struct jme_txdesc jme_txdesc[JME_TX_RING_CNT]; + bus_dma_tag_t jme_rx_tag; + struct jme_rxdesc jme_rxdesc[JME_RX_RING_CNT]; + bus_dmamap_t jme_tx_ring_map; + bus_dma_segment_t jme_tx_ring_seg; + bus_dmamap_t jme_rx_ring_map; + bus_dma_segment_t jme_rx_ring_seg; + bus_dmamap_t jme_rx_sparemap; + + int jme_tx_prod; + int jme_tx_cons; + int jme_tx_cnt; + + int jme_rx_cons; + int jme_rxlen; + struct mbuf *jme_rxhead; + struct mbuf *jme_rxtail; +}; + +struct jme_ring_data { + struct jme_desc *jme_tx_ring; + bus_dma_segment_t jme_tx_ring_seg; + bus_addr_t jme_tx_ring_paddr; + struct jme_desc *jme_rx_ring; + bus_dma_segment_t jme_rx_ring_seg; + bus_addr_t jme_rx_ring_paddr; + struct jme_ssb *jme_ssb_block; + bus_dma_segment_t jme_ssb_block_seg; + bus_addr_t jme_ssb_block_paddr; +}; + +#define JME_TX_RING_ADDR(sc, i) \ + ((sc)->jme_rdata.jme_tx_ring_paddr + sizeof(struct jme_desc) * (i)) +#define JME_RX_RING_ADDR(sc, i) \ + ((sc)->jme_rdata.jme_rx_ring_paddr + sizeof(struct jme_desc) * (i)) + +#define JME_TX_RING_SIZE \ + (sizeof(struct jme_desc) * JME_TX_RING_CNT) +#define JME_RX_RING_SIZE \ + (sizeof(struct jme_desc) * JME_RX_RING_CNT) +#define JME_SSB_SIZE sizeof(struct jme_ssb) + +struct jme_dmamap_ctx { + int nsegs; + bus_dma_segment_t *segs; +}; + +/* + * Software state per device. + */ +struct jme_softc { + struct device sc_dev; + struct arpcom sc_arpcom; + + int jme_mem_rid; + struct resource *jme_mem_res; + bus_space_tag_t jme_mem_bt; + bus_space_handle_t jme_mem_bh; + bus_size_t jme_mem_size; + bus_dma_tag_t sc_dmat; + pci_chipset_tag_t jme_pct; + pcitag_t jme_pcitag; + + int jme_irq_rid; + struct resource *jme_irq_res; + void *sc_irq_handle; + + struct mii_data sc_miibus; + int jme_phyaddr; + + uint32_t jme_tx_dma_size; + uint32_t jme_rx_dma_size; + + uint32_t jme_caps; +#define JME_CAP_FPGA 0x0001 +#define JME_CAP_PCIE 0x0002 +#define JME_CAP_PMCAP 0x0004 +#define JME_CAP_FASTETH 0x0008 +#define JME_CAP_JUMBO 0x0010 +#define JME_CAP_EXTFIFO 0x0020 + + uint32_t jme_flags; +#define JME_FLAG_MSI 0x0001 +#define JME_FLAG_MSIX 0x0002 +#define JME_FLAG_DETACH 0x0004 +#define JME_FLAG_LINK 0x0008 + + struct timeout jme_tick_ch; + struct jme_chain_data jme_cdata; + struct jme_ring_data jme_rdata; + int jme_if_flags; + uint32_t jme_txcsr; + uint32_t jme_rxcsr; + + int jme_txd_spare; + + /* + * Sysctl variables + */ + int jme_process_limit; + int jme_tx_coal_to; + int jme_tx_coal_pkt; + int jme_rx_coal_to; + int jme_rx_coal_pkt; +}; + +/* Register access macros. */ +#define CSR_WRITE_4(_sc, reg, val) \ + bus_space_write_4((_sc)->jme_mem_bt, (_sc)->jme_mem_bh, (reg), (val)) +#define CSR_READ_4(_sc, reg) \ + bus_space_read_4((_sc)->jme_mem_bt, (_sc)->jme_mem_bh, (reg)) + +#define JME_MAXERR 5 + +#define JME_RXCHAIN_RESET(_sc) \ +do { \ + (_sc)->jme_cdata.jme_rxhead = NULL; \ + (_sc)->jme_cdata.jme_rxtail = NULL; \ + (_sc)->jme_cdata.jme_rxlen = 0; \ +} while (0) + +#define JME_TX_TIMEOUT 5 +#define JME_TIMEOUT 1000 +#define JME_PHY_TIMEOUT 1000 +#define JME_EEPROM_TIMEOUT 1000 + +#define JME_TXD_RSVD 1 + +#endif |