summaryrefslogtreecommitdiffstats
path: root/sys/pci
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>2000-04-22 02:16:41 +0000
committerwpaul <wpaul@FreeBSD.org>2000-04-22 02:16:41 +0000
commitffc1f10e0bf55eb5612e560d3660d7dd9ae68234 (patch)
treeacde787f0bb34b1705de38d7202e48232b0d48cb /sys/pci
parentd98654ae0128bb79d63a6df0231ebed9c121ae5d (diff)
downloadFreeBSD-src-ffc1f10e0bf55eb5612e560d3660d7dd9ae68234.zip
FreeBSD-src-ffc1f10e0bf55eb5612e560d3660d7dd9ae68234.tar.gz
Reoganize/update the SysKonnect driver:
- Break out the support for the XMAC II's PHY into an miibus driver. - Reorganize the probe/attach stuff using newbus. Each XMAC is now attached to the parent GEnesis controller using newbus. This is necessary since each XMAC must also have an attached miibus, and the miibus read/write register routines need to be able to get at the softc struct for each XMAC, not the one for the parent controller. This allows me to get rid of the grotty code I added for selecting the unit numbers for the ifnet interfaces: the unit numbers are now derived from the newbus-assigned unit numbers, which should track with the ifnet interface numbers. I think. At the very least, there should never be any collisions. - Add support for the SK-9821 and SK-9822 1000baseTX adapters. Special thanks to SysKonnect for loaning me two adapters for testing.
Diffstat (limited to 'sys/pci')
-rw-r--r--sys/pci/if_sk.c603
-rw-r--r--sys/pci/if_skreg.h57
-rw-r--r--sys/pci/xmaciireg.h14
3 files changed, 513 insertions, 161 deletions
diff --git a/sys/pci/if_sk.c b/sys/pci/if_sk.c
index edb941f..94230cc 100644
--- a/sys/pci/if_sk.c
+++ b/sys/pci/if_sk.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 1998, 1999
+ * Copyright (c) 1997, 1998, 1999, 2000
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,9 +36,14 @@
* SysKonnect SK-NET gigabit ethernet driver for FreeBSD. Supports
* the SK-984x series adapters, both single port and dual port.
* References:
- * The XaQti XMAC II datasheet, http://www.xaqti.com
+ * The XaQti XMAC II datasheet,
+ * http://www.freebsd.org/~wpaul/SysKonnect/xmacii_datasheet_rev_c_9-29.pdf
* The SysKonnect GEnesis manual, http://www.syskonnect.com
*
+ * Note: XaQti has been aquired by Vitesse, and Vitesse does not have the
+ * XMAC II datasheet online. I have put my copy at www.freebsd.org as a
+ * convenience to others until Vitesse corrects this problem.
+ *
* Written by Bill Paul <wpaul@ee.columbia.edu>
* Department of Electrical Engineering
* Columbia University, New York City
@@ -88,6 +93,10 @@
#include <sys/bus.h>
#include <sys/rman.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/mii/brgphyreg.h>
+
#include <pci/pcireg.h>
#include <pci/pcivar.h>
@@ -96,6 +105,9 @@
#include <pci/if_skreg.h>
#include <pci/xmaciireg.h>
+/* "controller miibus0" required. See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
#ifndef lint
static const char rcsid[] =
"$FreeBSD$";
@@ -109,9 +121,13 @@ static struct sk_type sk_devs[] = {
static int sk_probe __P((device_t));
static int sk_attach __P((device_t));
static int sk_detach __P((device_t));
-static int sk_attach_xmac __P((struct sk_softc *, int));
+static int sk_detach_xmac __P((device_t));
+static int sk_probe_xmac __P((device_t));
+static int sk_attach_xmac __P((device_t));
+static void sk_tick __P((void *));
static void sk_intr __P((void *));
static void sk_intr_xmac __P((struct sk_if_softc *));
+static void sk_intr_bcom __P((struct sk_if_softc *));
static void sk_rxeof __P((struct sk_if_softc *));
static void sk_txeof __P((struct sk_if_softc *));
static int sk_encap __P((struct sk_if_softc *, struct mbuf *,
@@ -134,9 +150,7 @@ static void sk_jfree __P((caddr_t, u_int));
static void sk_jref __P((caddr_t, u_int));
static int sk_init_rx_ring __P((struct sk_if_softc *));
static void sk_init_tx_ring __P((struct sk_if_softc *));
-#ifdef notdef
static u_int32_t sk_win_read_4 __P((struct sk_softc *, int));
-#endif
static u_int16_t sk_win_read_2 __P((struct sk_softc *, int));
static u_int8_t sk_win_read_1 __P((struct sk_softc *, int));
static void sk_win_write_4 __P((struct sk_softc *, int, u_int32_t));
@@ -146,8 +160,11 @@ static u_int8_t sk_vpd_readbyte __P((struct sk_softc *, int));
static void sk_vpd_read_res __P((struct sk_softc *,
struct vpd_res *, int));
static void sk_vpd_read __P((struct sk_softc *));
-static u_int16_t sk_phy_readreg __P((struct sk_if_softc *, int));
-static void sk_phy_writereg __P((struct sk_if_softc *, int, u_int32_t));
+
+static int sk_miibus_readreg __P((device_t, int, int));
+static int sk_miibus_writereg __P((device_t, int, int, int));
+static void sk_miibus_statchg __P((device_t));
+
static u_int32_t sk_calchash __P((caddr_t));
static void sk_setfilt __P((struct sk_if_softc *, caddr_t, int));
static void sk_setmulti __P((struct sk_if_softc *));
@@ -160,24 +177,66 @@ static void sk_setmulti __P((struct sk_if_softc *));
#define SK_RID SK_PCI_LOMEM
#endif
-static device_method_t sk_methods[] = {
+/*
+ * Note that we have newbus methods for both the GEnesis controller
+ * itself and the XMAC(s). The XMACs are children of the GEnesis, and
+ * the miibus code is a child of the XMACs. We need to do it this way
+ * so that the miibus drivers can access the PHY registers on the
+ * right PHY. It's not quite what I had in mind, but it's the only
+ * design that achieves the desired effect.
+ */
+static device_method_t skc_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, sk_probe),
DEVMETHOD(device_attach, sk_attach),
DEVMETHOD(device_detach, sk_detach),
DEVMETHOD(device_shutdown, sk_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
{ 0, 0 }
};
-static driver_t sk_driver = {
+static driver_t skc_driver = {
"skc",
- sk_methods,
+ skc_methods,
sizeof(struct sk_softc)
};
+static devclass_t skc_devclass;
+
+static device_method_t sk_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, sk_probe_xmac),
+ DEVMETHOD(device_attach, sk_attach_xmac),
+ DEVMETHOD(device_detach, sk_detach_xmac),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+
+ /* MII interface */
+ DEVMETHOD(miibus_readreg, sk_miibus_readreg),
+ DEVMETHOD(miibus_writereg, sk_miibus_writereg),
+ DEVMETHOD(miibus_statchg, sk_miibus_statchg),
+
+ { 0, 0 }
+};
+
+static driver_t sk_driver = {
+ "sk",
+ sk_methods,
+ sizeof(struct sk_if_softc)
+};
+
static devclass_t sk_devclass;
-DRIVER_MODULE(if_sk, pci, sk_driver, sk_devclass, 0, 0);
+DRIVER_MODULE(if_sk, pci, skc_driver, skc_devclass, 0, 0);
+DRIVER_MODULE(sk, skc, sk_driver, sk_devclass, 0, 0);
+DRIVER_MODULE(miibus, sk, miibus_driver, miibus_devclass, 0, 0);
#define SK_SETBIT(sc, reg, x) \
CSR_WRITE_4(sc, reg, CSR_READ_4(sc, reg) | x)
@@ -197,7 +256,6 @@ DRIVER_MODULE(if_sk, pci, sk_driver, sk_devclass, 0, 0);
#define SK_WIN_CLRBIT_2(sc, reg, x) \
sk_win_write_2(sc, reg, sk_win_read_2(sc, reg) & ~x)
-#ifdef notdef
static u_int32_t sk_win_read_4(sc, reg)
struct sk_softc *sc;
int reg;
@@ -205,7 +263,6 @@ static u_int32_t sk_win_read_4(sc, reg)
CSR_WRITE_4(sc, SK_RAP, SK_WIN(reg));
return(CSR_READ_4(sc, SK_WIN_BASE + SK_REG(reg)));
}
-#endif
static u_int16_t sk_win_read_2(sc, reg)
struct sk_softc *sc;
@@ -343,34 +400,48 @@ static void sk_vpd_read(sc)
return;
}
-static u_int16_t sk_phy_readreg(sc_if, reg)
- struct sk_if_softc *sc_if;
- int reg;
+static int sk_miibus_readreg(dev, phy, reg)
+ device_t dev;
+ int phy, reg;
{
+ struct sk_if_softc *sc_if;
int i;
- SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg);
- for (i = 0; i < SK_TIMEOUT; i++) {
- if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY))
- break;
- }
+ sc_if = device_get_softc(dev);
- if (i == SK_TIMEOUT) {
- printf("sk%d: phy failed to come ready\n", sc_if->sk_unit);
+ if (sc_if->sk_phytype == SK_PHYTYPE_XMAC && phy != 0)
return(0);
- }
+ SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8));
+ SK_XM_READ_2(sc_if, XM_PHY_DATA);
+ if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) {
+ for (i = 0; i < SK_TIMEOUT; i++) {
+ DELAY(1);
+ if (SK_XM_READ_2(sc_if, XM_MMUCMD) &
+ XM_MMUCMD_PHYDATARDY)
+ break;
+ }
+
+ if (i == SK_TIMEOUT) {
+ printf("sk%d: phy failed to come ready\n",
+ sc_if->sk_unit);
+ return(0);
+ }
+ }
+ DELAY(1);
return(SK_XM_READ_2(sc_if, XM_PHY_DATA));
}
-static void sk_phy_writereg(sc_if, reg, val)
- struct sk_if_softc *sc_if;
- int reg;
- u_int32_t val;
+static int sk_miibus_writereg(dev, phy, reg, val)
+ device_t dev;
+ int phy, reg, val;
{
+ struct sk_if_softc *sc_if;
int i;
- SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg);
+ sc_if = device_get_softc(dev);
+
+ SK_XM_WRITE_2(sc_if, XM_PHY_ADDR, reg|(phy << 8));
for (i = 0; i < SK_TIMEOUT; i++) {
if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY))
break;
@@ -378,11 +449,12 @@ static void sk_phy_writereg(sc_if, reg, val)
if (i == SK_TIMEOUT) {
printf("sk%d: phy failed to come ready\n", sc_if->sk_unit);
- return;
+ return(ETIMEDOUT);
}
SK_XM_WRITE_2(sc_if, XM_PHY_DATA, val);
for (i = 0; i < SK_TIMEOUT; i++) {
+ DELAY(1);
if (!(SK_XM_READ_2(sc_if, XM_MMUCMD) & XM_MMUCMD_PHYBUSY))
break;
}
@@ -390,6 +462,30 @@ static void sk_phy_writereg(sc_if, reg, val)
if (i == SK_TIMEOUT)
printf("sk%d: phy write timed out\n", sc_if->sk_unit);
+ return(0);
+}
+
+static void sk_miibus_statchg(dev)
+ device_t dev;
+{
+ struct sk_if_softc *sc_if;
+ struct mii_data *mii;
+
+ sc_if = device_get_softc(dev);
+ mii = device_get_softc(sc_if->sk_miibus);
+
+ /*
+ * If this is a GMII PHY, manually set the XMAC's
+ * duplex mode accordingly.
+ */
+ if (sc_if->sk_phytype != SK_PHYTYPE_XMAC) {
+ if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
+ SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX);
+ } else {
+ SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_GMIIFDX);
+ }
+ }
+
return;
}
@@ -803,33 +899,12 @@ static int sk_ifmedia_upd(ifp)
struct ifnet *ifp;
{
struct sk_if_softc *sc_if;
- struct ifmedia *ifm;
+ struct mii_data *mii;
sc_if = ifp->if_softc;
- ifm = &sc_if->ifmedia;
-
- if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
- return(EINVAL);
-
- switch(IFM_SUBTYPE(ifm->ifm_media)) {
- case IFM_AUTO:
- sk_phy_writereg(sc_if, XM_PHY_BMCR,
- XM_BMCR_RENEGOTIATE|XM_BMCR_AUTONEGENBL);
- break;
- case IFM_1000_LX:
- case IFM_1000_SX:
- case IFM_1000_CX:
- case IFM_1000_TX:
- if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
- sk_phy_writereg(sc_if, XM_PHY_BMCR, XM_BMCR_DUPLEX);
- else
- sk_phy_writereg(sc_if, XM_PHY_BMCR, 0);
- break;
- default:
- printf("sk%d: invalid media selected\n", sc_if->sk_unit);
- return(EINVAL);
- break;
- }
+ mii = device_get_softc(sc_if->sk_miibus);
+ sk_init(sc_if);
+ mii_mediachg(mii);
return(0);
}
@@ -841,28 +916,15 @@ static void sk_ifmedia_sts(ifp, ifmr)
struct ifnet *ifp;
struct ifmediareq *ifmr;
{
- struct sk_softc *sc;
struct sk_if_softc *sc_if;
- u_int16_t bmsr, extsts;
+ struct mii_data *mii;
sc_if = ifp->if_softc;
- sc = sc_if->sk_softc;
+ mii = device_get_softc(sc_if->sk_miibus);
- ifmr->ifm_status = IFM_AVALID;
- ifmr->ifm_active = IFM_ETHER;
-
- bmsr = sk_phy_readreg(sc_if, XM_PHY_BMSR);
- extsts = sk_phy_readreg(sc_if, XM_PHY_EXTSTS);
-
- if (!(bmsr & XM_BMSR_LINKSTAT))
- return;
-
- ifmr->ifm_status |= IFM_ACTIVE;
- ifmr->ifm_active |= sc->sk_pmd;;
- if (extsts & XM_EXTSTS_FULLDUPLEX)
- ifmr->ifm_active |= IFM_FDX;
- else
- ifmr->ifm_active |= IFM_HDX;
+ mii_pollstat(mii);
+ ifmr->ifm_active = mii->mii_media_active;
+ ifmr->ifm_status = mii->mii_media_status;
return;
}
@@ -875,6 +937,7 @@ static int sk_ioctl(ifp, command, data)
struct sk_if_softc *sc_if = ifp->if_softc;
struct ifreq *ifr = (struct ifreq *) data;
int s, error = 0;
+ struct mii_data *mii;
s = splimp();
@@ -921,7 +984,8 @@ static int sk_ioctl(ifp, command, data)
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
- error = ifmedia_ioctl(ifp, ifr, &sc_if->ifmedia, command);
+ mii = device_get_softc(sc_if->sk_miibus);
+ error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
break;
default:
error = EINVAL;
@@ -994,45 +1058,46 @@ static void sk_reset(sc)
return;
}
+static int sk_probe_xmac(dev)
+ device_t dev;
+{
+ /*
+ * Not much to do here. We always know there will be
+ * at least one XMAC present, and if there are two,
+ * sk_attach() will create a second device instance
+ * for us.
+ */
+ device_set_desc(dev, "XaQti Corp. XMAC II");
+
+ return(0);
+}
+
/*
* Each XMAC chip is attached as a separate logical IP interface.
* Single port cards will have only one logical interface of course.
*/
-static int sk_attach_xmac(sc, port)
- struct sk_softc *sc;
- int port;
+static int sk_attach_xmac(dev)
+ device_t dev;
{
+ struct sk_softc *sc;
struct sk_if_softc *sc_if;
struct ifnet *ifp;
- int i;
- char ifname[64];
+ int i, port;
- if (sc == NULL)
+ if (dev == NULL)
return(EINVAL);
- if (port != SK_PORT_A && port != SK_PORT_B)
- return(EINVAL);
+ sc_if = device_get_softc(dev);
+ sc = device_get_softc(device_get_parent(dev));
+ port = *(int *)device_get_ivars(dev);
+ free(device_get_ivars(dev), M_DEVBUF);
+ device_set_ivars(dev, NULL);
+ sc_if->sk_dev = dev;
- sc_if = malloc(sizeof(struct sk_if_softc), M_DEVBUF, M_NOWAIT);
- if (sc_if == NULL) {
- printf("skc%d: no memory for interface softc!\n", sc->sk_unit);
- return(ENOMEM);
- }
bzero((char *)sc_if, sizeof(struct sk_if_softc));
- for (i = 0; i < SK_MAXUNIT; i++) {
- sprintf(ifname, "sk%d", i);
- if (ifunit(ifname) == NULL)
- break;
- }
-
- if (i == SK_MAXUNIT) {
- printf("skc%d: too many sk units\n", sc->sk_unit);
- free(sc_if, M_DEVBUF);
- return(ENODEV);
- }
-
- sc_if->sk_unit = i;
+ sc_if->sk_dev = dev;
+ sc_if->sk_unit = device_get_unit(dev);
sc_if->sk_port = port;
sc_if->sk_softc = sc;
sc->sk_if[port] = sc_if;
@@ -1054,9 +1119,6 @@ static int sk_attach_xmac(sc, port)
sc_if->arpcom.ac_enaddr[i] =
sk_win_read_1(sc, SK_MAC0_0 + (port * 8) + i);
- printf("sk%d: <XaQti Corp. XMAC II> at skc%d port %d\n",
- sc_if->sk_unit, sc->sk_unit, port);
-
printf("sk%d: Ethernet address: %6D\n",
sc_if->sk_unit, sc_if->arpcom.ac_enaddr, ":");
@@ -1093,13 +1155,27 @@ static int sk_attach_xmac(sc, port)
sc_if->sk_tx_ramend = val - 1;
}
+ /* Read and save PHY type and set PHY address */
+ sc_if->sk_phytype = sk_win_read_1(sc, SK_EPROM1) & 0xF;
+ switch(sc_if->sk_phytype) {
+ case SK_PHYTYPE_XMAC:
+ sc_if->sk_phyaddr = SK_PHYADDR_XMAC;
+ break;
+ case SK_PHYTYPE_BCOM:
+ sc_if->sk_phyaddr = SK_PHYADDR_BCOM;
+ break;
+ default:
+ printf("skc%d: unsupported PHY type: %d\n",
+ sc->sk_unit, sc_if->sk_phytype);
+ return(ENODEV);
+ }
+
/* Allocate the descriptor queues. */
sc_if->sk_rdata = contigmalloc(sizeof(struct sk_ring_data), M_DEVBUF,
M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
if (sc_if->sk_rdata == NULL) {
printf("sk%d: no memory for list buffers!\n", sc_if->sk_unit);
- free(sc_if, M_DEVBUF);
sc->sk_if[port] = NULL;
return(ENOMEM);
}
@@ -1110,8 +1186,8 @@ static int sk_attach_xmac(sc, port)
if (sk_alloc_jumbo_mem(sc_if)) {
printf("sk%d: jumbo buffer allocation failed\n",
sc_if->sk_unit);
- free(sc_if->sk_rdata, M_DEVBUF);
- free(sc_if, M_DEVBUF);
+ contigfree(sc_if->sk_rdata,
+ sizeof(struct sk_ring_data), M_DEVBUF);
sc->sk_if[port] = NULL;
return(ENOMEM);
}
@@ -1131,21 +1207,23 @@ static int sk_attach_xmac(sc, port)
ifp->if_snd.ifq_maxlen = SK_TX_RING_CNT - 1;
/*
- * Do ifmedia setup.
+ * Do miibus setup.
*/
- ifmedia_init(&sc_if->ifmedia, 0, sk_ifmedia_upd, sk_ifmedia_sts);
- ifmedia_add(&sc_if->ifmedia, IFM_ETHER|sc->sk_pmd, 0, NULL);
- ifmedia_add(&sc_if->ifmedia, IFM_ETHER|sc->sk_pmd|IFM_FDX, 0, NULL);
- ifmedia_add(&sc_if->ifmedia, IFM_ETHER|sc->sk_pmd|IFM_HDX, 0, NULL);
- ifmedia_add(&sc_if->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
- ifmedia_set(&sc_if->ifmedia, IFM_ETHER|IFM_AUTO);
+ sk_init_xmac(sc_if);
+ if (mii_phy_probe(dev, &sc_if->sk_miibus,
+ sk_ifmedia_upd, sk_ifmedia_sts)) {
+ printf("skc%d: no PHY found!\n", sc_if->sk_unit);
+ contigfree(sc_if->sk_rdata,
+ sizeof(struct sk_ring_data), M_DEVBUF);
+ return(ENXIO);
+ }
/*
* Call MI attach routines.
*/
if_attach(ifp);
ether_ifattach(ifp);
-
+ callout_handle_init(&sc_if->sk_tick_ch);
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
return(0);
@@ -1161,7 +1239,7 @@ static int sk_attach(dev)
int s;
u_int32_t command;
struct sk_softc *sc;
- int unit, error = 0, rid;
+ int unit, error = 0, rid, *port;
s = splimp();
@@ -1317,10 +1395,17 @@ static int sk_attach(dev)
/* Announce the product name. */
printf("skc%d: %s\n", sc->sk_unit, sc->sk_vpd_prodname);
-
- sk_attach_xmac(sc, SK_PORT_A);
- if (!(sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC))
- sk_attach_xmac(sc, SK_PORT_B);
+ sc->sk_devs[SK_PORT_A] = device_add_child(dev, "sk", -1);
+ port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT);
+ *port = SK_PORT_A;
+ device_set_ivars(sc->sk_devs[SK_PORT_A], port);
+
+ if (!(sk_win_read_1(sc, SK_CONFIG) & SK_CONFIG_SINGLEMAC)) {
+ sc->sk_devs[SK_PORT_B] = device_add_child(dev, "sk", -1);
+ port = malloc(sizeof(int), M_DEVBUF, M_NOWAIT);
+ *port = SK_PORT_B;
+ device_set_ivars(sc->sk_devs[SK_PORT_B], port);
+ }
/* Turn on the 'driver is loaded' LED. */
CSR_WRITE_2(sc, SK_LED, SK_LED_GREEN_ON);
@@ -1330,33 +1415,45 @@ fail:
return(error);
}
+static int sk_detach_xmac(dev)
+ device_t dev;
+{
+ struct sk_softc *sc;
+ struct sk_if_softc *sc_if;
+ struct ifnet *ifp;
+ int s;
+
+ s = splimp();
+
+ sc = device_get_softc(device_get_parent(dev));
+ sc_if = device_get_softc(dev);
+ ifp = &sc_if->arpcom.ac_if;
+ sk_stop(sc_if);
+ if_detach(ifp);
+ bus_generic_detach(dev);
+ if (sc_if->sk_miibus != NULL)
+ device_delete_child(dev, sc_if->sk_miibus);
+ contigfree(sc_if->sk_cdata.sk_jumbo_buf, SK_JMEM, M_DEVBUF);
+ contigfree(sc_if->sk_rdata, sizeof(struct sk_ring_data), M_DEVBUF);
+
+ return(0);
+}
+
static int sk_detach(dev)
device_t dev;
{
struct sk_softc *sc;
- struct sk_if_softc *sc_if0 = NULL, *sc_if1 = NULL;
- struct ifnet *ifp0 = NULL, *ifp1 = NULL;
int s;
s = splimp();
sc = device_get_softc(dev);
- sc_if0 = sc->sk_if[SK_PORT_A];
- ifp0 = &sc_if0->arpcom.ac_if;
- sk_stop(sc_if0);
- if_detach(ifp0);
- contigfree(sc_if0->sk_cdata.sk_jumbo_buf, SK_JMEM, M_DEVBUF);
- ifmedia_removeall(&sc_if0->ifmedia);
- free(sc->sk_if[SK_PORT_A], M_DEVBUF);
- if (sc->sk_if[SK_PORT_B] != NULL) {
- sc_if1 = sc->sk_if[SK_PORT_B];
- ifp1 = &sc_if1->arpcom.ac_if;
- sk_stop(sc_if1);
- if_detach(ifp1);
- contigfree(sc_if1->sk_cdata.sk_jumbo_buf, SK_JMEM, M_DEVBUF);
- ifmedia_removeall(&sc_if1->ifmedia);
- free(sc->sk_if[SK_PORT_B], M_DEVBUF);
- }
+
+ bus_generic_detach(dev);
+ if (sc->sk_devs[SK_PORT_A] != NULL)
+ device_delete_child(dev, sc->sk_devs[SK_PORT_A]);
+ if (sc->sk_devs[SK_PORT_B] != NULL)
+ device_delete_child(dev, sc->sk_devs[SK_PORT_B]);
bus_teardown_intr(dev, sc->sk_irq, sc->sk_intrhand);
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sk_irq);
@@ -1612,30 +1709,132 @@ static void sk_txeof(sc_if)
return;
}
+static void sk_tick(xsc_if)
+ void *xsc_if;
+{
+ struct sk_if_softc *sc_if;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+ int i;
+
+ sc_if = xsc_if;
+ ifp = &sc_if->arpcom.ac_if;
+ mii = device_get_softc(sc_if->sk_miibus);
+
+ if (!(ifp->if_flags & IFF_UP))
+ return;
+
+ if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) {
+ sk_intr_bcom(sc_if);
+ return;
+ }
+
+ /*
+ * According to SysKonnect, the correct way to verify that
+ * the link has come back up is to poll bit 0 of the GPIO
+ * register three times. This pin has the signal from the
+ * link_sync pin connected to it; if we read the same link
+ * state 3 times in a row, we know the link is up.
+ */
+ for (i = 0; i < 3; i++) {
+ if (SK_XM_READ_2(sc_if, XM_GPIO) & XM_GPIO_GP0_SET)
+ break;
+ }
+
+ if (i != 3) {
+ sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz);
+ return;
+ }
+
+ /* Turn the GP0 interrupt back on. */
+ SK_XM_CLRBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET);
+ SK_XM_READ_2(sc_if, XM_ISR);
+ mii_tick(mii);
+ mii_pollstat(mii);
+ untimeout(sk_tick, sc_if, sc_if->sk_tick_ch);
+
+ return;
+}
+
+static void sk_intr_bcom(sc_if)
+ struct sk_if_softc *sc_if;
+{
+ struct sk_softc *sc;
+ struct mii_data *mii;
+ struct ifnet *ifp;
+ int status;
+
+ sc = sc_if->sk_softc;
+ mii = device_get_softc(sc_if->sk_miibus);
+ ifp = &sc_if->arpcom.ac_if;
+
+ SK_XM_CLRBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB);
+
+ /*
+ * Read the PHY interrupt register to make sure
+ * we clear any pending interrupts.
+ */
+ status = sk_miibus_readreg(sc_if->sk_dev,
+ SK_PHYADDR_BCOM, BRGPHY_MII_ISR);
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ sk_init_xmac(sc_if);
+ return;
+ }
+
+ if (status & (BRGPHY_ISR_LNK_CHG|BRGPHY_ISR_AN_PR)) {
+ int lstat;
+ lstat = sk_miibus_readreg(sc_if->sk_dev,
+ SK_PHYADDR_BCOM, BRGPHY_MII_AUXSTS);
+
+ if (!(lstat & BRGPHY_AUXSTS_LINK) && sc_if->sk_link) {
+ mii_mediachg(mii);
+ /* Turn off the link LED. */
+ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL, SK_LINKLED_OFF);
+ sc_if->sk_link = 0;
+ } else if (status & BRGPHY_ISR_LNK_CHG) {
+ sk_miibus_writereg(sc_if->sk_dev, SK_PHYADDR_BCOM,
+ BRGPHY_MII_IMR, 0xFF00);
+ mii_tick(mii);
+ sc_if->sk_link = 1;
+ /* Turn on the link LED. */
+ SK_IF_WRITE_1(sc_if, 0, SK_LINKLED1_CTL,
+ SK_LINKLED_ON|SK_LINKLED_LINKSYNC_OFF|
+ SK_LINKLED_BLINK_OFF);
+ } else {
+ mii_tick(mii);
+ sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz);
+ }
+ }
+
+ SK_XM_SETBIT_2(sc_if, XM_MMUCMD, XM_MMUCMD_TX_ENB|XM_MMUCMD_RX_ENB);
+
+ return;
+}
+
static void sk_intr_xmac(sc_if)
struct sk_if_softc *sc_if;
{
struct sk_softc *sc;
u_int16_t status;
- u_int16_t bmsr;
+ struct mii_data *mii;
sc = sc_if->sk_softc;
+ mii = device_get_softc(sc_if->sk_miibus);
status = SK_XM_READ_2(sc_if, XM_ISR);
- if (status & XM_ISR_LINKEVENT) {
- SK_XM_SETBIT_2(sc_if, XM_IMR, XM_IMR_LINKEVENT);
- if (sc_if->sk_link == 1) {
- printf("sk%d: gigabit link down\n", sc_if->sk_unit);
- sc_if->sk_link = 0;
+ /*
+ * Link has gone down. Start MII tick timeout to
+ * watch for link resync.
+ */
+ if (sc_if->sk_phytype == SK_PHYTYPE_XMAC) {
+ if (status & XM_ISR_GP0_SET) {
+ SK_XM_SETBIT_2(sc_if, XM_IMR, XM_IMR_GP0_SET);
+ sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz);
}
- }
- if (status & XM_ISR_AUTONEG_DONE) {
- bmsr = sk_phy_readreg(sc_if, XM_PHY_BMSR);
- if (bmsr & XM_BMSR_LINKSTAT) {
- sc_if->sk_link = 1;
- SK_XM_CLRBIT_2(sc_if, XM_IMR, XM_IMR_LINKEVENT);
- printf("sk%d: gigabit link up\n", sc_if->sk_unit);
+ if (status & XM_ISR_AUTONEG_DONE) {
+ sc_if->sk_tick_ch = timeout(sk_tick, sc_if, hz);
}
}
@@ -1645,6 +1844,8 @@ static void sk_intr_xmac(sc_if)
if (status & XM_IMR_RX_OVERRUN)
SK_XM_SETBIT_4(sc_if, XM_MODE, XM_MODE_FLUSH_RXFIFO);
+ status = SK_XM_READ_2(sc_if, XM_ISR);
+
return;
}
@@ -1662,7 +1863,7 @@ static void sk_intr(xsc)
if (sc_if0 != NULL)
ifp0 = &sc_if0->arpcom.ac_if;
if (sc_if1 != NULL)
- ifp1 = &sc_if0->arpcom.ac_if;
+ ifp1 = &sc_if1->arpcom.ac_if;
for (;;) {
status = CSR_READ_4(sc, SK_ISSR);
@@ -1694,11 +1895,20 @@ static void sk_intr(xsc)
}
/* Then MAC interrupts. */
- if (status & SK_ISR_MAC1)
+ if (status & SK_ISR_MAC1 &&
+ ifp0->if_flags & IFF_RUNNING)
sk_intr_xmac(sc_if0);
- if (status & SK_ISR_MAC2)
+ if (status & SK_ISR_MAC2 &&
+ ifp1->if_flags & IFF_RUNNING)
sk_intr_xmac(sc_if1);
+
+ if (status & SK_ISR_EXTERNAL_REG) {
+ if (ifp0 != NULL)
+ sk_intr_bcom(sc_if0);
+ if (ifp1 != NULL)
+ sk_intr_bcom(sc_if1);
+ }
}
CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask);
@@ -1716,6 +1926,11 @@ static void sk_init_xmac(sc_if)
{
struct sk_softc *sc;
struct ifnet *ifp;
+ struct sk_bcom_hack bhack[] = {
+ { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 }, { 0x17, 0x0013 },
+ { 0x15, 0x0404 }, { 0x17, 0x8006 }, { 0x15, 0x0132 }, { 0x17, 0x8006 },
+ { 0x15, 0x0232 }, { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 },
+ { 0, 0 } };
sc = sc_if->sk_softc;
ifp = &sc_if->arpcom.ac_if;
@@ -1724,9 +1939,55 @@ static void sk_init_xmac(sc_if)
SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_UNRESET);
DELAY(1000);
+ /* Reset the XMAC's internal state. */
+ SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC);
+
/* Save the XMAC II revision */
sc_if->sk_xmac_rev = XM_XMAC_REV(SK_XM_READ_4(sc_if, XM_DEVID));
+ /*
+ * Perform additional initialization for external PHYs,
+ * namely for the 1000baseTX cards that use the XMAC's
+ * GMII mode.
+ */
+ if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) {
+ int i = 0;
+ u_int32_t val;
+
+ /* Take PHY out of reset. */
+ val = sk_win_read_4(sc, SK_GPIO);
+ if (sc_if->sk_port == SK_PORT_A)
+ val |= SK_GPIO_DIR0|SK_GPIO_DAT0;
+ else
+ val |= SK_GPIO_DIR2|SK_GPIO_DAT2;
+ sk_win_write_4(sc, SK_GPIO, val);
+
+ /* Enable GMII mode on the XMAC. */
+ SK_XM_SETBIT_2(sc_if, XM_HWCFG, XM_HWCFG_GMIIMODE);
+
+ sk_miibus_writereg(sc_if->sk_dev, SK_PHYADDR_BCOM,
+ BRGPHY_MII_BMCR, BRGPHY_BMCR_RESET);
+ DELAY(10000);
+ sk_miibus_writereg(sc_if->sk_dev, SK_PHYADDR_BCOM,
+ BRGPHY_MII_IMR, 0xFFF0);
+
+ /*
+ * Early versions of the BCM5400 apparently have
+ * a bug that requires them to have their reserved
+ * registers initialized to some magic values. I don't
+ * know what the numbers do, I'm just the messenger.
+ */
+ if (sk_miibus_readreg(sc_if->sk_dev,
+ SK_PHYADDR_BCOM, 0x03) == 0x6041) {
+ while(bhack[i].reg) {
+ sk_miibus_writereg(sc_if->sk_dev,
+ SK_PHYADDR_BCOM, bhack[i].reg,
+ bhack[i].val);
+ i++;
+ }
+ }
+ }
+
/* Set station address */
SK_XM_WRITE_2(sc_if, XM_PAR0,
*(u_int16_t *)(&sc_if->arpcom.ac_enaddr[0]));
@@ -1788,9 +2049,10 @@ static void sk_init_xmac(sc_if)
/* Clear and enable interrupts */
SK_XM_READ_2(sc_if, XM_ISR);
- SK_XM_WRITE_2(sc_if, XM_IMR, XM_INTRS);
-
- sc_if->sk_link = 0;
+ if (sc_if->sk_phytype == SK_PHYTYPE_XMAC)
+ SK_XM_WRITE_2(sc_if, XM_IMR, XM_INTRS);
+ else
+ SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF);
/* Configure MAC arbiter */
switch(sc_if->sk_xmac_rev) {
@@ -1822,6 +2084,8 @@ static void sk_init_xmac(sc_if)
sk_win_write_2(sc, SK_MACARB_CTL,
SK_MACARBCTL_UNRESET|SK_MACARBCTL_FASTOE_OFF);
+ sc_if->sk_link = 1;
+
return;
}
@@ -1835,12 +2099,14 @@ static void sk_init(xsc)
struct sk_if_softc *sc_if = xsc;
struct sk_softc *sc;
struct ifnet *ifp;
+ struct mii_data *mii;
int s;
s = splimp();
ifp = &sc_if->arpcom.ac_if;
sc = sc_if->sk_softc;
+ mii = device_get_softc(sc_if->sk_miibus);
/* Cancel pending I/O and free all RX/TX buffers. */
sk_stop(sc_if);
@@ -1859,6 +2125,7 @@ static void sk_init(xsc)
/* Configure XMAC(s) */
sk_init_xmac(sc_if);
+ mii_mediachg(mii);
/* Configure MAC FIFOs */
SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_UNRESET);
@@ -1916,6 +2183,9 @@ static void sk_init(xsc)
sc->sk_intrmask |= SK_INTRS1;
else
sc->sk_intrmask |= SK_INTRS2;
+
+ sc->sk_intrmask |= SK_ISR_EXTERNAL_REG;
+
CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask);
/* Start BMUs. */
@@ -1942,7 +2212,25 @@ static void sk_stop(sc_if)
sc = sc_if->sk_softc;
ifp = &sc_if->arpcom.ac_if;
+ untimeout(sk_tick, sc_if, sc_if->sk_tick_ch);
+
+ if (sc_if->sk_phytype == SK_PHYTYPE_BCOM) {
+ u_int32_t val;
+
+ /* Put PHY back into reset. */
+ val = sk_win_read_4(sc, SK_GPIO);
+ if (sc_if->sk_port == SK_PORT_A) {
+ val |= SK_GPIO_DIR0;
+ val &= ~SK_GPIO_DAT0;
+ } else {
+ val |= SK_GPIO_DIR2;
+ val &= ~SK_GPIO_DAT2;
+ }
+ sk_win_write_4(sc, SK_GPIO, val);
+ }
+
/* Turn off various components of this interface. */
+ SK_XM_SETBIT_2(sc_if, XM_GPIO, XM_GPIO_RESETMAC);
SK_IF_WRITE_2(sc_if, 0, SK_TXF1_MACCTL, SK_TXMACCTL_XMAC_RESET);
SK_IF_WRITE_4(sc_if, 0, SK_RXF1_CTL, SK_FIFO_RESET);
SK_IF_WRITE_4(sc_if, 0, SK_RXQ1_BMU_CSR, SK_RXBMU_OFFLINE);
@@ -1962,6 +2250,9 @@ static void sk_stop(sc_if)
sc->sk_intrmask &= ~SK_INTRS2;
CSR_WRITE_4(sc, SK_IMR, sc->sk_intrmask);
+ SK_XM_READ_2(sc_if, XM_ISR);
+ SK_XM_WRITE_2(sc_if, XM_IMR, 0xFFFF);
+
/* Free RX and TX mbufs still in the queues. */
for (i = 0; i < SK_RX_RING_CNT; i++) {
if (sc_if->sk_cdata.sk_rx_chain[i].sk_mbuf != NULL) {
diff --git a/sys/pci/if_skreg.h b/sys/pci/if_skreg.h
index f307636..af17c1c 100644
--- a/sys/pci/if_skreg.h
+++ b/sys/pci/if_skreg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 1998, 1999
+ * Copyright (c) 1997, 1998, 1999, 2000
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -319,6 +319,24 @@
#define SK_RBOFF_0 0x0
#define SK_RBOFF_80000 0x80000
+/*
+ * SK_EEPROM1 contains the PHY type, which may be XMAC for
+ * fiber-based cards or BCOM for 1000baseT cards with a Broadcom
+ * PHY.
+ */
+#define SK_PHYTYPE_XMAC 0 /* integeated XMAC II PHY */
+#define SK_PHYTYPE_BCOM 1 /* Broadcom BCM5400 */
+#define SK_PHYTYPE_LONE 2 /* Level One LXT1000 */
+#define SK_PHYTYPE_NAT 3 /* National DP83891 */
+
+/*
+ * PHY addresses.
+ */
+#define SK_PHYADDR_XMAC 0x0
+#define SK_PHYADDR_BCOM 0x1
+#define SK_PHYADDR_LONE 0x3
+#define SK_PHYADDR_NAT 0x0
+
#define SK_CONFIG_SINGLEMAC 0x01
#define SK_CONFIG_DIS_DSL_CLK 0x02
@@ -327,6 +345,28 @@
#define SK_PMD_1000BASECX 0x43
#define SK_PMD_1000BASETX 0x54
+/* GPIO bits */
+#define SK_GPIO_DAT0 0x00000001
+#define SK_GPIO_DAT1 0x00000002
+#define SK_GPIO_DAT2 0x00000004
+#define SK_GPIO_DAT3 0x00000008
+#define SK_GPIO_DAT4 0x00000010
+#define SK_GPIO_DAT5 0x00000020
+#define SK_GPIO_DAT6 0x00000040
+#define SK_GPIO_DAT7 0x00000080
+#define SK_GPIO_DAT8 0x00000100
+#define SK_GPIO_DAT9 0x00000200
+#define SK_GPIO_DIR0 0x00010000
+#define SK_GPIO_DIR1 0x00020000
+#define SK_GPIO_DIR2 0x00040000
+#define SK_GPIO_DIR3 0x00080000
+#define SK_GPIO_DIR4 0x00100000
+#define SK_GPIO_DIR5 0x00200000
+#define SK_GPIO_DIR6 0x00400000
+#define SK_GPIO_DIR7 0x00800000
+#define SK_GPIO_DIR8 0x01000000
+#define SK_GPIO_DIR9 0x02000000
+
/* Block 3 Ram interface and MAC arbiter registers */
#define SK_RAMADDR 0x0180
#define SK_RAMDATA0 0x0184
@@ -1118,6 +1158,11 @@ struct sk_ring_data {
struct sk_rx_desc sk_rx_ring[SK_RX_RING_CNT];
};
+struct sk_bcom_hack {
+ int reg;
+ int val;
+};
+
#define SK_INC(x, y) (x) = (x + 1) % y
/* Forward decl. */
@@ -1139,20 +1184,26 @@ struct sk_softc {
u_int32_t sk_pmd; /* physical media type */
u_int32_t sk_intrmask;
struct sk_if_softc *sk_if[2];
+ device_t sk_devs[2];
};
/* Softc for each logical interface */
struct sk_if_softc {
struct arpcom arpcom; /* interface info */
- struct ifmedia ifmedia; /* media info */
+ device_t sk_miibus;
u_int8_t sk_unit; /* interface number */
u_int8_t sk_port; /* port # on controller */
u_int8_t sk_xmac_rev; /* XMAC chip rev (B2 or C1) */
- u_int8_t sk_link;
u_int32_t sk_rx_ramstart;
u_int32_t sk_rx_ramend;
u_int32_t sk_tx_ramstart;
u_int32_t sk_tx_ramend;
+ int sk_phytype;
+ int sk_phyaddr;
+ device_t sk_dev;
+ int sk_cnt;
+ int sk_link;
+ struct callout_handle sk_tick_ch;
struct sk_chain_data sk_cdata;
struct sk_ring_data *sk_rdata;
struct sk_softc *sk_softc; /* parent controller */
diff --git a/sys/pci/xmaciireg.h b/sys/pci/xmaciireg.h
index f649e93..137f47ee 100644
--- a/sys/pci/xmaciireg.h
+++ b/sys/pci/xmaciireg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 1998, 1999
+ * Copyright (c) 1997, 1998, 1999, 2000
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -227,6 +227,12 @@
#define XM_RXCMD_BIGPKTOK 0x0080
#define XM_RXCMD_LENERROK 0x0100
+#define XM_GPIO_GP0_SET 0x0001
+#define XM_GPIO_RESETSTATS 0x0004
+#define XM_GPIO_RESETMAC 0x0008
+#define XM_GPIO_FORCEINT 0x0020
+#define XM_GPIO_ANEGINPROG 0x0040
+
#define XM_IMR_RX_EOF 0x0001
#define XM_IMR_TX_EOF 0x0002
#define XM_IMR_TX_UNDERRUN 0x0004
@@ -244,7 +250,7 @@
#define XM_IMR_LINKEVENT 0x4000
#define XM_INTRS \
- (~(XM_IMR_LINKEVENT|XM_IMR_AUTONEG_DONE|XM_IMR_TX_UNDERRUN))
+ (~(XM_IMR_GP0_SET|XM_IMR_AUTONEG_DONE|XM_IMR_TX_UNDERRUN))
#define XM_ISR_RX_EOF 0x0001
#define XM_ISR_TX_EOF 0x0002
@@ -262,6 +268,10 @@
#define XM_ISR_TX_ABORT 0x2000
#define XM_ISR_LINKEVENT 0x4000
+#define XM_HWCFG_GENEOP 0x0008
+#define XM_HWCFG_SIGSTATCKH 0x0004
+#define XM_HWCFG_GMIIMODE 0x0001
+
#define XM_MODE_FLUSH_RXFIFO 0x00000001
#define XM_MODE_FLUSH_TXFIFO 0x00000002
#define XM_MODE_BIGENDIAN 0x00000004
OpenPOWER on IntegriCloud