summaryrefslogtreecommitdiffstats
path: root/sys/pci/if_sk.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/pci/if_sk.c')
-rw-r--r--sys/pci/if_sk.c603
1 files changed, 447 insertions, 156 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) {
OpenPOWER on IntegriCloud