Page MenuHomeFreeBSD

D55013.diff
No OneTemporary

D55013.diff

diff --git a/sys/arm64/conf/std.rockchip b/sys/arm64/conf/std.rockchip
--- a/sys/arm64/conf/std.rockchip
+++ b/sys/arm64/conf/std.rockchip
@@ -41,6 +41,9 @@
device dwc # Synopsys DesignWare GMAC controller
device dwc_rk # Rockchip Designware
device eqos # Synopsys Designware Ethernet QoS controller
+device etherswitch # Enable etherswitch support
+device mtkswitch # Enable mediatek 7531 switch
+device mdio
# IO Domains
device rk_iodomain
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1697,6 +1697,10 @@
dev/etherswitch/rtl8366/rtl8366rb.c optional rtl8366rb
dev/etherswitch/e6000sw/e6000sw.c optional e6000sw fdt
dev/etherswitch/e6000sw/e6060sw.c optional e6060sw
+dev/etherswitch/mtkswitch/mtkswitch.c optional mtkswitch
+dev/etherswitch/mtkswitch/mtkswitch_mt7531.c optional mtkswitch
+dev/etherswitch/mtkswitch/mtkswitch_mt7620.c optional mtkswitch
+dev/etherswitch/mtkswitch/mtkswitch_rt3050.c optional mtkswitch
dev/etherswitch/infineon/adm6996fc.c optional adm6996fc
dev/etherswitch/micrel/ksz8995ma.c optional ksz8995ma
dev/etherswitch/ukswitch/ukswitch.c optional ukswitch
diff --git a/sys/dev/eqos/if_eqos.c b/sys/dev/eqos/if_eqos.c
--- a/sys/dev/eqos/if_eqos.c
+++ b/sys/dev/eqos/if_eqos.c
@@ -59,9 +59,13 @@
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
+#include <dev/fdt/fdt_common.h>
+
+#include <dev/mdio/mdio.h>
#include "miibus_if.h"
#include "if_eqos_if.h"
+#include "mdio_if.h"
#ifdef FDT
#include <dev/ofw/openfirm.h>
@@ -172,22 +176,13 @@
}
static void
-eqos_miibus_statchg(device_t dev)
+eqos_mac_change(struct eqos_softc *sc, uint32_t media)
{
- struct eqos_softc *sc = device_get_softc(dev);
- struct mii_data *mii = device_get_softc(sc->miibus);
uint32_t reg;
- EQOS_ASSERT_LOCKED(sc);
-
- if (mii->mii_media_status & IFM_ACTIVE)
- sc->link_up = true;
- else
- sc->link_up = false;
-
reg = RD4(sc, GMAC_MAC_CONFIGURATION);
- switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ switch (IFM_SUBTYPE(media)) {
case IFM_10_T:
reg |= GMAC_MAC_CONFIGURATION_PS;
reg &= ~GMAC_MAC_CONFIGURATION_FES;
@@ -211,28 +206,56 @@
return;
}
- if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX))
+ if ((IFM_OPTIONS(media) & IFM_FDX))
reg |= GMAC_MAC_CONFIGURATION_DM;
else
reg &= ~GMAC_MAC_CONFIGURATION_DM;
WR4(sc, GMAC_MAC_CONFIGURATION, reg);
- IF_EQOS_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active));
+ IF_EQOS_SET_SPEED(sc->dev, IFM_SUBTYPE(media));
WR4(sc, GMAC_MAC_1US_TIC_COUNTER, (sc->csr_clock / 1000000) - 1);
}
+static void
+eqos_miibus_statchg(device_t dev)
+{
+ struct eqos_softc *sc = device_get_softc(dev);
+ struct mii_data *mii = device_get_softc(sc->miibus);
+
+ KASSERT(sc->phy_attached == 1, ("wqos_tick while PHY not attached"));
+
+ EQOS_ASSERT_LOCKED(sc);
+
+ if (mii->mii_media_status & IFM_ACTIVE)
+ sc->link_up = true;
+ else
+ sc->link_up = false;
+
+ eqos_mac_change(sc, mii->mii_media_active);
+}
+
static void
eqos_media_status(if_t ifp, struct ifmediareq *ifmr)
{
struct eqos_softc *sc = if_getsoftc(ifp);
- struct mii_data *mii = device_get_softc(sc->miibus);
+ struct mii_data *mii;
EQOS_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 = device_get_softc(sc->miibus);
mii_pollstat(mii);
ifmr->ifm_active = mii->mii_media_active;
ifmr->ifm_status = mii->mii_media_status;
+
+ out_unlock:
EQOS_UNLOCK(sc);
}
@@ -240,11 +263,17 @@
eqos_media_change(if_t ifp)
{
struct eqos_softc *sc = if_getsoftc(ifp);
- int error;
+ int error = 0;
+ /*
+ * Do not do anything for switch here
+ */
+
+ if (sc->phy_attached) {
EQOS_LOCK(sc);
error = mii_mediachg(device_get_softc(sc->miibus));
EQOS_UNLOCK(sc);
+ }
return (error);
}
@@ -578,8 +607,16 @@
if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
+ if (sc->switch_attached) {
+ sc->link_up = true;
+ eqos_mac_change(sc, IFM_ETHER | IFM_1000_T | IFM_FDX);
+ }
+
+ if (sc->phy_attached) {
+ mii = device_get_softc(sc->miibus);
mii_mediachg(mii);
callout_reset(&sc->callout, hz, eqos_tick, sc);
+ }
EQOS_UNLOCK(sc);
}
@@ -1145,6 +1182,84 @@
return (0);
}
+static int
+eqos_mdio_writereg(device_t dev, int phy, int reg, int val)
+{
+ struct eqos_softc *sc = device_get_softc(dev);
+ uint32_t addr;
+ int retry;
+
+ KASSERT(sc != NULL, ("NULL softc ptr!"));
+
+ WR4(sc, GMAC_MAC_MDIO_DATA, val);
+
+ addr = sc->csr_clock_range |
+ (phy << GMAC_MAC_MDIO_ADDRESS_PA_SHIFT) |
+ (reg << GMAC_MAC_MDIO_ADDRESS_RDA_SHIFT) |
+ GMAC_MAC_MDIO_ADDRESS_GOC_WRITE | GMAC_MAC_MDIO_ADDRESS_GB;
+ WR4(sc, GMAC_MAC_MDIO_ADDRESS, addr);
+
+ DELAY(100);
+
+ for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
+ addr = RD4(sc, GMAC_MAC_MDIO_ADDRESS);
+ if (!(addr & GMAC_MAC_MDIO_ADDRESS_GB))
+ break;
+ DELAY(10);
+ }
+ if (!retry) {
+ device_printf(dev, "phy write timeout, phy=%d reg=%d\n",
+ phy, reg);
+ return (ETIMEDOUT);
+ }
+ return (0);
+}
+
+static int
+eqos_mdio_readreg(device_t dev, int phy, int reg)
+{
+ struct eqos_softc *sc = device_get_softc(dev);
+ uint32_t addr;
+ int retry, val;
+
+
+ KASSERT(sc != NULL, ("NULL softc ptr!"));
+
+ addr = sc->csr_clock_range |
+ (phy << GMAC_MAC_MDIO_ADDRESS_PA_SHIFT) |
+ (reg << GMAC_MAC_MDIO_ADDRESS_RDA_SHIFT) |
+ GMAC_MAC_MDIO_ADDRESS_GOC_READ | GMAC_MAC_MDIO_ADDRESS_GB;
+ WR4(sc, GMAC_MAC_MDIO_ADDRESS, addr);
+
+ DELAY(100);
+
+ for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
+ addr = RD4(sc, GMAC_MAC_MDIO_ADDRESS);
+ if (!(addr & GMAC_MAC_MDIO_ADDRESS_GB)) {
+ val = RD4(sc, GMAC_MAC_MDIO_DATA) & 0xFFFF;
+ break;
+ }
+ DELAY(10);
+ }
+ if (!retry) {
+ device_printf(dev, "phy read timeout, phy=%d reg=%d\n",
+ phy, reg);
+ return (ETIMEDOUT);
+ }
+ return (val);
+}
+
+static boolean_t
+eqos_has_switch(device_t dev)
+{
+#ifdef FDT
+ phandle_t node;
+ node = ofw_bus_get_node(dev);
+ return (fdt_find_ethernet_prop_switch(node, OF_finddevice("/")));
+#endif
+ return (false);
+}
+
static int
eqos_attach(device_t dev)
{
@@ -1155,6 +1270,8 @@
u_int userver, snpsver;
int error;
int n;
+ int phy;
+ phandle_t node;
/* default values */
sc->thresh_dma_mode = false;
@@ -1175,6 +1292,11 @@
return (error);
sc->dev = dev;
+
+ sc->phy_attached = 0;
+ sc->switch_attached = 0;
+ node = ofw_bus_get_node(dev);
+
ver = RD4(sc, GMAC_MAC_VERSION);
userver = (ver & GMAC_MAC_VERSION_USERVER_MASK) >>
GMAC_MAC_VERSION_USERVER_SHIFT;
@@ -1241,12 +1363,36 @@
if_setcapenable(ifp, if_getcapabilities(ifp));
/* Attach MII driver */
+ if (fdt_get_phyaddr(node, sc->dev, &phy, NULL) == 0) {
if ((error = mii_attach(sc->dev, &sc->miibus, ifp, eqos_media_change,
- eqos_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY,
+ eqos_media_status, BMSR_DEFCAPMASK, phy,
MII_OFFSET_ANY, 0))) {
device_printf(sc->dev, "PHY attach failed\n");
return (ENXIO);
}
+ sc->phy_attached = 1;
+ } else {
+ /* Fixed-link, use predefined values */
+ ifmedia_init(&sc->eqos_ifmedia, 0,
+ eqos_media_change,
+ eqos_media_status);
+ ifmedia_add(&sc->eqos_ifmedia,
+ IFM_ETHER | IFM_1000_T | IFM_FDX,
+ 0, NULL);
+ ifmedia_set(&sc->eqos_ifmedia,
+ IFM_ETHER | IFM_1000_T | IFM_FDX);
+
+ if (eqos_has_switch(dev)) {
+ device_t child;
+ child = device_add_child(dev, "mdio", -1);
+ bus_attach_children(dev);
+ bus_attach_children(child);
+ device_printf(dev, "Switch attached.\n");
+ sc->switch_attached = 1;
+ } else {
+ device_printf(dev, "PHY not attached.\n");
+ }
+ }
/* Attach ethernet interface */
ether_ifattach(ifp, eaddr);
diff --git a/sys/dev/eqos/if_eqos_var.h b/sys/dev/eqos/if_eqos_var.h
--- a/sys/dev/eqos/if_eqos_var.h
+++ b/sys/dev/eqos/if_eqos_var.h
@@ -92,6 +92,7 @@
uint32_t ttc;
uint32_t rtc;
+ struct ifmedia eqos_ifmedia;
struct ifnet *ifp;
device_t miibus;
struct mtx lock;
@@ -99,6 +100,9 @@
struct eqos_ring tx;
struct eqos_ring rx;
+
+ int phy_attached;
+ int switch_attached;
};
DECLARE_CLASS(eqos_driver);
diff --git a/sys/dev/etherswitch/mtkswitch/mtkswitch.c b/sys/dev/etherswitch/mtkswitch/mtkswitch.c
--- a/sys/dev/etherswitch/mtkswitch/mtkswitch.c
+++ b/sys/dev/etherswitch/mtkswitch/mtkswitch.c
@@ -1,4 +1,5 @@
/*-
+ * Copyright (c) 2026 Martin Filla
* Copyright (c) 2016 Stanislav Galabov.
* Copyright (c) 2011-2012 Stefan Bethke.
* Copyright (c) 2012 Adrian Chadd.
@@ -38,6 +39,7 @@
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
+#include <sys/rman.h>
#include <net/if.h>
#include <net/if_var.h>
@@ -78,31 +80,60 @@
{ "mediatek,mt7620-gsw", MTK_SWITCH_MT7620 },
{ "mediatek,mt7621-gsw", MTK_SWITCH_MT7621 },
{ "mediatek,mt7628-esw", MTK_SWITCH_MT7628 },
+ { "mediatek,mt7531", MTK_SWITCH_MT7531},
/* Sentinel */
{ NULL, MTK_SWITCH_NONE }
};
+static void
+mtkswitch_identify(driver_t *driver, device_t parent)
+{
+ if (device_find_child(parent, "mtkswitch", -1) == NULL)
+ BUS_ADD_CHILD(parent, 0, "mtkswitch", -1);
+}
+
static int
mtkswitch_probe(device_t dev)
{
struct mtkswitch_softc *sc;
mtk_switch_type switch_type;
+ phandle_t switch_node;
+
+ sc = device_get_softc(dev);
+ bzero(sc, sizeof(*sc));
if (!ofw_bus_status_okay(dev))
return (ENXIO);
switch_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
if (switch_type == MTK_SWITCH_NONE)
+ {
+ // TODO: remove this hack
+ switch_node = ofw_bus_find_compatible(OF_finddevice("/"),
+ "mediatek,mt7531");
+ if (switch_node == MTK_SWITCH_NONE || switch_node == 0)
+ {
return (ENXIO);
-
- sc = device_get_softc(dev);
- bzero(sc, sizeof(*sc));
+ }
+ else
+ {
+ if (bootverbose)
+ {
+ device_printf(dev, "Found switch_node: 0x%x\n", switch_node);
+ }
+
+ sc->sc_switchtype = MTK_SWITCH_MT7531;
+ }
+ }
+ else
+ {
sc->sc_switchtype = switch_type;
+ }
device_set_desc(dev, "MTK Switch Driver");
- return (0);
+ return (BUS_PROBE_DEFAULT);
}
static int
@@ -172,7 +203,22 @@
/* sc->sc_switchtype is already decided in mtkswitch_probe() */
sc->numports = MTKSWITCH_MAX_PORTS;
sc->numphys = MTKSWITCH_MAX_PHYS;
- sc->cpuport = MTKSWITCH_CPU_PORT;
+ if (sc->sc_switchtype == MTK_SWITCH_MT7621 ||
+ sc->sc_switchtype == MTK_SWITCH_MT7621 ||
+ sc->sc_switchtype == MTK_SWITCH_MT7628 ||
+ sc->sc_switchtype == MTK_SWITCH_RT3050 ||
+ sc->sc_switchtype == MTK_SWITCH_RT3352 ||
+ sc->sc_switchtype == MTK_SWITCH_RT5350)
+ {
+ /* mt7621/21/28/3050/3352/5350 has 6 ports */
+ sc->cpuport = 6;
+ }
+ else if (sc->sc_switchtype == MTK_SWITCH_MT7531)
+ {
+ /*bpi-r2-pro has cpu port 5*/
+ sc->cpuport = 5;
+ }
+
sc->sc_dev = dev;
/* Attach switch related functions */
@@ -182,11 +228,21 @@
}
if (sc->sc_switchtype == MTK_SWITCH_MT7620 ||
- sc->sc_switchtype == MTK_SWITCH_MT7621)
+ sc->sc_switchtype == MTK_SWITCH_MT7621) {
mtk_attach_switch_mt7620(sc);
- else
+ } else if (sc->sc_switchtype == MTK_SWITCH_MT7531) {
+ mtk_attach_switch_mt7531(sc);
+ } else {
mtk_attach_switch_rt3050(sc);
+ }
+ if (sc->sc_switchtype == MTK_SWITCH_MT7621 ||
+ sc->sc_switchtype == MTK_SWITCH_MT7621 ||
+ sc->sc_switchtype == MTK_SWITCH_MT7628 ||
+ sc->sc_switchtype == MTK_SWITCH_RT3050 ||
+ sc->sc_switchtype == MTK_SWITCH_RT3352 ||
+ sc->sc_switchtype == MTK_SWITCH_RT5350)
+ {
/* Allocate resources */
rid = 0;
sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
@@ -195,6 +251,7 @@
device_printf(dev, "could not map memory\n");
return (ENXIO);
}
+ }
mtx_init(&sc->sc_mtx, "mtkswitch", NULL, MTX_DEF);
@@ -232,7 +289,17 @@
return (err);
bus_identify_children(dev);
+
+ if (sc->sc_switchtype == MTK_SWITCH_MT7621 ||
+ sc->sc_switchtype == MTK_SWITCH_MT7621 ||
+ sc->sc_switchtype == MTK_SWITCH_MT7628 ||
+ sc->sc_switchtype == MTK_SWITCH_RT3050 ||
+ sc->sc_switchtype == MTK_SWITCH_RT3352 ||
+ sc->sc_switchtype == MTK_SWITCH_RT5350)
+ {
bus_enumerate_hinted_children(dev);
+ }
+
bus_attach_children(dev);
callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
@@ -241,9 +308,14 @@
mtkswitch_tick(sc);
MTKSWITCH_UNLOCK(sc);
+ if (sc->sc_switchtype == MTK_SWITCH_MT7531) {
+ mt7531_sysctl_attach(sc);
+ }
+
return (0);
}
+
static int
mtkswitch_detach(device_t dev)
{
@@ -591,6 +663,14 @@
return (sc->hal.mtkswitch_phy_read(dev, phy, reg));
}
+static int
+mtkswitch_readphy_mii(device_t dev, int phy, int reg)
+{
+ struct mtkswitch_softc *sc = device_get_softc(dev);
+
+ return (sc->hal.mtkswitch_phy_read(dev, phy, reg));
+}
+
static int
mtkswitch_writephy(device_t dev, int phy, int reg, int val)
{
@@ -599,6 +679,14 @@
return (sc->hal.mtkswitch_phy_write(dev, phy, reg, val));
}
+static int
+mtkswitch_writephy_mii(device_t dev, int phy, int reg, int val)
+{
+ struct mtkswitch_softc *sc = device_get_softc(dev);
+
+ return (sc->hal.mtkswitch_phy_write(dev, phy, reg, val));
+}
+
static int
mtkswitch_readreg(device_t dev, int addr)
{
@@ -617,6 +705,7 @@
static device_method_t mtkswitch_methods[] = {
/* Device interface */
+ DEVMETHOD(device_identify, mtkswitch_identify),
DEVMETHOD(device_probe, mtkswitch_probe),
DEVMETHOD(device_attach, mtkswitch_attach),
DEVMETHOD(device_detach, mtkswitch_detach),
@@ -625,13 +714,13 @@
DEVMETHOD(bus_add_child, device_add_child_ordered),
/* MII interface */
- DEVMETHOD(miibus_readreg, mtkswitch_readphy),
- DEVMETHOD(miibus_writereg, mtkswitch_writephy),
+ DEVMETHOD(miibus_readreg, mtkswitch_readphy_mii),
+ DEVMETHOD(miibus_writereg, mtkswitch_writephy_mii),
DEVMETHOD(miibus_statchg, mtkswitch_statchg),
/* MDIO interface */
- DEVMETHOD(mdio_readreg, mtkswitch_readphy),
- DEVMETHOD(mdio_writereg, mtkswitch_writephy),
+ //DEVMETHOD(mdio_readreg, mtkswitch_readphy),
+ //DEVMETHOD(mdio_writereg, mtkswitch_writephy),
/* ehterswitch interface */
DEVMETHOD(etherswitch_lock, mtkswitch_lock),
@@ -648,16 +737,28 @@
DEVMETHOD(etherswitch_getconf, mtkswitch_getconf),
DEVMETHOD(etherswitch_setconf, mtkswitch_setconf),
+ DEVMETHOD(etherswitch_fetch_table, mt7531_atu_fetch_table),
+ DEVMETHOD(etherswitch_fetch_table_entry, mt7531_atu_fetch_table_entry),
+
DEVMETHOD_END
};
DEFINE_CLASS_0(mtkswitch, mtkswitch_driver, mtkswitch_methods,
sizeof(struct mtkswitch_softc));
-DRIVER_MODULE(mtkswitch, simplebus, mtkswitch_driver, 0, 0);
+//#ifndef MT7531
+//DRIVER_MODULE(mtkswitch, simplebus, mtkswitch_driver, 0, 0);
+//DRIVER_MODULE(miibus, mtkswitch, miibus_driver, 0, 0);
+//DRIVER_MODULE(mdio, mtkswitch, mdio_driver, 0, 0);
+//DRIVER_MODULE(etherswitch, mtkswitch, etherswitch_driver, 0, 0);
+//MODULE_VERSION(mtkswitch, 1);
+//MODULE_DEPEND(mtkswitch, miibus, 1, 1, 1);
+//MODULE_DEPEND(mtkswitch, etherswitch, 1, 1, 1);
+//MODULE_DEPEND(mtkswitch, mdio, 1, 1, 1); //
+//#else
+DRIVER_MODULE(mtkswitch, mdio, mtkswitch_driver, 0, 0);
DRIVER_MODULE(miibus, mtkswitch, miibus_driver, 0, 0);
-DRIVER_MODULE(mdio, mtkswitch, mdio_driver, 0, 0);
DRIVER_MODULE(etherswitch, mtkswitch, etherswitch_driver, 0, 0);
MODULE_VERSION(mtkswitch, 1);
-MODULE_DEPEND(mtkswitch, miibus, 1, 1, 1);
-MODULE_DEPEND(mtkswitch, etherswitch, 1, 1, 1);
+MODULE_DEPEND(mtkswitch, mdio, 1, 1, 1);
+//#endif
\ No newline at end of file
diff --git a/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7531.h b/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7531.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7531.h
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 2023 Priit Trees.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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$
+ */
+
+#ifndef __MTKSWITCH_MT7631_H__
+#define __MTKSWITCH_MT7631_H__
+
+#define MTKSWITCH_ATC 0x0080
+#define ATC_BUSY (1u<<15)
+#define ATC_AC_MAT_NON_STATIC_MACS (4u<<8)
+#define ATC_AC_CMD_CLEAN (2u<<0)
+
+#define MTKSWITCH_VTCR 0x0090
+#define VTCR_BUSY (1u<<31)
+#define VTCR_FUNC_VID_READ (0u<<12)
+#define VTCR_FUNC_VID_WRITE (1u<<12)
+#define VTCR_FUNC_VID_INVALID (2u<<12)
+#define VTCR_FUNC_VID_VALID (3u<<12)
+#define VTCR_IDX_INVALID (1u<<16)
+#define VTCR_VID_MASK 0xfff
+
+#define MTKSWITCH_VAWD1 0x0094
+#define VAWD1_IVL_MAC (1u<<30)
+#define VAWD1_VTAG_EN (1u<<28)
+#define VAWD1_MEMBER_OFF 16
+#define VAWD1_MEMBER_MASK 0xff
+#define VAWD1_FID_OFFSET 1
+#define VAWD1_VALID (1u<<0)
+
+#define MTKSWITCH_VAWD2 0x0098
+#define VAWD2_PORT_UNTAGGED(p) (0u<<((p)*2))
+#define VAWD2_PORT_TAGGED(p) (2u<<((p)*2))
+#define VAWD2_PORT_MASK(p) (3u<<((p)*2))
+
+#define MTKSWITCH_PIAC 0x701C
+#define PIAC_PHY_ACS_ST (1u<<31)
+#define PIAC_MDIO_REG_ADDR_OFF 25
+#define PIAC_MDIO_PHY_ADDR_OFF 20
+#define PIAC_MDIO_CMD_WRITE (1u<<18)
+#define PIAC_MDIO_CMD_READ (2u<<18)
+#define PIAC_MDIO_ST (1u<<16)
+#define PIAC_MDIO_RW_DATA_MASK 0xffff
+
+#define MTKSWITCH_PORTREG(r, p) ((r) + ((p) * 0x100))
+
+#define MTKSWITCH_PCR(x) MTKSWITCH_PORTREG(0x2004, (x))
+#define PCR_PORT_VLAN_SECURE (3u<<0)
+
+#define MTKSWITCH_PVC(x) MTKSWITCH_PORTREG(0x2010, (x))
+#define PVC_VLAN_ATTR_MASK (3u<<6)
+
+#define MTKSWITCH_PPBV1(x) MTKSWITCH_PORTREG(0x2014, (x))
+#define MTKSWITCH_PPBV2(x) MTKSWITCH_PORTREG(0x2018, (x))
+#define PPBV_VID(v) (((v)<<16) | (v))
+#define PPBV_VID_FROM_REG(x) ((x) & 0xfff)
+#define PPBV_VID_MASK 0xfff
+
+#define MTKSWITCH_PMCR(x) MTKSWITCH_PORTREG(0x3000, (x))
+#define PMCR_FORCE_LINK (1u<<0)
+#define PMCR_FORCE_DPX (1u<<1)
+#define PMCR_FORCE_SPD_1000 (2u<<2)
+#define PMCR_FORCE_TX_FC (1u<<4)
+#define PMCR_FORCE_RX_FC (1u<<5)
+#define PMCR_BACKPR_EN (1u<<8)
+#define PMCR_BKOFF_EN (1u<<9)
+#define PMCR_MAC_RX_EN (1u<<13)
+#define PMCR_MAC_TX_EN (1u<<14)
+#define PMCR_MAC_MODE (1u<<16)
+#define PMCR_IPG_CFG_RND (1u<<18)
+#define MT7531_PMCR_FORCE_TX_FC (1u<<27)
+#define MT7531_PMCR_FORCE_RX_FC (1u<<28)
+#define MT7531_PMCR_FORCE_DPX (1u<<29)
+#define MT7531_PMCR_FORCE_SPX (1u<<30)
+#define MT7531_PMCR_FORCE_LINK (1u<<31)
+#define MT7631_PMCR_FORCE_MODE (MT7531_PMCR_FORCE_TX_FC | \
+ MT7531_PMCR_FORCE_RX_FC | MT7531_PMCR_FORCE_DPX | \
+ MT7531_PMCR_FORCE_SPX | MT7531_PMCR_FORCE_LINK)
+#define PMCR_CFG_DEFAULT (PMCR_BACKPR_EN | PMCR_BKOFF_EN | \
+ PMCR_MAC_RX_EN | PMCR_MAC_TX_EN | PMCR_IPG_CFG_RND | \
+ PMCR_FORCE_RX_FC | PMCR_FORCE_TX_FC)
+
+#define MTKSWITCH_PMSR(x) MTKSWITCH_PORTREG(0x3008, (x))
+#define PMSR_MAC_LINK_STS (1u<<0)
+#define PMSR_MAC_DPX_STS (1u<<1)
+#define PMSR_MAC_SPD_STS (3u<<2)
+#define PMSR_MAC_SPD(x) (((x)>>2) & 0x3)
+#define PMSR_MAC_SPD_10 0
+#define PMSR_MAC_SPD_100 1
+#define PMSR_MAC_SPD_1000 2
+#define PMSR_TX_FC_STS (1u<<4)
+#define PMSR_RX_FC_STS (1u<<5)
+#define MTKSWITCH_REG_ADDR(r) (((r) >> 6) & 0x3ff)
+#define MTKSWITCH_REG_LO(r) (((r) >> 2) & 0xf)
+#define MTKSWITCH_REG_HI(r) (1 << 4)
+#define MTKSWITCH_VAL_LO(v) ((v) & 0xffff)
+#define MTKSWITCH_VAL_HI(v) (((v) >> 16) & 0xffff)
+#define MTKSWITCH_GLOBAL_PHY 31
+#define MTKSWITCH_GLOBAL_REG 31
+
+#define MTKSWITCH_STRAP 0x7800
+#define MTKSWITCH_SWSTRAP 0x7804
+#define STRAP_TM_STRAP (1u<<0)
+#define STRAP_EEPROM_MODE (1u<<1)
+#define STRAP_PWR_ON_LIGHT (1u<<2)
+#define STRAP_PWR_ON_PLL (1u<<3)
+#define STRAP_EEE (1u<<4)
+#define STRAP_AUTO_EEPROM (1u<<5)
+#define STRAP_PHY (1u<<6)
+#define STRAP_XTAL (1u<<7) /* 1 25MHz 0 40MHz */
+#define SWSTRAP_CH_STRAP (1u<<9)
+
+#define MT7531_TOP_SIG_SR 0x780c
+#define PAD_DUAL_SGMII (1u << 1)
+#define PAD_MCM_SMI (1u << 0)
+
+#define MTKSWITCH_CREV 0x781C
+#define CREV_CHIP_NAME(x) ((x >> 16) & 0xFFFF)
+#define CREV_CHIP_REV(x) (x & 0xF)
+
+#define MT7531_PLLGP 0x7820
+#define PLLGP_COREPLL (1u << 2)
+#define PLLGP_SW_CLKSW (1u << 1)
+#define PLLGP_SW_PLLGP (1u << 0)
+
+#define MT7531_PLLGP_CR0 0x78a8
+#define PLLGP_RG_COREPLL (1u << 22)
+#define PLLGP_RG_COREPLL_POSDIV_S 23
+#define PLLGP_RG_COREPLL_POSDIV_M 0x3800000
+#define PLLGP_RG_COREPLL_SDM_PCW_S 1
+#define PLLGP_RG_COREPLL_SDM_PCW_M 0x3ffffe
+#define MT7531_XTAL_25MHZ 0x140000
+#define MT7531_XTAL_40MHZ 0x190000
+#define PLLGP_RG_COREPLL_SDM_PCW_CHG (1u << 0)
+
+/* RGMII and SGMII PLL clock */
+#define MT7531_ANA_PLLGP_CR2 0x78b0
+#define MT7531_ANA_PLLGP_CR5 0x78bc
+
+#define MT7531_ATC 0x80
+#define MT7531_AC_CMD_RD 0x00
+#define MT7531_AC_CMD_RW 0x01
+#define MT7531_AC_CMD_CLN 0x02
+#define MT7531_AC_CMD_SSC 0x04
+#define MT7531_AC_CMD_NSC 0x05
+
+#define MT7531_ATC_ADDR_INVLD (1u<<12)
+#define MT7531_ATC_SRCH_HIT (1u<<13)
+#define MT7531_ATC_SRCH_END (1u<<14)
+#define MT7531_ATC_BUSY (1u<<15)
+#define MT7531_TSRA1 0x84
+#define MT7531_TSRA2 0x88
+#define MT7531_TSRD 0x8C
+
+#endif /* __MTKSWITCH_MT7531_H__ */
\ No newline at end of file
diff --git a/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7531.c b/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7531.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7531.c
@@ -0,0 +1,946 @@
+/*-
+ * Copyright (c) 2023 Priit Trees
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 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$
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <machine/bus.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/mdio/mdio.h>
+
+#include <dev/etherswitch/etherswitch.h>
+#include <dev/etherswitch/mtkswitch/mtkswitchvar.h>
+#include <dev/etherswitch/mtkswitch/mtkswitch_mt7531.h>
+
+#include "etherswitch_if.h"
+#include "miibus_if.h"
+#include "mdio_if.h"
+
+#define MDIO_READ(dev, addr, reg) \
+ MDIO_READREG(device_get_parent(dev), (addr), (reg))
+#define MDIO_WRITE(dev, addr, reg, val) \
+ MDIO_WRITEREG(device_get_parent(dev), (addr), (reg), (val))
+
+static int
+mtkswitch_phy_read_locked(struct mtkswitch_softc *sc, int phy, int reg)
+{
+ uint32_t data;
+
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_PIAC) & PIAC_PHY_ACS_ST);
+
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_PIAC,
+ PIAC_PHY_ACS_ST | PIAC_MDIO_ST | (reg << PIAC_MDIO_REG_ADDR_OFF) |
+ (phy << PIAC_MDIO_PHY_ADDR_OFF) | PIAC_MDIO_CMD_READ);
+
+ while ((data = sc->hal.mtkswitch_read(sc,MTKSWITCH_PIAC)) & PIAC_PHY_ACS_ST);
+
+ return ((int)(data & PIAC_MDIO_RW_DATA_MASK));
+
+}
+
+static int
+mtkswitch_phy_read(device_t dev, int phy, int reg)
+{
+ struct mtkswitch_softc *sc = device_get_softc(dev);
+ int data;
+
+ if ((phy < 0 || phy >= 32) || (reg < 0 || reg >= 32))
+ return (ENXIO);
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+ MTKSWITCH_LOCK(sc);
+ data = mtkswitch_phy_read_locked(sc, phy, reg);
+ MTKSWITCH_UNLOCK(sc);
+
+ return (data);
+}
+
+static int
+mtkswitch_phy_write_locked(struct mtkswitch_softc *sc, int phy, int reg,
+ int val)
+{
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_PIAC,
+ PIAC_PHY_ACS_ST | PIAC_MDIO_ST | (reg << PIAC_MDIO_REG_ADDR_OFF) |
+ (phy << PIAC_MDIO_PHY_ADDR_OFF) | PIAC_MDIO_CMD_WRITE |
+ (val & PIAC_MDIO_RW_DATA_MASK));
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_PIAC) & PIAC_PHY_ACS_ST);
+
+ return (0);
+}
+
+static int
+mtkswitch_phy_write(device_t dev, int phy, int reg, int val)
+{
+ struct mtkswitch_softc *sc = device_get_softc(dev);
+ int res;
+
+ if ((phy < 0 || phy >= 32) || (reg < 0 || reg >= 32))
+ return (ENXIO);
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+ MTKSWITCH_LOCK(sc);
+ res = mtkswitch_phy_write_locked(sc, phy, reg, val);
+ MTKSWITCH_UNLOCK(sc);
+
+ return (res);
+}
+
+static uint32_t
+mtkswitch_reg_read32(struct mtkswitch_softc *sc, int reg)
+{
+ uint32_t low, hi;
+
+ MDIO_WRITE(sc->sc_dev, MTKSWITCH_GLOBAL_PHY,
+ MTKSWITCH_GLOBAL_REG, MTKSWITCH_REG_ADDR(reg));
+ low = MDIO_READ(sc->sc_dev, MTKSWITCH_GLOBAL_PHY,
+ MTKSWITCH_REG_LO(reg));
+ hi = MDIO_READ(sc->sc_dev, MTKSWITCH_GLOBAL_PHY,
+ MTKSWITCH_REG_HI(reg));
+ return (low | (hi << 16));
+}
+
+static uint32_t
+mtkswitch_reg_write32(struct mtkswitch_softc *sc, int reg, uint32_t val)
+{
+
+ MDIO_WRITE(sc->sc_dev, MTKSWITCH_GLOBAL_PHY,
+ MTKSWITCH_GLOBAL_REG, MTKSWITCH_REG_ADDR(reg));
+ MDIO_WRITE(sc->sc_dev, MTKSWITCH_GLOBAL_PHY,
+ MTKSWITCH_REG_LO(reg), MTKSWITCH_VAL_LO(val));
+ MDIO_WRITE(sc->sc_dev, MTKSWITCH_GLOBAL_PHY,
+ MTKSWITCH_REG_HI(reg), MTKSWITCH_VAL_HI(val));
+ return (0);
+}
+
+static int
+mtkswitch_reg_read(device_t dev, int reg)
+{
+
+ struct mtkswitch_softc *sc = device_get_softc(dev);
+ uint32_t val;
+
+ val = sc->hal.mtkswitch_read(sc, MTKSWITCH_REG32(reg));
+ return val;
+}
+
+static int
+mtkswitch_reg_write(device_t dev, int reg, int val)
+{
+ struct mtkswitch_softc *sc = device_get_softc(dev);
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_REG32(reg), val);
+
+ return (0);
+}
+
+static int
+mtkswitch_reset(struct mtkswitch_softc *sc)
+{
+
+ /* We don't reset the switch for now */
+ return (0);
+}
+
+static void
+mtkswitch_setup_xtal(struct mtkswitch_softc *sc, uint32_t xtal)
+{
+ uint32_t val;
+
+ /* Step 1 : Disable MT7531 COREPLL */
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP);
+ val &= ~PLLGP_COREPLL;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP, val);
+
+ /* Step 2: switch to XTAL output */
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP);
+ val |= PLLGP_SW_CLKSW;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP, val);
+
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP_CR0);
+ val &= ~PLLGP_RG_COREPLL;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP_CR0, val);
+
+ /* Step 3: disable PLLGP and enable program PLLGP */
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP);
+ val |= PLLGP_SW_PLLGP;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP, val);
+
+ /* Step 4: program COREPLL output frequency to 500MHz */
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP_CR0);
+ val &= ~PLLGP_RG_COREPLL_POSDIV_M;
+ val |= 2 << PLLGP_RG_COREPLL_POSDIV_S;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP_CR0, val);
+ DELAY(35);
+
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP_CR0);
+ val &= ~PLLGP_RG_COREPLL_SDM_PCW_M;
+ val |= xtal << PLLGP_RG_COREPLL_SDM_PCW_S;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP_CR0, val);
+
+ /* Set feedback divide ratio update signal to high */
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP_CR0);
+ val |= PLLGP_RG_COREPLL_SDM_PCW_CHG;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP_CR0, val);
+ /* Wait for at least 16 XTAL clocks */
+ DELAY(20);
+
+ /* Step 5: set feedback divide ratio update signal to low */
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP_CR0);
+ val &= ~PLLGP_RG_COREPLL_SDM_PCW_CHG;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP_CR0, val);
+
+ /* Enable 325M clock for SGMII */
+ sc->hal.mtkswitch_write(sc, MT7531_ANA_PLLGP_CR5, 0xad0000);
+
+ /* Enable 250SSC clock for RGMII */
+ sc->hal.mtkswitch_write(sc, MT7531_ANA_PLLGP_CR2, 0x4f40000);
+
+ /* Step 6: Enable MT7531 PLL */
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP_CR0);
+ val |= PLLGP_RG_COREPLL;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP_CR0, val);
+
+ val = sc->hal.mtkswitch_read(sc, MT7531_PLLGP);
+ val |= PLLGP_COREPLL;
+ sc->hal.mtkswitch_write(sc, MT7531_PLLGP, val);
+ DELAY(35);
+}
+
+static int
+mtkswitch_hw_setup(struct mtkswitch_softc *sc)
+{
+
+ /*
+ * TODO: parse the device tree and see if we need to configure
+ * ports, etc. differently. For now we fallback to defaults.
+ */
+
+ uint32_t crev, topsig, val, xtal;
+
+ crev = sc->hal.mtkswitch_read(sc, MTKSWITCH_CREV);
+ topsig = sc->hal.mtkswitch_read(sc, MT7531_TOP_SIG_SR);
+
+ /* Print chip name and revision. In future need
+ * to move other place and mayby can use it.
+ */
+ device_printf(sc->sc_dev, "chip %s rev 0x%x\n",
+ topsig & PAD_DUAL_SGMII ? "MT7531AE" : "MT7531BE",
+ CREV_CHIP_REV(crev));
+
+ /* MT7531AE has got two SGMII units. One for port 5, one for port 6.
+ * MT7531BE has got only one SGMII unit which is for port 6.
+ */
+ if (topsig & PAD_DUAL_SGMII)
+ return (0);
+
+ /* on linux rev is checked. if it bigger than 0
+ * and SMI is enabled then 40MHz other 25MHz
+ * else hwstrap is set then 25MHz other 40MHz
+ */
+
+ val = sc->hal.mtkswitch_read(sc, MTKSWITCH_STRAP);
+ if (val & STRAP_XTAL) /* starp is set 25MHz */
+ xtal = MT7531_XTAL_25MHZ;
+ else /* starp is not set. Then is used 40MHz */
+ xtal = MT7531_XTAL_40MHZ;
+
+ mtkswitch_setup_xtal(sc, xtal);
+
+ /* Called early and hence unlocked */
+ return (0);
+}
+
+static int
+mtkswitch_hw_global_setup(struct mtkswitch_softc *sc)
+{
+ /* Currently does nothing */
+
+ /* Called early and hence unlocked */
+ return (0);
+}
+
+static void
+mtkswitch_port_init(struct mtkswitch_softc *sc, int port)
+{
+ uint32_t val;
+
+ /* Called early and hence unlocked */
+
+ /* Set the port to secure mode */
+ val = sc->hal.mtkswitch_read(sc, MTKSWITCH_PCR(port));
+ val |= PCR_PORT_VLAN_SECURE;
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_PCR(port), val);
+
+ /* Set port's vlan_attr to user port */
+ val = sc->hal.mtkswitch_read(sc, MTKSWITCH_PVC(port));
+ val &= ~PVC_VLAN_ATTR_MASK;
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_PVC(port), val);
+
+ val = PMCR_CFG_DEFAULT;
+ if (port == sc->cpuport){
+ val |= PMCR_FORCE_LINK | PMCR_FORCE_DPX | PMCR_FORCE_SPD_1000 |
+ MT7631_PMCR_FORCE_MODE | PMCR_MAC_MODE;
+ }
+ /* Set port's MAC to default settings */
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_PMCR(port), val);
+}
+
+static uint32_t
+mtkswitch_get_port_status(struct mtkswitch_softc *sc, int port)
+{
+ uint32_t val, res, tmp;
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+ res = 0;
+ val = sc->hal.mtkswitch_read(sc, MTKSWITCH_PMSR(port));
+
+ if (val & PMSR_MAC_LINK_STS)
+ res |= MTKSWITCH_LINK_UP;
+ if (val & PMSR_MAC_DPX_STS)
+ res |= MTKSWITCH_DUPLEX;
+ tmp = PMSR_MAC_SPD(val);
+ if (tmp == 0)
+ res |= MTKSWITCH_SPEED_10;
+ else if (tmp == 1)
+ res |= MTKSWITCH_SPEED_100;
+ else if (tmp == 2)
+ res |= MTKSWITCH_SPEED_1000;
+ if (val & PMSR_TX_FC_STS)
+ res |= MTKSWITCH_TXFLOW;
+ if (val & PMSR_RX_FC_STS)
+ res |= MTKSWITCH_RXFLOW;
+
+ return (res);
+}
+
+static int
+mtkswitch_atu_flush(struct mtkswitch_softc *sc)
+{
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* Flush all non-static MAC addresses */
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_ATC) & ATC_BUSY);
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_ATC, ATC_BUSY |
+ ATC_AC_MAT_NON_STATIC_MACS | ATC_AC_CMD_CLEAN);
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_ATC) & ATC_BUSY);
+
+ return (0);
+}
+
+static int
+mtkswitch_port_vlan_setup(struct mtkswitch_softc *sc, etherswitch_port_t *p)
+{
+ int err;
+
+ /*
+ * Port behaviour wrt tag/untag/stack is currently defined per-VLAN.
+ * So we say we don't support it here.
+ */
+ if ((p->es_flags & (ETHERSWITCH_PORT_DOUBLE_TAG |
+ ETHERSWITCH_PORT_ADDTAG | ETHERSWITCH_PORT_STRIPTAG)) != 0)
+ return (ENOTSUP);
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+ MTKSWITCH_LOCK(sc);
+
+ /* Set the PVID */
+ if (p->es_pvid != 0) {
+ err = sc->hal.mtkswitch_vlan_set_pvid(sc, p->es_port,
+ p->es_pvid);
+ if (err != 0) {
+ MTKSWITCH_UNLOCK(sc);
+ return (err);
+ }
+ }
+
+ MTKSWITCH_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+mtkswitch_port_vlan_get(struct mtkswitch_softc *sc, etherswitch_port_t *p)
+{
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+ MTKSWITCH_LOCK(sc);
+
+ /* Retrieve the PVID */
+ sc->hal.mtkswitch_vlan_get_pvid(sc, p->es_port, &p->es_pvid);
+
+ /*
+ * Port flags are not supported at the moment.
+ * Port's tag/untag/stack behaviour is defined per-VLAN.
+ */
+ p->es_flags = 0;
+
+ MTKSWITCH_UNLOCK(sc);
+
+ return (0);
+}
+
+static void
+mtkswitch_invalidate_vlan(struct mtkswitch_softc *sc, uint32_t vid)
+{
+
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_VTCR, VTCR_BUSY |
+ VTCR_FUNC_VID_INVALID | (vid & VTCR_VID_MASK));
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
+}
+
+static int
+mtkswitch_update_vlan_entry(struct mtkswitch_softc *sc, uint16_t vid,
+ uint8_t members, uint16_t untag)
+{
+ uint32_t val;
+
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
+
+ /* We use FID 0 */
+ val = VAWD1_IVL_MAC | VAWD1_VTAG_EN | VAWD1_VALID |
+ ((members & VAWD1_MEMBER_MASK) << VAWD1_MEMBER_OFF);
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_VAWD1, val);
+
+ /* Set tagged ports */
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_VAWD2, (untag &0xFFFF));
+
+ /* Write the VLAN entry */
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_VTCR, VTCR_BUSY |
+ VTCR_FUNC_VID_WRITE | (vid & VTCR_VID_MASK));
+ while ((val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR)) & VTCR_BUSY);
+ return (val);
+}
+
+static void
+mtkswitch_vlan_init_hw(struct mtkswitch_softc *sc)
+{
+ uint8_t members = 0;
+ uint32_t i;
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+ MTKSWITCH_LOCK(sc);
+ /* Reset all VLANs to defaults first */
+ for (i = 0; i < sc->info.es_nvlangroups; i++) {
+ mtkswitch_invalidate_vlan(sc, i);
+ }
+
+ /* Now, add all ports as untagged members of VLAN 1 */
+ for (i = 0; i < sc->info.es_nports; i++)
+ members |= ((1u)<<(i));
+
+ mtkswitch_update_vlan_entry(sc, 1, members, 0);
+
+ /* Reset internal VLAN table. */
+ for (i = 0; i < nitems(sc->vlans); i++)
+ sc->vlans[i] = 0;
+
+ sc->vlans[0] = 1;
+
+ /* Set all port PVIDs to 1 */
+ for (i = 0; i < sc->info.es_nports; i++) {
+ sc->hal.mtkswitch_vlan_set_pvid(sc, i, 1);
+ }
+
+ MTKSWITCH_UNLOCK(sc);
+}
+
+static int
+mtkswitch_vlan_getvgroup(struct mtkswitch_softc *sc, etherswitch_vlangroup_t *v)
+{
+ uint32_t val, i;
+ MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+
+ /* Reset the member ports. */
+ v->es_untagged_ports = 0;
+ v->es_member_ports = 0;
+
+ /* Not supported for now */
+ v->es_fid = 0;
+
+ MTKSWITCH_LOCK(sc);
+ v->es_vid = sc->vlans[v->es_vlangroup];
+
+ if (v->es_vid == 0)
+ {
+ MTKSWITCH_UNLOCK(sc);
+ return (0);
+ }
+ while (sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR) & VTCR_BUSY);
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_VTCR, VTCR_BUSY |
+ VTCR_FUNC_VID_READ | (v->es_vid & VTCR_VID_MASK));
+ while ((val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VTCR)) & VTCR_BUSY);
+ if (val & VTCR_IDX_INVALID) {
+ MTKSWITCH_UNLOCK(sc);
+ return (0);
+ }
+
+ val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VAWD1);
+ if (val & VAWD1_VALID)
+ v->es_vid |= ETHERSWITCH_VID_VALID;
+ else {
+ MTKSWITCH_UNLOCK(sc);
+ return (0);
+ }
+ v->es_member_ports = (val >> VAWD1_MEMBER_OFF) & VAWD1_MEMBER_MASK;
+
+ val = sc->hal.mtkswitch_read(sc, MTKSWITCH_VAWD2);
+ for (i = 0; i < sc->info.es_nports; i++) {
+ if ((val & VAWD2_PORT_MASK(i)) == VAWD2_PORT_UNTAGGED(i))
+ v->es_untagged_ports |= (1<<i);
+ }
+
+ MTKSWITCH_UNLOCK(sc);
+ return (0);
+}
+
+static int
+mtkswitch_vlan_setvgroup(struct mtkswitch_softc *sc, etherswitch_vlangroup_t *v)
+{
+ uint16_t untagged_ports = 0;
+ uint32_t val;
+ int i, vlan;
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+
+ vlan = v->es_vid & ETHERSWITCH_VID_MASK;
+ if (vlan == 0)
+ {
+ mtkswitch_invalidate_vlan(sc, sc->vlans[v->es_vlangroup]);
+ sc->vlans[v->es_vlangroup] = 0;
+ return (0);
+ }
+
+ /* Is this VLAN already in table ? */
+ for (i = 0; i < sc->info.es_nvlangroups; i++)
+ if (i != v->es_vlangroup && vlan == sc->vlans[i])
+ return (EINVAL);
+
+ sc->vlans[v->es_vlangroup] = vlan;
+
+ /* We currently don't support FID */
+ if (v->es_fid != 0)
+ return (EINVAL);
+
+ MTKSWITCH_LOCK(sc);
+ /* Set tagged ports and Write the VLAN entry*/
+ for (i = 0; i < sc->info.es_nports; i++)
+ if (((1<<i) & v->es_untagged_ports) == 0)
+ untagged_ports |= VAWD2_PORT_TAGGED(i);
+
+ val = mtkswitch_update_vlan_entry(sc, v->es_vid, v->es_member_ports,
+ untagged_ports);
+ MTKSWITCH_UNLOCK(sc);
+
+ if (val & VTCR_IDX_INVALID)
+ return (EINVAL);
+
+ return (0);
+}
+
+static int
+mtkswitch_vlan_get_pvid(struct mtkswitch_softc *sc, int port, int *pvid)
+{
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ *pvid = sc->hal.mtkswitch_read(sc, MTKSWITCH_PPBV1(port));
+ *pvid = PPBV_VID_FROM_REG(*pvid);
+
+ return (0);
+}
+
+static int
+mtkswitch_vlan_set_pvid(struct mtkswitch_softc *sc, int port, int pvid)
+{
+ uint32_t val;
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+ val = PPBV_VID(pvid & PPBV_VID_MASK);
+ sc->hal.mtkswitch_write(sc, MTKSWITCH_PPBV1(port), val);
+
+ return (0);
+}
+
+extern void
+mtk_attach_switch_mt7531(struct mtkswitch_softc *sc)
+{
+
+ sc->portmap = 0x7f;
+
+ /* bpi-r2-pro port 0 not connected */
+ sc->phymap = 0x1f;
+
+ sc->info.es_nports = 7;
+ sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q;
+ sprintf(sc->info.es_name, "Mediatek GSW");
+ sc->hal.mtkswitch_read = mtkswitch_reg_read32;
+ sc->hal.mtkswitch_write = mtkswitch_reg_write32;
+ sc->info.es_nvlangroups = 4096;
+ sc->hal.mtkswitch_reset = mtkswitch_reset;
+ sc->hal.mtkswitch_hw_setup = mtkswitch_hw_setup;
+ sc->hal.mtkswitch_hw_global_setup = mtkswitch_hw_global_setup;
+ sc->hal.mtkswitch_port_init = mtkswitch_port_init;
+ sc->hal.mtkswitch_get_port_status = mtkswitch_get_port_status;
+ sc->hal.mtkswitch_atu_flush = mtkswitch_atu_flush;
+ sc->hal.mtkswitch_port_vlan_setup = mtkswitch_port_vlan_setup;
+ sc->hal.mtkswitch_port_vlan_get = mtkswitch_port_vlan_get;
+ sc->hal.mtkswitch_vlan_init_hw = mtkswitch_vlan_init_hw;
+ sc->hal.mtkswitch_vlan_getvgroup = mtkswitch_vlan_getvgroup;
+ sc->hal.mtkswitch_vlan_setvgroup = mtkswitch_vlan_setvgroup;
+ sc->hal.mtkswitch_vlan_get_pvid = mtkswitch_vlan_get_pvid;
+ sc->hal.mtkswitch_vlan_set_pvid = mtkswitch_vlan_set_pvid;
+ sc->hal.mtkswitch_phy_read = mtkswitch_phy_read;
+ sc->hal.mtkswitch_phy_write = mtkswitch_phy_write;
+ sc->hal.mtkswitch_reg_read = mtkswitch_reg_read;
+ sc->hal.mtkswitch_reg_write = mtkswitch_reg_write;
+}
+
+#define MT7530_PORT_MIB_COUNTER(x) (0x4000 + (x) * 0x100)
+
+struct mt7530_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+ const char *desc;
+};
+
+#define MIB_DESC(_s, _o, _n, _d)\
+{ \
+ .size = (_s), \
+ .offset = (_o), \
+ .name = (_n), \
+ .desc = (_d), \
+}
+
+// vaata üle
+static const
+struct mt7530_mib_desc mt7530_mib[] = {
+ MIB_DESC(1, 0x00, "tx_drop", "Transmit droped frames"),
+ MIB_DESC(1, 0x04, "tx_crcerrs", "Transmit CRC errors"),
+ MIB_DESC(1, 0x08, "tx_ucast_frames", "Transmit good unicast frames"),
+ MIB_DESC(1, 0x0c, "tx_mcast_frames", "Transmit good multicast frames"),
+ MIB_DESC(1, 0x10, "tx_bcast_frames", "Transmit good broadcast frames"),
+ MIB_DESC(1, 0x14, "tx_colls", "Transmit collisions"),
+ MIB_DESC(1, 0x18, "tx_single_colls", "Transmit single collisions"),
+ MIB_DESC(1, 0x1c, "tx_multi_colls", "Transmit multiple collisions"),
+ MIB_DESC(1, 0x20, "tx_deferred", "Transmit deferred frames"),
+ MIB_DESC(1, 0x24, "tx_late_colls", "Transmit late collisions"),
+ MIB_DESC(1, 0x28, "tx_excess_colls", "Transmit excessive collisions"),
+ MIB_DESC(1, 0x2c, "tx_pause_frames", "Transmit pause frames"),
+ MIB_DESC(1, 0x30, "tx_frames_64", "Transmit 64 bytes frames"),
+ MIB_DESC(1, 0x34, "tx_frames_65_127", "Transmit 65 to 127 bytes frames"),
+ MIB_DESC(1, 0x38, "tx_frames_128_255", "Transmit 128 to 255 bytes frames"),
+ MIB_DESC(1, 0x3c, "tx_frames_256_511", "Transmit 256 to 511 bytes frames"),
+ MIB_DESC(1, 0x40, "tx_frames_512_1023", "Transmit 512 to 1023 bytes frames"),
+ MIB_DESC(1, 0x44, "tx_frame_1024_max", "Transmit 1024 to max bytes frames"),
+ MIB_DESC(2, 0x48, "tx_bytes", "Transmit good bytes"),
+ MIB_DESC(1, 0x60, "rx_drop", "Receive droped frames"),
+ MIB_DESC(1, 0x64, "rx_pkts_filtered", "Receive frames is filtered"),
+ MIB_DESC(1, 0x68, "rx_ucast_frames", "Receive unicast frames"),
+ MIB_DESC(1, 0x6c, "rx_mcast_frames", "Receive multicast frames"),
+ MIB_DESC(1, 0x70, "rx_bcast_frames", "Receive broadcast frames"),
+ MIB_DESC(1, 0x74, "rx_align_errs", "Receive alignment errors"),
+ MIB_DESC(1, 0x78, "rx_crcerrs", "Receive CRC errors"),
+ MIB_DESC(1, 0x7c, "rx_runts", "Receive undersized frames"),
+ MIB_DESC(1, 0x80, "rx_fragments", "Receive fragmented frames"),
+ MIB_DESC(1, 0x84, "rx_oversize_frames", "Receive oversize frames"),
+ MIB_DESC(1, 0x88, "rx_jabbers", "Receive jabbers frames"),
+ MIB_DESC(1, 0x8c, "rx_pause_frames", "Receive pause control frames"),
+ MIB_DESC(1, 0x90, "rx_frames_64", "Receive 64 bytes frames"),
+ MIB_DESC(1, 0x94, "rx_frames_65_127", "Receive 65 to 127 bytes frames"),
+ MIB_DESC(1, 0x98, "rx_frames_128_255", "Receive 128 to 255 bytes frames"),
+ MIB_DESC(1, 0x9c, "rx_frames_256_511", "Receive 256 to 511 bytes frames"),
+ MIB_DESC(1, 0xa0, "rx_frames_512_1023", "Receive 512 to 1023 bytes frames"),
+ MIB_DESC(1, 0xa4, "rx_frame_1024_max", "Receive 1024 to max bytes frames"),
+ MIB_DESC(2, 0xa8, "rx_bytes", "Receive good bytes"),
+ MIB_DESC(1, 0xb0, "rx_ctrl_drop", "Receive droped frames"),
+ MIB_DESC(1, 0xb4, "rx_ingress_drop", "Receive droped by ingress rate limited"),
+ MIB_DESC(1, 0xb8, "rx_arl_drop", "Receive droped by ACL"),
+};
+
+static int64_t
+mt7531_hw_port_mib_read_count(struct mtkswitch_softc *sc, int port, int index)
+{
+ const struct mt7530_mib_desc *mib;
+ uint64_t val;
+ uint32_t reg, hi;
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ mib = &mt7530_mib[index];
+ reg = MT7530_PORT_MIB_COUNTER(port) + mib->offset;
+
+ val = sc->hal.mtkswitch_read(sc, reg);
+ if (mib->size == 2) {
+ hi = sc->hal.mtkswitch_read(sc, reg + 4);
+ val |= ((uint64_t) hi << 32);
+ }
+
+ return val;
+}
+
+static int
+mt7531_hw_port_mib_clear(struct mtkswitch_softc *sc, int port,
+ int index, uint32_t val)
+{
+ uint32_t reg;
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ reg = MT7530_PORT_MIB_COUNTER(port) + index;
+
+ return sc->hal.mtkswitch_write(sc, reg, val);
+}
+
+struct mt7531_sysctl_mib {
+ struct mtkswitch_softc *sc;
+ int index;
+ int port;
+};
+
+static int
+mt7531_sysctl_port_mib_read_count(SYSCTL_HANDLER_ARGS)
+{
+ struct mtkswitch_softc *sc;
+ uint64_t val = 0;
+ uint32_t reg = (uint32_t)arg2;
+ uint32_t port = ((reg >> 16) & 0xffff);
+ uint32_t index = (reg & 0xffff);
+
+ sc = (struct mtkswitch_softc *)arg1;
+ if (sc == NULL)
+ return (EINVAL);
+
+ if (index < 0 || index > nitems(mt7530_mib))
+ return (EINVAL);
+
+ if (port < 0 || port > MTKSWITCH_MAX_PORTS)
+ return (EINVAL);
+
+ //MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+ MTKSWITCH_LOCK(sc);
+ val = mt7531_hw_port_mib_read_count(sc, port, index);
+ MTKSWITCH_UNLOCK(sc);
+
+ return sysctl_handle_64(oidp, &val, 0, req);
+}
+
+static int
+mt7531_sysctl_port_mib_clear_count(SYSCTL_HANDLER_ARGS)
+{
+ struct mtkswitch_softc *sc;
+ uint32_t val = 0;
+ uint32_t reg = (uint32_t)arg2;
+ uint32_t port = ((reg >> 16) & 0xffff);
+ uint32_t index = (reg & 0xffff);
+ int error;
+
+ sc = (struct mtkswitch_softc *)arg1;
+ if (sc == NULL)
+ return (EINVAL);
+
+ //if (index < 0 || index > nitems(mt7530_mib))
+ // return (EINVAL);
+
+ if (port < 0 || port > MTKSWITCH_MAX_PORTS)
+ return (EINVAL);
+
+ error = sysctl_handle_32(oidp, &val, 0, req);
+
+ if (error || !req->newptr)
+ return (error);
+
+ //MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+ MTKSWITCH_LOCK(sc);
+ mt7531_hw_port_mib_clear(sc, port, index, val);
+ MTKSWITCH_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+mt7531_sysctl_attach(struct mtkswitch_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid *tree;
+ struct sysctl_oid *ptree;
+ struct sysctl_oid_list *children;
+ struct sysctl_oid_list *pchildren;
+ struct sysctl_oid_list *ichildren;
+
+ char port_num_buf[32];
+ uint32_t reg;
+ int index, port;
+
+ ctx = device_get_sysctl_ctx(sc->sc_dev);
+ children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
+
+ tree = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "port",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "ethernet stndard mib counters of ports");
+ pchildren = SYSCTL_CHILDREN(tree);
+
+ for (port = 0; port < MTKSWITCH_MAX_PORTS; port++) {
+ snprintf(port_num_buf, sizeof(port_num_buf), "%d", port);
+ ptree = SYSCTL_ADD_NODE(ctx, pchildren, port,
+ port_num_buf, CTLFLAG_RD | CTLFLAG_MPSAFE,
+ NULL, "port mib counters");
+ ichildren = SYSCTL_CHILDREN(ptree);
+ for (index = 0; index < nitems(mt7530_mib); index++) {
+ reg = ((port << 16) | index);
+
+ SYSCTL_ADD_PROC(ctx, ichildren, index,
+ mt7530_mib[index].name,
+ CTLTYPE_U64 | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, reg, mt7531_sysctl_port_mib_read_count,
+ "LU", mt7530_mib[index].desc);
+ }
+ reg = ((port << 16) | 0xD0);
+ SYSCTL_ADD_PROC(ctx, ichildren, OID_AUTO,
+ "clear_tx", CTLTYPE_UINT | CTLFLAG_WR | CTLFLAG_MPSAFE,
+ sc, reg, mt7531_sysctl_port_mib_clear_count,
+ "IU", "Clear TX Counters");
+ reg = ((port << 16) | 0xD4);
+ SYSCTL_ADD_PROC(ctx, ichildren, OID_AUTO,
+ "clear_rx", CTLTYPE_UINT | CTLFLAG_WR | CTLFLAG_MPSAFE,
+ sc, reg, mt7531_sysctl_port_mib_clear_count,
+ "IU", "Clear RX Counters");
+ }
+ return (0);
+}
+
+static int
+mt7531_arl_fetch_entry(struct mtkswitch_softc *sc, etherswitch_atu_entry_t *e)
+{
+
+ uint32_t tsra1, tsra2, tsrd;
+
+ tsra1 = sc->hal.mtkswitch_read(sc, MT7531_TSRA1);
+ tsra2 = sc->hal.mtkswitch_read(sc, MT7531_TSRA2);
+ tsrd = sc->hal.mtkswitch_read(sc, MT7531_TSRD);
+
+ /* MAC address */
+ e->es_macaddr[5] = ((tsra2 >> 16) & 0xFF);
+ e->es_macaddr[4] = ((tsra2 >> 24) & 0xFF);
+ e->es_macaddr[3] = ((tsra1 >> 0) & 0xFF);
+ e->es_macaddr[2] = ((tsra1 >> 8) & 0xFF);
+ e->es_macaddr[1] = ((tsra1 >> 16) & 0xFF);
+ e->es_macaddr[0] = ((tsra1 >> 24) & 0xFF);
+
+ /* Bitmask of ports this entry is for */
+ e->es_portmask = ((tsrd >> 4) & 0xFF);
+
+ return 0;
+}
+
+static void mt7531_is_atc_busy(struct mtkswitch_softc *sc)
+{
+ uint32_t val;
+ do {
+ val = sc->hal.mtkswitch_read(sc,MT7531_ATC);
+ } while(val & MT7531_ATC_BUSY);
+}
+
+int
+mt7531_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
+{
+ struct mtkswitch_softc *sc;
+ int nitems;
+ uint32_t val;
+
+ sc = device_get_softc(dev);
+
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ memset(&sc->atu.entries, 0, sizeof(sc->atu.entries));
+
+ table->es_nitems = 0;
+ nitems = 0;
+
+ MTKSWITCH_LOCK(sc);
+ sc->atu.count = 0;
+
+ sc->hal.mtkswitch_write(sc, MT7531_ATC,
+ (MT7531_ATC_BUSY | MT7531_AC_CMD_SSC));
+ mt7531_is_atc_busy(sc);
+ val = sc->hal.mtkswitch_read(sc, MT7531_ATC);
+ while(!((val) & MT7531_ATC_SRCH_END)) {
+ mt7531_arl_fetch_entry(sc, &sc->atu.entries[nitems]);
+ sc->atu.entries[nitems].id = nitems;
+ nitems++;
+ sc->hal.mtkswitch_write(sc, MT7531_ATC,
+ (MT7531_ATC_BUSY | MT7531_AC_CMD_NSC));
+ mt7531_is_atc_busy(sc);
+ val = sc->hal.mtkswitch_read(sc, MT7531_ATC);
+ }
+ sc->atu.count = nitems;
+ table->es_nitems = nitems;
+ MTKSWITCH_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+mt7531_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
+{
+ struct mtkswitch_softc *sc;
+ int id, err = 0;
+ sc = device_get_softc(dev);
+ MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+ id = e->id;
+
+ MTKSWITCH_LOCK(sc);
+ if (id > sc->atu.count) {
+ err = ENOENT;
+ goto done;
+ }
+ memcpy(e, &sc->atu.entries[id], sizeof(*e));
+ done:
+ MTKSWITCH_UNLOCK(sc);
+ return (err);
+}
\ No newline at end of file
diff --git a/sys/dev/etherswitch/mtkswitch/mtkswitchvar.h b/sys/dev/etherswitch/mtkswitch/mtkswitchvar.h
--- a/sys/dev/etherswitch/mtkswitch/mtkswitchvar.h
+++ b/sys/dev/etherswitch/mtkswitch/mtkswitchvar.h
@@ -35,6 +35,7 @@
MTK_SWITCH_MT7620,
MTK_SWITCH_MT7621,
MTK_SWITCH_MT7628,
+ MTK_SWITCH_MT7531,
} mtk_switch_type;
#define MTK_IS_SWITCH(_sc, _type) \
@@ -42,7 +43,10 @@
#define MTKSWITCH_MAX_PORTS 7
#define MTKSWITCH_MAX_PHYS 7
-#define MTKSWITCH_CPU_PORT 6
+#define MTKSWITCH_NUM_VLANS 4096
+/* Size of the ALR table in hardware */
+#define MTKSWITCH_NUM_ARL_ENTRIES 4096
+
#define MTKSWITCH_LINK_UP (1<<0)
#define MTKSWITCH_SPEED_MASK (3<<1)
@@ -70,6 +74,13 @@
struct callout callout_tick;
etherswitch_info_t info;
+ int vlans[MTKSWITCH_NUM_VLANS];
+ /* ARL (address resolution table) */
+ struct {
+ int count;
+ etherswitch_atu_entry_t entries[MTKSWITCH_NUM_ARL_ENTRIES];
+ } atu;
+
uint32_t vlan_mode;
struct {
@@ -158,5 +169,9 @@
extern void mtk_attach_switch_rt3050(struct mtkswitch_softc *);
extern void mtk_attach_switch_mt7620(struct mtkswitch_softc *);
+extern void mtk_attach_switch_mt7531(struct mtkswitch_softc *);
+extern int mt7531_sysctl_attach(struct mtkswitch_softc *sc);
+extern int mt7531_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table);
+extern int mt7531_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e);
#endif /* __MTKSWITCHVAR_H__ */
diff --git a/sys/dev/fdt/fdt_common.h b/sys/dev/fdt/fdt_common.h
--- a/sys/dev/fdt/fdt_common.h
+++ b/sys/dev/fdt/fdt_common.h
@@ -75,6 +75,7 @@
int fdt_foreach_mem_region(fdt_mem_region_cb, void *);
int fdt_foreach_reserved_mem(fdt_mem_region_cb, void *);
int fdt_foreach_reserved_region(fdt_mem_region_cb, void *);
+boolean_t fdt_find_ethernet_prop_switch(phandle_t ethernet, phandle_t node);
int fdt_get_phyaddr(phandle_t, device_t, int *, void **);
int fdt_get_range(phandle_t, int, u_long *, u_long *);
int fdt_immr_addr(vm_offset_t);
diff --git a/sys/dev/fdt/fdt_common.c b/sys/dev/fdt/fdt_common.c
--- a/sys/dev/fdt/fdt_common.c
+++ b/sys/dev/fdt/fdt_common.c
@@ -338,6 +338,27 @@
return (0);
}
+boolean_t
+fdt_find_ethernet_prop_switch(phandle_t ethernet, phandle_t node)
+{
+ boolean_t ret;
+ phandle_t child, switch_handle, switch_eth;
+ for (child = OF_child(node); child != 0; child = OF_peer(child)) {
+ if (OF_getencprop(child, "ethernet", (void*)&switch_handle,
+ sizeof(switch_handle)) > 0){
+ if (switch_handle > 0) {
+ switch_eth = OF_node_from_xref(switch_handle);
+ if (switch_eth == ethernet)
+ return (true);
+ }
+ }
+ ret = fdt_find_ethernet_prop_switch(ethernet, child);
+ if (ret != 0)
+ return (ret);
+ }
+ return (false);
+}
+
int
fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc)
{
diff --git a/sys/modules/mtkswitch/Makefile b/sys/modules/mtkswitch/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/mtkswitch/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/etherswitch/mtkswitch
+
+KMOD= mtkswitch
+SRCS= mtkswitch.c mtkswitch_mt7531.c
+
+SRCS+= bus_if.h etherswitch_if.h mdio_if.h miibus_if.h ofw_bus_if.h
+
+.include <bsd.kmod.mk>
+

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 19, 4:38 AM (16 m, 52 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31746359
Default Alt Text
D55013.diff (51 KB)

Event Timeline