]> xenbits.xensource.com Git - people/liuw/freebsd.git/commitdiff
Add etherswitch support to mge
authorzbb <zbb@FreeBSD.org>
Sun, 25 Oct 2015 22:00:56 +0000 (22:00 +0000)
committerzbb <zbb@FreeBSD.org>
Sun, 25 Oct 2015 22:00:56 +0000 (22:00 +0000)
This commit introduces support for etherswitch devices that utilize SMI as
a way of accessing its registers. SMI register is located in address space
of mge -- access to it was exported through MDIO interface.

Attachment functions were enhanced so as to ensure proper initialisation
in both cases: 1) PHYs attached directly to mge, 2) PHYs attached to
switch device and switch attached to mge. Attachment of etherswitch device
depends on dts entry with compatible="mrvl,sw" property. If none is found,
typical PHY attachment procedure follows.

In case of switch attached, PHYs' status and configuration is accessible
via etherswitchcfg, and ifconfig shows always-up, non-configurable mge
interfaces.

Due to the fact that there may be simultaneous accessess to SMI
registers (e.g. from PHY attached to one of mge instances and switch
to the other), SMI access interlock was added. It is SX lock,
because sleep ability is necessary -- busy-waiting would result
in poor performance due to long delays required by hardware.
Underlying switch driver is obliged to use sleepable locks as well.

Reviewed by:    adrian
Obtained from:  Semihalf
Submitted by:   Bartosz Szczepanek <bsz@semihalf.com>
Differential revision: https://reviews.freebsd.org/D3900

sys/arm/conf/ARMADAXP
sys/arm/conf/DB-78XXX
sys/arm/conf/DB-88F5XXX
sys/arm/conf/DB-88F6XXX
sys/arm/conf/DOCKSTAR
sys/arm/conf/DREAMPLUG-1001
sys/arm/conf/SHEEVAPLUG
sys/arm/conf/TS7800
sys/conf/files
sys/dev/mge/if_mge.c
sys/dev/mge/if_mgevar.h

index 6d51c16ca71242ebf37ff192a04b54941c7e27bf..f1b2776cd62024b6ab303b94e64d62656db04f05 100644 (file)
@@ -95,6 +95,7 @@ device                iicbus
 device         ether
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 device         bpf
 options        DEVICE_POLLING
index e59e4264721e442fb145390dcfd7408603cb622e..024593cd33afb5447e1f63c0afecd2d42c336185 100644 (file)
@@ -66,6 +66,7 @@ device                uart
 device         ether
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 device         bpf
 
index f1729f060a1b835cfeae506271df5f581b419b17..5fd96fc92aa9783f5aca6a21df3261c85c39bbf5 100644 (file)
@@ -65,6 +65,7 @@ device                uart
 device         ether
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 device         bpf
 options        DEVICE_POLLING
index ec3eff09f5635c9a3c98922ebb69bbf6d2deb9c9..c3f238c3c94576ed7c656e6f175798521dfe1568 100644 (file)
@@ -67,6 +67,7 @@ device                uart
 device         ether
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 device         bpf
 
index b00b3bf298253dcc0126f02e56b2004a7ddd49ca..d7fa96e26cf19a6cf2bd6e3fde8889b80487cdc9 100644 (file)
@@ -96,6 +96,7 @@ device                uart
 # Networking
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 
 # USB
index 3baacaaaff4d8bfd6dba0e4333c5499655010ed6..3277b43dfdfffd640ebd1f4a6eb92a3186124643 100644 (file)
@@ -100,6 +100,7 @@ device              uart
 # Networking
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 
 # USB
index 507ba75d0ec635341987f99ae20d224f40b2fa83..5d89c2dd186e81603b51a4d126f4a22c9776e861 100644 (file)
@@ -60,6 +60,7 @@ device                uart
 device         ether
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 device         bpf
 options        DEVICE_POLLING
index 1a565c43fb6fef387c01874aa3f9dba0cdb41b1a..1b62e59ba333c4e0230469a0e163510b3205cba5 100644 (file)
@@ -60,6 +60,7 @@ device                uart
 device         ether
 device         mge                     # Marvell Gigabit Ethernet controller
 device         mii
+device         mdio
 device         e1000phy
 device         bpf
 options        HZ=1000
index 9533c2bf3ef049357d9a3cb8251fe33300538356..9a6dec43a8b4a4c28383705017a646a5b0bcd6d4 100644 (file)
@@ -1398,8 +1398,8 @@ dev/etherswitch/ip17x/ip175c.c            optional ip17x
 dev/etherswitch/ip17x/ip175d.c         optional ip17x
 dev/etherswitch/ip17x/ip17x_phy.c      optional ip17x
 dev/etherswitch/ip17x/ip17x_vlans.c    optional ip17x
-dev/etherswitch/mdio_if.m              optional miiproxy
-dev/etherswitch/mdio.c                 optional miiproxy
+dev/etherswitch/mdio_if.m              optional miiproxy | mdio
+dev/etherswitch/mdio.c                 optional miiproxy | mdio
 dev/etherswitch/miiproxy.c             optional miiproxy
 dev/etherswitch/rtl8366/rtl8366rb.c    optional rtl8366rb
 dev/etherswitch/ukswitch/ukswitch.c    optional ukswitch
index c35dd04dcbf69a9e5fc5f19127b73e9a4021c362..7791662f7dfcb05cf37d1b17331308e6f191d190 100644 (file)
@@ -1,5 +1,7 @@
 /*-
  * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
+ * Copyright (C) 2009-2015 Semihalf
+ * Copyright (C) 2015 Stormshield
  * All rights reserved.
  *
  * Developed by Semihalf.
@@ -50,7 +52,6 @@ __FBSDID("$FreeBSD$");
 #include <net/ethernet.h>
 #include <net/bpf.h>
 #include <net/if.h>
-#include <net/if_var.h>
 #include <net/if_arp.h>
 #include <net/if_dl.h>
 #include <net/if_media.h>
@@ -73,12 +74,16 @@ __FBSDID("$FreeBSD$");
 #include <dev/fdt/fdt_common.h>
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
+#include <dev/etherswitch/mdio.h>
 
 #include <dev/mge/if_mgevar.h>
 #include <arm/mv/mvreg.h>
 #include <arm/mv/mvvar.h>
 
 #include "miibus_if.h"
+#include "mdio_if.h"
+
+#define        MGE_DELAY(x)    pause("SMI access sleep", (x) / tick_sbt)
 
 static int mge_probe(device_t dev);
 static int mge_attach(device_t dev);
@@ -90,6 +95,9 @@ static int mge_resume(device_t dev);
 static int mge_miibus_readreg(device_t dev, int phy, int reg);
 static int mge_miibus_writereg(device_t dev, int phy, int reg, int value);
 
+static int mge_mdio_readreg(device_t dev, int phy, int reg);
+static int mge_mdio_writereg(device_t dev, int phy, int reg, int value);
+
 static int mge_ifmedia_upd(struct ifnet *ifp);
 static void mge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
 
@@ -154,21 +162,23 @@ static device_method_t mge_methods[] = {
        /* MII interface */
        DEVMETHOD(miibus_readreg,       mge_miibus_readreg),
        DEVMETHOD(miibus_writereg,      mge_miibus_writereg),
+       /* MDIO interface */
+       DEVMETHOD(mdio_readreg,         mge_mdio_readreg),
+       DEVMETHOD(mdio_writereg,        mge_mdio_writereg),
        { 0, 0 }
 };
 
-static driver_t mge_driver = {
-       "mge",
-       mge_methods,
-       sizeof(struct mge_softc),
-};
+DEFINE_CLASS_0(mge, mge_driver, mge_methods, sizeof(struct mge_softc));
 
 static devclass_t mge_devclass;
+static int switch_attached = 0;
 
 DRIVER_MODULE(mge, simplebus, mge_driver, mge_devclass, 0, 0);
 DRIVER_MODULE(miibus, mge, miibus_driver, miibus_devclass, 0, 0);
+DRIVER_MODULE(mdio, mge, mdio_driver, mdio_devclass, 0, 0);
 MODULE_DEPEND(mge, ether, 1, 1, 1);
 MODULE_DEPEND(mge, miibus, 1, 1, 1);
+MODULE_DEPEND(mge, mdio, 1, 1, 1);
 
 static struct resource_spec res_spec[] = {
        { SYS_RES_MEMORY, 0, RF_ACTIVE },
@@ -190,6 +200,133 @@ static struct {
        { mge_intr_err, "GbE error interrupt" },
 };
 
+/* SMI access interlock */
+static struct sx sx_smi;
+
+static uint32_t
+mv_read_ge_smi(device_t dev, int phy, int reg)
+{
+       uint32_t timeout;
+       uint32_t ret;
+       struct mge_softc *sc;
+
+       sc = device_get_softc(dev);
+       KASSERT(sc != NULL, ("NULL softc ptr!"));
+       timeout = MGE_SMI_WRITE_RETRIES;
+
+       MGE_SMI_LOCK();
+       while (--timeout &&
+           (MGE_READ(sc, MGE_REG_SMI) & MGE_SMI_BUSY))
+               MGE_DELAY(MGE_SMI_WRITE_DELAY);
+
+       if (timeout == 0) {
+               device_printf(dev, "SMI write timeout.\n");
+               ret = ~0U;
+               goto out;
+       }
+
+       MGE_WRITE(sc, MGE_REG_SMI, MGE_SMI_MASK &
+           (MGE_SMI_READ | (reg << 21) | (phy << 16)));
+
+       /* Wait till finished. */
+       timeout = MGE_SMI_WRITE_RETRIES;
+       while (--timeout &&
+           !((MGE_READ(sc, MGE_REG_SMI) & MGE_SMI_READVALID)))
+               MGE_DELAY(MGE_SMI_WRITE_DELAY);
+
+       if (timeout == 0) {
+               device_printf(dev, "SMI write validation timeout.\n");
+               ret = ~0U;
+               goto out;
+       }
+
+       /* Wait for the data to update in the SMI register */
+       MGE_DELAY(MGE_SMI_DELAY);
+       ret = MGE_READ(sc, MGE_REG_SMI) & MGE_SMI_DATA_MASK;
+
+out:
+       MGE_SMI_UNLOCK();
+       return (ret);
+
+}
+
+static void
+mv_write_ge_smi(device_t dev, int phy, int reg, uint32_t value)
+{
+       uint32_t timeout;
+       struct mge_softc *sc;
+
+       sc = device_get_softc(dev);
+       KASSERT(sc != NULL, ("NULL softc ptr!"));
+
+       MGE_SMI_LOCK();
+       timeout = MGE_SMI_READ_RETRIES;
+       while (--timeout &&
+           (MGE_READ(sc, MGE_REG_SMI) & MGE_SMI_BUSY))
+               MGE_DELAY(MGE_SMI_READ_DELAY);
+
+       if (timeout == 0) {
+               device_printf(dev, "SMI read timeout.\n");
+               goto out;
+       }
+
+       MGE_WRITE(sc, MGE_REG_SMI, MGE_SMI_MASK &
+           (MGE_SMI_WRITE | (reg << 21) | (phy << 16) |
+           (value & MGE_SMI_DATA_MASK)));
+
+out:
+       MGE_SMI_UNLOCK();
+}
+
+static int
+mv_read_ext_phy(device_t dev, int phy, int reg)
+{
+       uint32_t retries;
+       struct mge_softc *sc;
+       uint32_t ret;
+
+       sc = device_get_softc(dev);
+
+       MGE_SMI_LOCK();
+       MGE_WRITE(sc->phy_sc, MGE_REG_SMI, MGE_SMI_MASK &
+           (MGE_SMI_READ | (reg << 21) | (phy << 16)));
+
+       retries = MGE_SMI_READ_RETRIES;
+       while (--retries &&
+           !(MGE_READ(sc->phy_sc, MGE_REG_SMI) & MGE_SMI_READVALID))
+               DELAY(MGE_SMI_READ_DELAY);
+
+       if (retries == 0)
+               device_printf(dev, "Timeout while reading from PHY\n");
+
+       ret = MGE_READ(sc->phy_sc, MGE_REG_SMI) & MGE_SMI_DATA_MASK;
+       MGE_SMI_UNLOCK();
+
+       return (ret);
+}
+
+static void
+mv_write_ext_phy(device_t dev, int phy, int reg, int value)
+{
+       uint32_t retries;
+       struct mge_softc *sc;
+
+       sc = device_get_softc(dev);
+
+       MGE_SMI_LOCK();
+       MGE_WRITE(sc->phy_sc, MGE_REG_SMI, MGE_SMI_MASK &
+           (MGE_SMI_WRITE | (reg << 21) | (phy << 16) |
+           (value & MGE_SMI_DATA_MASK)));
+
+       retries = MGE_SMI_WRITE_RETRIES;
+       while (--retries && MGE_READ(sc->phy_sc, MGE_REG_SMI) & MGE_SMI_BUSY)
+               DELAY(MGE_SMI_WRITE_DELAY);
+
+       if (retries == 0)
+               device_printf(dev, "Timeout while writing to PHY\n");
+       MGE_SMI_UNLOCK();
+}
+
 static void
 mge_get_mac_address(struct mge_softc *sc, uint8_t *addr)
 {
@@ -605,10 +742,10 @@ mge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
        uint32_t int_cause, int_cause_ext;
        int rx_npkts = 0;
 
-       MGE_GLOBAL_LOCK(sc);
+       MGE_RECEIVE_LOCK(sc);
 
        if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
-               MGE_GLOBAL_UNLOCK(sc);
+               MGE_RECEIVE_UNLOCK(sc);
                return (rx_npkts);
        }
 
@@ -626,10 +763,13 @@ mge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
                }
        }
 
-       mge_intr_tx_locked(sc);
+
        rx_npkts = mge_intr_rx_locked(sc, count);
 
-       MGE_GLOBAL_UNLOCK(sc);
+       MGE_RECEIVE_UNLOCK(sc);
+       MGE_TRANSMIT_LOCK(sc);
+       mge_intr_tx_locked(sc);
+       MGE_TRANSMIT_UNLOCK(sc);
        return (rx_npkts);
 }
 #endif /* DEVICE_POLLING */
@@ -646,14 +786,34 @@ mge_attach(device_t dev)
        sc = device_get_softc(dev);
        sc->dev = dev;
        sc->node = ofw_bus_get_node(dev);
+       phy = 0;
+
+       if (fdt_get_phyaddr(sc->node, sc->dev, &phy, (void **)&sc->phy_sc) == 0) {
+               device_printf(dev, "PHY%i attached, phy_sc points to %s\n", phy,
+                   device_get_nameunit(sc->phy_sc->dev));
+               sc->phy_attached = 1;
+       } else {
+               device_printf(dev, "PHY not attached.\n");
+               sc->phy_attached = 0;
+               sc->phy_sc = sc;
+       }
+
+       if (fdt_find_compatible(sc->node, "mrvl,sw", 1) != 0) {
+               device_printf(dev, "Switch attached.\n");
+               sc->switch_attached = 1;
+               /* additional variable available across instances */
+               switch_attached = 1;
+       } else {
+               sc->switch_attached = 0;
+       }
+
+       if (device_get_unit(dev) == 0) {
+               sx_init(&sx_smi, "mge_tick() SMI access threads interlock");
+       }
 
        /* Set chip version-dependent parameters */
        mge_ver_params(sc);
 
-       /* Get phy address and used softc from fdt */
-       if (fdt_get_phyaddr(sc->node, sc->dev, &phy, (void **)&sc->phy_sc) != 0)
-               return (ENXIO);
-
        /* Initialize mutexes */
        mtx_init(&sc->transmit_lock, device_get_nameunit(dev), "mge TX lock", MTX_DEF);
        mtx_init(&sc->receive_lock, device_get_nameunit(dev), "mge RX lock", MTX_DEF);
@@ -719,18 +879,32 @@ mge_attach(device_t dev)
        callout_init(&sc->wd_callout, 0);
 
        /* Attach PHY(s) */
-       error = mii_attach(dev, &sc->miibus, ifp, mge_ifmedia_upd,
-           mge_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
-       if (error) {
-               device_printf(dev, "attaching PHYs failed\n");
-               mge_detach(dev);
-               return (error);
-       }
-       sc->mii = device_get_softc(sc->miibus);
+       if (sc->phy_attached) {
+               error = mii_attach(dev, &sc->miibus, ifp, mge_ifmedia_upd,
+                   mge_ifmedia_sts, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
+               if (error) {
+                       device_printf(dev, "MII failed to find PHY\n");
+                       if_free(ifp);
+                       sc->ifp = NULL;
+                       mge_detach(dev);
+                       return (error);
+               }
+               sc->mii = device_get_softc(sc->miibus);
 
-       /* Tell the MAC where to find the PHY so autoneg works */
-       miisc = LIST_FIRST(&sc->mii->mii_phys);
-       MGE_WRITE(sc, MGE_REG_PHYDEV, miisc->mii_phy);
+               /* Tell the MAC where to find the PHY so autoneg works */
+               miisc = LIST_FIRST(&sc->mii->mii_phys);
+               MGE_WRITE(sc, MGE_REG_PHYDEV, miisc->mii_phy);
+       } else {
+               /* no PHY, so use hard-coded values */
+               ifmedia_init(&sc->mge_ifmedia, 0,
+                   mge_ifmedia_upd,
+                   mge_ifmedia_sts);
+               ifmedia_add(&sc->mge_ifmedia,
+                   IFM_ETHER | IFM_1000_T | IFM_FDX,
+                   0, NULL);
+               ifmedia_set(&sc->mge_ifmedia,
+                   IFM_ETHER | IFM_1000_T | IFM_FDX);
+       }
 
        /* Attach interrupt handlers */
        /* TODO: review flags, in part. mark RX as INTR_ENTROPY ? */
@@ -747,6 +921,13 @@ mge_attach(device_t dev)
                }
        }
 
+       if (sc->switch_attached) {
+               device_t child;
+               MGE_WRITE(sc, MGE_REG_PHYDEV, MGE_SWITCH_PHYDEV);
+               child = device_add_child(dev, "mdio", -1);
+               bus_generic_attach(dev);
+       }
+
        return (0);
 }
 
@@ -792,6 +973,9 @@ mge_detach(device_t dev)
        mtx_destroy(&sc->receive_lock);
        mtx_destroy(&sc->transmit_lock);
 
+       if (device_get_unit(dev) == 0)
+               sx_destroy(&sx_smi);
+
        return (0);
 }
 
@@ -801,7 +985,13 @@ mge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
        struct mge_softc *sc = ifp->if_softc;
        struct mii_data *mii;
 
-       MGE_TRANSMIT_LOCK(sc);
+       MGE_GLOBAL_LOCK(sc);
+
+       if (!sc->phy_attached) {
+               ifmr->ifm_active = IFM_1000_T | IFM_FDX | IFM_ETHER;
+               ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+               goto out_unlock;
+       }
 
        mii = sc->mii;
        mii_pollstat(mii);
@@ -809,7 +999,8 @@ mge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
        ifmr->ifm_active = mii->mii_media_active;
        ifmr->ifm_status = mii->mii_media_status;
 
-       MGE_TRANSMIT_UNLOCK(sc);
+out_unlock:
+       MGE_GLOBAL_UNLOCK(sc);
 }
 
 static uint32_t
@@ -826,13 +1017,13 @@ mge_set_port_serial_control(uint32_t media)
                                break;
                        case IFM_1000_T:
                                port_config  |= (PORT_SERIAL_GMII_SPEED_1000 |
-                                   PORT_SERIAL_AUTONEG | PORT_SERIAL_AUTONEG_FC |
-                                   PORT_SERIAL_SPEED_AUTONEG);
+                                   PORT_SERIAL_AUTONEG | PORT_SERIAL_AUTONEG_FC
+                                   PORT_SERIAL_SPEED_AUTONEG);
                                break;
                        case IFM_100_TX:
                                port_config  |= (PORT_SERIAL_MII_SPEED_100 |
-                                   PORT_SERIAL_AUTONEG | PORT_SERIAL_AUTONEG_FC |
-                                   PORT_SERIAL_SPEED_AUTONEG);
+                                   PORT_SERIAL_AUTONEG | PORT_SERIAL_AUTONEG_FC
+                                   PORT_SERIAL_SPEED_AUTONEG);
                                break;
                        case IFM_10_T:
                                port_config  |= (PORT_SERIAL_AUTONEG |
@@ -851,13 +1042,21 @@ mge_ifmedia_upd(struct ifnet *ifp)
 {
        struct mge_softc *sc = ifp->if_softc;
 
-       if (ifp->if_flags & IFF_UP) {
+       /*
+        * Do not do anything for switch here, as updating media between
+        * MGE MAC and switch MAC is hardcoded in PCB. Changing it here would
+        * break the link.
+        */
+       if (sc->phy_attached) {
                MGE_GLOBAL_LOCK(sc);
+               if (ifp->if_flags & IFF_UP) {
+                       sc->mge_media_status = sc->mii->mii_media.ifm_media;
+                       mii_mediachg(sc->mii);
 
-               sc->mge_media_status = sc->mii->mii_media.ifm_media;
-               mii_mediachg(sc->mii);
-               mge_init_locked(sc);
+                       /* MGE MAC needs to be reinitialized. */
+                       mge_init_locked(sc);
 
+               }
                MGE_GLOBAL_UNLOCK(sc);
        }
 
@@ -883,6 +1082,7 @@ mge_init_locked(void *arg)
        struct mge_desc_wrapper *dw;
        volatile uint32_t reg_val;
        int i, count;
+       uint32_t media_status;
 
 
        MGE_GLOBAL_LOCK_ASSERT(sc);
@@ -925,8 +1125,17 @@ mge_init_locked(void *arg)
            PORT_CONFIG_ARO_RXQ(0));
        MGE_WRITE(sc, MGE_PORT_EXT_CONFIG , 0x0);
 
+       /* Configure promisc mode */
+       mge_set_prom_mode(sc, MGE_RX_DEFAULT_QUEUE);
+
+       media_status = sc->mge_media_status;
+       if (sc->switch_attached) {
+               media_status &= ~IFM_TMASK;
+               media_status |= IFM_1000_T;
+       }
+
        /* Setup port configuration */
-       reg_val = mge_set_port_serial_control(sc->mge_media_status);
+       reg_val = mge_set_port_serial_control(media_status);
        MGE_WRITE(sc, MGE_PORT_SERIAL_CTRL, reg_val);
 
        /* Setup SDMA configuration */
@@ -997,7 +1206,8 @@ mge_init_locked(void *arg)
        sc->wd_timer = 0;
 
        /* Schedule watchdog timeout */
-       callout_reset(&sc->wd_callout, hz, mge_tick, sc);
+       if (sc->phy_attached)
+               callout_reset(&sc->wd_callout, hz, mge_tick, sc);
 }
 
 static void
@@ -1331,6 +1541,18 @@ mge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
                break;
        case SIOCGIFMEDIA: /* fall through */
        case SIOCSIFMEDIA:
+               /*
+                * Setting up media type via ioctls is *not* supported for MAC
+                * which is connected to switch. Use etherswitchcfg.
+                */
+               if (!sc->phy_attached && (command == SIOCSIFMEDIA))
+                       return (0);
+               else if (!sc->phy_attached) {
+                       error = ifmedia_ioctl(ifp, ifr, &sc->mge_ifmedia,
+                           command);
+                       break;
+               }
+
                if (IFM_SUBTYPE(ifr->ifr_media) == IFM_1000_T
                    && !(ifr->ifr_media & IFM_FDX)) {
                        device_printf(sc->dev,
@@ -1349,41 +1571,23 @@ static int
 mge_miibus_readreg(device_t dev, int phy, int reg)
 {
        struct mge_softc *sc;
-       uint32_t retries;
-
        sc = device_get_softc(dev);
 
-       MGE_WRITE(sc->phy_sc, MGE_REG_SMI, 0x1fffffff &
-           (MGE_SMI_READ | (reg << 21) | (phy << 16)));
-
-       retries = MGE_SMI_READ_RETRIES;
-       while (--retries &&
-           !(MGE_READ(sc->phy_sc, MGE_REG_SMI) & MGE_SMI_READVALID))
-               DELAY(MGE_SMI_READ_DELAY);
+       KASSERT(!switch_attached, ("miibus used with switch attached"));
 
-       if (retries == 0)
-               device_printf(dev, "Timeout while reading from PHY\n");
-
-       return (MGE_READ(sc->phy_sc, MGE_REG_SMI) & 0xffff);
+       return (mv_read_ext_phy(dev, phy, reg));
 }
 
 static int
 mge_miibus_writereg(device_t dev, int phy, int reg, int value)
 {
        struct mge_softc *sc;
-       uint32_t retries;
-
        sc = device_get_softc(dev);
 
-       MGE_WRITE(sc->phy_sc, MGE_REG_SMI, 0x1fffffff &
-           (MGE_SMI_WRITE | (reg << 21) | (phy << 16) | (value & 0xffff)));
+       KASSERT(!switch_attached, ("miibus used with switch attached"));
 
-       retries = MGE_SMI_WRITE_RETRIES;
-       while (--retries && MGE_READ(sc->phy_sc, MGE_REG_SMI) & MGE_SMI_BUSY)
-               DELAY(MGE_SMI_WRITE_DELAY);
+       mv_write_ext_phy(dev, phy, reg, value);
 
-       if (retries == 0)
-               device_printf(dev, "Timeout while writing to PHY\n");
        return (0);
 }
 
@@ -1489,6 +1693,10 @@ mge_tick(void *msc)
 {
        struct mge_softc *sc = msc;
 
+       KASSERT(sc->phy_attached == 1, ("mge_tick while PHY not attached"));
+
+       MGE_GLOBAL_LOCK(sc);
+
        /* Check for TX timeout */
        mge_watchdog(sc);
 
@@ -1498,8 +1706,12 @@ mge_tick(void *msc)
        if(sc->mge_media_status != sc->mii->mii_media.ifm_media)
                mge_ifmedia_upd(sc->ifp);
 
+       MGE_GLOBAL_UNLOCK(sc);
+
        /* Schedule another timeout one second from now */
        callout_reset(&sc->wd_callout, hz, mge_tick, sc);
+
+       return;
 }
 
 static void
@@ -1509,10 +1721,7 @@ mge_watchdog(struct mge_softc *sc)
 
        ifp = sc->ifp;
 
-       MGE_GLOBAL_LOCK(sc);
-
        if (sc->wd_timer == 0 || --sc->wd_timer) {
-               MGE_GLOBAL_UNLOCK(sc);
                return;
        }
 
@@ -1521,8 +1730,6 @@ mge_watchdog(struct mge_softc *sc)
 
        mge_stop(sc);
        mge_init_locked(sc);
-
-       MGE_GLOBAL_UNLOCK(sc);
 }
 
 static void
@@ -1927,3 +2134,23 @@ mge_add_sysctls(struct mge_softc *sc)
            CTLTYPE_UINT | CTLFLAG_RW, sc, MGE_IC_TX, mge_sysctl_ic,
            "I", "IC TX time threshold");
 }
+
+static int
+mge_mdio_writereg(device_t dev, int phy, int reg, int value)
+{
+
+       mv_write_ge_smi(dev, phy, reg, value);
+
+       return (0);
+}
+
+
+static int
+mge_mdio_readreg(device_t dev, int phy, int reg)
+{
+       int ret;
+
+       ret = mv_read_ge_smi(dev, phy, reg);
+
+       return (ret);
+}
index 0ab28bf6f0becea8b30d7189ff71db6f4c0bbc5f..1888efba99844b0c54c71fb27babe1548896f497 100644 (file)
@@ -34,6 +34,8 @@
 #ifndef __IF_MGE_H__
 #define __IF_MGE_H__
 
+#include <arm/mv/mvvar.h>
+
 #define MGE_INTR_COUNT         5       /* ETH controller occupies 5 IRQ lines */
 #define MGE_TX_DESC_NUM                256
 #define MGE_RX_DESC_NUM                256
@@ -71,6 +73,7 @@ struct mge_softc {
        device_t        miibus;
 
        struct mii_data *mii;
+       struct ifmedia  mge_ifmedia;
        struct resource *res[1 + MGE_INTR_COUNT];       /* resources */
        void            *ih_cookie[MGE_INTR_COUNT];     /* interrupt handlers cookies */
        struct mtx      transmit_lock;                  /* transmitter lock */
@@ -106,6 +109,8 @@ struct mge_softc {
        int             mge_intr_cnt;
        uint8_t         mge_hw_csum;
 
+       int             phy_attached;
+       int             switch_attached;
        struct mge_softc *phy_sc;
 };
 
@@ -150,6 +155,14 @@ struct mge_softc {
                        MGE_RECEIVE_LOCK_ASSERT(sc);                            \
 } while (0)
 
+#define MGE_SMI_LOCK() do {                            \
+    sx_assert(&sx_smi, SA_UNLOCKED);                   \
+    sx_xlock(&sx_smi);                                 \
+} while (0)
+
+#define MGE_SMI_UNLOCK()               sx_unlock(&sx_smi)
+#define MGE_SMI_LOCK_ASSERT()          sx_assert(&sx_smi, SA_XLOCKED)
+
 /* SMI-related macros */
 #define MGE_REG_PHYDEV         0x000
 #define MGE_REG_SMI            0x004
@@ -158,6 +171,17 @@ struct mge_softc {
 #define MGE_SMI_READVALID      (1 << 27)
 #define MGE_SMI_BUSY           (1 << 28)
 
+#define        MGE_SMI_MASK            0x1fffffff
+#define        MGE_SMI_DATA_MASK       0xffff
+#define        MGE_SMI_DELAY           1000
+
+#define        MGE_SWITCH_PHYDEV       6
+
+/* Internal Switch SMI Command */
+
+#define SW_SMI_READ_CMD(phy, reg)              ((1 << 15) | (1 << 12) | (1 << 11) | (phy << 5) | reg)
+#define SW_SMI_WRITE_CMD(phy, reg)             ((1 << 15) | (1 << 12) | (1 << 10) | (phy << 5) | reg)
+
 /* TODO verify the timings and retries count w/specs */
 #define MGE_SMI_READ_RETRIES           1000
 #define MGE_SMI_READ_DELAY             100