svn commit: r299910 - head/sys/dev/etherswitch/mtkswitch

Stanislav Galabov sgalabov at FreeBSD.org
Mon May 16 07:00:51 UTC 2016


Author: sgalabov
Date: Mon May 16 07:00:49 2016
New Revision: 299910
URL: https://svnweb.freebsd.org/changeset/base/299910

Log:
  Introduce basic etherswitch support for Ralink SoCs
  
  This revision introduces basic support for the internal ESW switch found
  Ralink/Mediatek SoCs such as RT3050, RT3352, RT5350, MT7628; and GSW
  found in MT7620 and MT7621.
  
  It only supports 802.1q VLANs and doesn't support external PHYs at the
  moment (only the ones that are built into the switch itself).
  
  Approved by:	adrian (mentor)
  Sponsored by:	Smartcom - Bulgaria AD
  Differential Revision:	https://reviews.freebsd.org/D6348

Added:
  head/sys/dev/etherswitch/mtkswitch/
  head/sys/dev/etherswitch/mtkswitch/mtkswitch.c   (contents, props changed)
  head/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7620.c   (contents, props changed)
  head/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7620.h   (contents, props changed)
  head/sys/dev/etherswitch/mtkswitch/mtkswitch_rt3050.c   (contents, props changed)
  head/sys/dev/etherswitch/mtkswitch/mtkswitch_rt3050.h   (contents, props changed)
  head/sys/dev/etherswitch/mtkswitch/mtkswitchvar.h   (contents, props changed)

Added: head/sys/dev/etherswitch/mtkswitch/mtkswitch.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/etherswitch/mtkswitch/mtkswitch.c	Mon May 16 07:00:49 2016	(r299910)
@@ -0,0 +1,668 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * Copyright (c) 2011-2012 Stefan Bethke.
+ * Copyright (c) 2012 Adrian Chadd.
+ * 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/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/ofw/ofw_bus_subr.h>
+
+#include "mdio_if.h"
+#include "miibus_if.h"
+#include "etherswitch_if.h"
+
+#define DEBUG
+
+#if defined(DEBUG)
+static SYSCTL_NODE(_debug, OID_AUTO, mtkswitch, CTLFLAG_RD, 0, "mtkswitch");
+#endif
+
+static inline int mtkswitch_portforphy(int phy);
+static int mtkswitch_ifmedia_upd(struct ifnet *ifp);
+static void mtkswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr);
+static void mtkswitch_tick(void *arg);
+
+static const struct ofw_compat_data compat_data[] = {
+	{ "ralink,rt3050-esw",		MTK_SWITCH_RT3050 },
+	{ "ralink,rt3352-esw",		MTK_SWITCH_RT3352 },
+	{ "ralink,rt5350-esw",		MTK_SWITCH_RT5350 },
+	{ "mediatek,mt7620-gsw",	MTK_SWITCH_MT7620 },
+	{ "mediatek,mt7621-gsw",	MTK_SWITCH_MT7621 },
+	{ "mediatek,mt7628-esw",	MTK_SWITCH_MT7628 },
+
+	/* Sentinel */
+	{ NULL,				MTK_SWITCH_NONE }
+};
+
+static int
+mtkswitch_probe(device_t dev)
+{
+	struct mtkswitch_softc *sc;
+	mtk_switch_type switch_type;
+
+	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)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+	bzero(sc, sizeof(*sc));
+	sc->sc_switchtype = switch_type;
+
+	device_set_desc_copy(dev, "MTK Switch Driver");
+
+	return (0);
+}
+
+static int
+mtkswitch_attach_phys(struct mtkswitch_softc *sc)
+{
+	int phy, err = 0;
+	char name[IFNAMSIZ];
+
+	/* PHYs need an interface, so we generate a dummy one */
+	snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->sc_dev));
+	for (phy = 0; phy < sc->numphys; phy++) {
+		if ((sc->phymap & (1u << phy)) == 0) {
+			sc->ifp[phy] = NULL;
+			sc->ifname[phy] = NULL;
+			sc->miibus[phy] = NULL;
+			continue;
+		}
+		sc->ifp[phy] = if_alloc(IFT_ETHER);
+		sc->ifp[phy]->if_softc = sc;
+		sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST |
+		    IFF_DRV_RUNNING | IFF_SIMPLEX;
+		sc->ifname[phy] = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK);
+		bcopy(name, sc->ifname[phy], strlen(name) + 1);
+		if_initname(sc->ifp[phy], sc->ifname[phy],
+		    mtkswitch_portforphy(phy));
+		err = mii_attach(sc->sc_dev, &sc->miibus[phy], sc->ifp[phy],
+		    mtkswitch_ifmedia_upd, mtkswitch_ifmedia_sts,
+		    BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0);
+		if (err != 0) {
+			device_printf(sc->sc_dev,
+			    "attaching PHY %d failed\n",
+			    phy);
+		} else {
+			DPRINTF(sc->sc_dev, "%s attached to pseudo interface "
+			    "%s\n", device_get_nameunit(sc->miibus[phy]),
+			    sc->ifp[phy]->if_xname);
+		}
+	}
+	return (err);
+}
+
+static int
+mtkswitch_set_vlan_mode(struct mtkswitch_softc *sc, uint32_t mode)
+{
+
+	/* Check for invalid modes. */
+	if ((mode & sc->info.es_vlan_caps) != mode)
+		return (EINVAL);
+
+	sc->vlan_mode = mode;
+
+	/* Reset VLANs. */
+	sc->hal.mtkswitch_vlan_init_hw(sc);
+
+	return (0);
+}
+
+static int
+mtkswitch_attach(device_t dev)
+{
+	struct mtkswitch_softc *sc;
+	int err = 0;
+	int port, rid;
+
+	sc = device_get_softc(dev);
+
+	/* sc->sc_switchtype is already decided in mtkswitch_probe() */
+	sc->numports = MTKSWITCH_MAX_PORTS;
+	sc->numphys = MTKSWITCH_MAX_PHYS;
+	sc->cpuport = MTKSWITCH_CPU_PORT;
+	sc->sc_dev = dev;
+
+	/* Attach switch related functions */
+	if (sc->sc_switchtype == MTK_SWITCH_NONE) {
+		device_printf(dev, "Unknown switch type\n");
+		return (ENXIO);
+	}
+
+	if (sc->sc_switchtype == MTK_SWITCH_MT7620 ||
+	    sc->sc_switchtype == MTK_SWITCH_MT7621)
+		mtk_attach_switch_mt7620(sc);
+	else
+		mtk_attach_switch_rt3050(sc);
+
+	/* Allocate resources */
+	rid = 0;
+	sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->sc_res == NULL) {
+		device_printf(dev, "could not map memory\n");
+		return (ENXIO);
+	}
+
+	mtx_init(&sc->sc_mtx, "mtkswitch", NULL, MTX_DEF);
+
+	/* Reset the switch */
+	if (sc->hal.mtkswitch_reset(sc)) {
+		DPRINTF(dev, "%s: mtkswitch_reset: failed\n", __func__);
+		return (ENXIO);
+	}
+
+	err = sc->hal.mtkswitch_hw_setup(sc);
+	DPRINTF(dev, "%s: hw_setup: err=%d\n", __func__, err);
+	if (err != 0)
+		return (err);
+
+	err = sc->hal.mtkswitch_hw_global_setup(sc);
+	DPRINTF(dev, "%s: hw_global_setup: err=%d\n", __func__, err);
+	if (err != 0)
+		return (err);
+
+	/* Initialize the switch ports */
+	for (port = 0; port < sc->numports; port++) {
+		sc->hal.mtkswitch_port_init(sc, port);
+	}
+
+	/* Attach the PHYs and complete the bus enumeration */
+	err = mtkswitch_attach_phys(sc);
+	DPRINTF(dev, "%s: attach_phys: err=%d\n", __func__, err);
+	if (err != 0)
+		return (err);
+
+	/* Default to ingress filters off. */
+	err = mtkswitch_set_vlan_mode(sc, ETHERSWITCH_VLAN_DOT1Q);
+	DPRINTF(dev, "%s: set_vlan_mode: err=%d\n", __func__, err);
+	if (err != 0)
+		return (err);
+
+	bus_generic_probe(dev);
+	bus_enumerate_hinted_children(dev);
+	err = bus_generic_attach(dev);
+	DPRINTF(dev, "%s: bus_generic_attach: err=%d\n", __func__, err);
+	if (err != 0)
+		return (err);
+
+	callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0);
+
+	MTKSWITCH_LOCK(sc);
+	mtkswitch_tick(sc);
+	MTKSWITCH_UNLOCK(sc);
+
+	return (0);
+}
+
+static int
+mtkswitch_detach(device_t dev)
+{
+	struct mtkswitch_softc *sc = device_get_softc(dev);
+	int phy;
+
+	callout_drain(&sc->callout_tick);
+
+	for (phy = 0; phy < MTKSWITCH_MAX_PHYS; phy++) {
+		if (sc->miibus[phy] != NULL)
+			device_delete_child(dev, sc->miibus[phy]);
+		if (sc->ifp[phy] != NULL)
+			if_free(sc->ifp[phy]);
+		free(sc->ifname[phy], M_DEVBUF);
+	}
+
+	bus_generic_detach(dev);
+	mtx_destroy(&sc->sc_mtx);
+
+	return (0);
+}
+
+/* PHY <-> port mapping is currently 1:1 */
+static inline int
+mtkswitch_portforphy(int phy)
+{
+
+	return (phy);
+}
+
+static inline int
+mtkswitch_phyforport(int port)
+{
+
+	return (port);
+}
+
+static inline struct mii_data *
+mtkswitch_miiforport(struct mtkswitch_softc *sc, int port)
+{
+	int phy = mtkswitch_phyforport(port);
+
+	if (phy < 0 || phy >= MTKSWITCH_MAX_PHYS || sc->miibus[phy] == NULL)
+		return (NULL);
+
+	return (device_get_softc(sc->miibus[phy]));
+}
+
+static inline struct ifnet *
+mtkswitch_ifpforport(struct mtkswitch_softc *sc, int port)
+{
+	int phy = mtkswitch_phyforport(port);
+
+	if (phy < 0 || phy >= MTKSWITCH_MAX_PHYS)
+		return (NULL);
+
+	return (sc->ifp[phy]);
+}
+
+/*
+ * Convert port status to ifmedia.
+ */
+static void
+mtkswitch_update_ifmedia(uint32_t portstatus, u_int *media_status,
+    u_int *media_active)
+{
+	*media_active = IFM_ETHER;
+	*media_status = IFM_AVALID;
+
+	if ((portstatus & MTKSWITCH_LINK_UP) != 0)
+		*media_status |= IFM_ACTIVE;
+	else {
+		*media_active |= IFM_NONE;
+		return;
+	}
+
+	switch (portstatus & MTKSWITCH_SPEED_MASK) {
+	case MTKSWITCH_SPEED_10:
+		*media_active |= IFM_10_T;
+		break;
+	case MTKSWITCH_SPEED_100:
+		*media_active |= IFM_100_TX;
+		break;
+	case MTKSWITCH_SPEED_1000:
+		*media_active |= IFM_1000_T;
+		break;
+	}
+
+	if ((portstatus & MTKSWITCH_DUPLEX) != 0)
+		*media_active |= IFM_FDX;
+	else
+		*media_active |= IFM_HDX;
+
+	if ((portstatus & MTKSWITCH_TXFLOW) != 0)
+		*media_active |= IFM_ETH_TXPAUSE;
+	if ((portstatus & MTKSWITCH_RXFLOW) != 0)
+		*media_active |= IFM_ETH_RXPAUSE;
+}
+
+static void
+mtkswitch_miipollstat(struct mtkswitch_softc *sc)
+{
+	struct mii_data *mii;
+	struct mii_softc *miisc;
+	uint32_t portstatus;
+	int i, port_flap = 0;
+
+	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+
+	for (i = 0; i < sc->numphys; i++) {
+		if (sc->miibus[i] == NULL)
+			continue;
+		mii = device_get_softc(sc->miibus[i]);
+		portstatus = sc->hal.mtkswitch_get_port_status(sc,
+		    mtkswitch_portforphy(i));
+
+		/* If a port has flapped - mark it so we can flush the ATU */
+		if (((mii->mii_media_status & IFM_ACTIVE) == 0 &&
+		    (portstatus & MTKSWITCH_LINK_UP) != 0) ||
+		    ((mii->mii_media_status & IFM_ACTIVE) != 0 &&
+		    (portstatus & MTKSWITCH_LINK_UP) == 0)) {
+			port_flap = 1;
+		}
+
+		mtkswitch_update_ifmedia(portstatus, &mii->mii_media_status,
+		    &mii->mii_media_active);
+		LIST_FOREACH(miisc, &mii->mii_phys, mii_list) {
+			if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) !=
+			    miisc->mii_inst)
+				continue;
+			mii_phy_update(miisc, MII_POLLSTAT);
+		}
+	}
+
+	if (port_flap)
+		sc->hal.mtkswitch_atu_flush(sc);
+}
+
+static void
+mtkswitch_tick(void *arg)
+{
+	struct mtkswitch_softc *sc = arg;
+
+	mtkswitch_miipollstat(sc);
+	callout_reset(&sc->callout_tick, hz, mtkswitch_tick, sc);
+}
+
+static void
+mtkswitch_lock(device_t dev)
+{
+        struct mtkswitch_softc *sc = device_get_softc(dev);
+
+	MTKSWITCH_LOCK_ASSERT(sc, MA_NOTOWNED);
+	MTKSWITCH_LOCK(sc);
+}
+
+static void
+mtkswitch_unlock(device_t dev)
+{
+	struct mtkswitch_softc *sc = device_get_softc(dev);
+
+	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+	MTKSWITCH_UNLOCK(sc);
+}
+
+static etherswitch_info_t *
+mtkswitch_getinfo(device_t dev)
+{
+	struct mtkswitch_softc *sc = device_get_softc(dev);
+
+	return (&sc->info);
+}
+
+static inline int
+mtkswitch_is_cpuport(struct mtkswitch_softc *sc, int port)
+{
+
+	return (sc->cpuport == port);
+}
+
+static int
+mtkswitch_getport(device_t dev, etherswitch_port_t *p)
+{       
+	struct mtkswitch_softc *sc;
+	struct mii_data *mii;
+	struct ifmediareq *ifmr;
+	int err;
+
+	sc = device_get_softc(dev);
+	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
+		return (ENXIO);
+
+	err = sc->hal.mtkswitch_port_vlan_get(sc, p);
+	if (err != 0)
+		return (err);
+
+	mii = mtkswitch_miiforport(sc, p->es_port);
+	if (mtkswitch_is_cpuport(sc, p->es_port)) {
+		/* fill in fixed values for CPU port */
+		/* XXX is this valid in all cases? */
+		p->es_flags |= ETHERSWITCH_PORT_CPU;
+		ifmr = &p->es_ifmr;
+		ifmr->ifm_count = 0;
+		ifmr->ifm_current = ifmr->ifm_active =
+		    IFM_ETHER | IFM_1000_T | IFM_FDX;
+		ifmr->ifm_mask = 0;
+		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+	} else if (mii != NULL) {
+		err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
+		    &mii->mii_media, SIOCGIFMEDIA);
+		if (err)
+			return (err);
+	} else {
+		ifmr = &p->es_ifmr;
+		ifmr->ifm_count = 0;
+		ifmr->ifm_current = ifmr->ifm_active = IFM_NONE;
+		ifmr->ifm_mask = 0;
+		ifmr->ifm_status = 0;
+	}
+	return (0);
+}
+
+static int
+mtkswitch_setport(device_t dev, etherswitch_port_t *p)
+{       
+	int err;
+	struct mtkswitch_softc *sc;
+	struct ifmedia *ifm;
+	struct mii_data *mii;
+	struct ifnet *ifp;
+
+	sc = device_get_softc(dev);
+	if (p->es_port < 0 || p->es_port > sc->info.es_nports)
+		return (ENXIO);
+        
+	/* Port flags. */ 
+	if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) {
+		err = sc->hal.mtkswitch_port_vlan_setup(sc, p);
+		if (err)
+			return (err);
+	}
+
+	/* Do not allow media changes on CPU port. */
+	if (mtkswitch_is_cpuport(sc, p->es_port))
+		return (0);
+
+	mii = mtkswitch_miiforport(sc, p->es_port);
+	if (mii == NULL)
+		return (ENXIO);
+
+	ifp = mtkswitch_ifpforport(sc, p->es_port);
+
+	ifm = &mii->mii_media;
+	return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
+}
+
+static void
+mtkswitch_statchg(device_t dev)
+{
+
+	DPRINTF(dev, "%s\n", __func__);
+}
+
+static int
+mtkswitch_ifmedia_upd(struct ifnet *ifp)
+{
+	struct mtkswitch_softc *sc = ifp->if_softc;
+	struct mii_data *mii = mtkswitch_miiforport(sc, ifp->if_dunit);
+        
+	if (mii == NULL)
+		return (ENXIO);
+	mii_mediachg(mii);
+	return (0);
+}
+
+static void
+mtkswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+	struct mtkswitch_softc *sc = ifp->if_softc;
+	struct mii_data *mii = mtkswitch_miiforport(sc, ifp->if_dunit);
+
+	DPRINTF(sc->sc_dev, "%s\n", __func__);
+
+	if (mii == NULL)
+		return;
+	mii_pollstat(mii); 
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
+}
+
+static int
+mtkswitch_getconf(device_t dev, etherswitch_conf_t *conf)
+{
+	struct mtkswitch_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Return the VLAN mode. */
+	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
+	conf->vlan_mode = sc->vlan_mode;
+
+	return (0);
+}
+
+static int
+mtkswitch_setconf(device_t dev, etherswitch_conf_t *conf)
+{
+	struct mtkswitch_softc *sc;
+	int err;
+        
+	sc = device_get_softc(dev);
+        
+	/* Set the VLAN mode. */
+	if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
+		err = mtkswitch_set_vlan_mode(sc, conf->vlan_mode);
+		if (err != 0)
+			return (err);
+	}
+
+	return (0);
+}
+
+static int
+mtkswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *e)
+{
+        struct mtkswitch_softc *sc = device_get_softc(dev);
+
+        return (sc->hal.mtkswitch_vlan_getvgroup(sc, e));
+}
+
+static int
+mtkswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *e)
+{
+	struct mtkswitch_softc *sc = device_get_softc(dev);
+
+	return (sc->hal.mtkswitch_vlan_setvgroup(sc, e));
+}
+
+static int
+mtkswitch_readphy(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)
+{
+	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)
+{
+	struct mtkswitch_softc *sc = device_get_softc(dev);
+
+	return (sc->hal.mtkswitch_reg_read(dev, addr));
+}
+
+static int
+mtkswitch_writereg(device_t dev, int addr, int value)
+{
+	struct mtkswitch_softc *sc = device_get_softc(dev);
+
+	return (sc->hal.mtkswitch_reg_write(dev, addr, value));
+}
+
+static device_method_t mtkswitch_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		mtkswitch_probe),
+	DEVMETHOD(device_attach,	mtkswitch_attach),
+	DEVMETHOD(device_detach,	mtkswitch_detach),
+
+	/* bus interface */
+	DEVMETHOD(bus_add_child,	device_add_child_ordered),
+
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	mtkswitch_readphy),
+	DEVMETHOD(miibus_writereg,	mtkswitch_writephy),
+	DEVMETHOD(miibus_statchg,	mtkswitch_statchg),
+
+	/* MDIO interface */
+	DEVMETHOD(mdio_readreg,		mtkswitch_readphy),
+	DEVMETHOD(mdio_writereg,	mtkswitch_writephy),
+
+	/* ehterswitch interface */
+	DEVMETHOD(etherswitch_lock,	mtkswitch_lock),
+	DEVMETHOD(etherswitch_unlock,	mtkswitch_unlock),
+	DEVMETHOD(etherswitch_getinfo,	mtkswitch_getinfo),
+	DEVMETHOD(etherswitch_readreg,	mtkswitch_readreg),
+	DEVMETHOD(etherswitch_writereg,	mtkswitch_writereg),
+	DEVMETHOD(etherswitch_readphyreg,	mtkswitch_readphy),
+	DEVMETHOD(etherswitch_writephyreg,	mtkswitch_writephy),
+	DEVMETHOD(etherswitch_getport,	mtkswitch_getport),
+	DEVMETHOD(etherswitch_setport,	mtkswitch_setport),
+	DEVMETHOD(etherswitch_getvgroup,	mtkswitch_getvgroup),
+	DEVMETHOD(etherswitch_setvgroup,	mtkswitch_setvgroup),
+	DEVMETHOD(etherswitch_getconf,	mtkswitch_getconf),
+	DEVMETHOD(etherswitch_setconf,	mtkswitch_setconf),
+
+	DEVMETHOD_END
+};
+
+DEFINE_CLASS_0(mtkswitch, mtkswitch_driver, mtkswitch_methods,
+    sizeof(struct mtkswitch_softc));
+static devclass_t mtkswitch_devclass;
+
+DRIVER_MODULE(mtkswitch, simplebus, mtkswitch_driver, mtkswitch_devclass, 0, 0);
+DRIVER_MODULE(miibus, mtkswitch, miibus_driver, miibus_devclass, 0, 0);
+DRIVER_MODULE(mdio, mtkswitch, mdio_driver, mdio_devclass, 0, 0);
+DRIVER_MODULE(etherswitch, mtkswitch, etherswitch_driver, etherswitch_devclass,
+    0, 0);
+MODULE_VERSION(mtkswitch, 1);
+MODULE_DEPEND(mtkswitch, miibus, 1, 1, 1);
+MODULE_DEPEND(mtkswitch, etherswitch, 1, 1, 1);

Added: head/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7620.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/etherswitch/mtkswitch/mtkswitch_mt7620.c	Mon May 16 07:00:49 2016	(r299910)
@@ -0,0 +1,563 @@
+/*-
+ * Copyright (c) 2016 Stanislav Galabov.
+ * 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_mt7620.h>
+
+static int
+mtkswitch_phy_read_locked(struct mtkswitch_softc *sc, int phy, int reg)
+{
+	uint32_t data;
+        
+	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 = 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)
+{
+
+	MTKSWITCH_WRITE(sc, MTKSWITCH_PIAC, PIAC_PHY_ACS_ST | PIAC_MDIO_ST |
+	    (reg << PIAC_MDIO_REG_ADDR_OFF) | (phy << PIAC_MDIO_PHY_ADDR_OFF) |
+	    (val & PIAC_MDIO_RW_DATA_MASK) | PIAC_MDIO_CMD_WRITE);
+	while (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)
+{
+
+	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+	return (MTKSWITCH_READ(sc, reg));
+}
+
+static uint32_t
+mtkswitch_reg_write32(struct mtkswitch_softc *sc, int reg, uint32_t val)
+{
+
+	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+	MTKSWITCH_WRITE(sc, reg, val);
+	return (0);
+}
+
+static uint32_t
+mtkswitch_reg_read32_mt7621(struct mtkswitch_softc *sc, int reg)
+{
+	uint32_t low, hi;
+
+	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+	mtkswitch_phy_write_locked(sc, MTKSWITCH_GLOBAL_PHY,
+	    MTKSWITCH_GLOBAL_REG, MTKSWITCH_REG_ADDR(reg));
+	low = mtkswitch_phy_read_locked(sc, MTKSWITCH_GLOBAL_PHY,
+	    MTKSWITCH_REG_LO(reg));
+	hi = mtkswitch_phy_read_locked(sc, MTKSWITCH_GLOBAL_PHY,
+	    MTKSWITCH_REG_HI(reg));;
+	return (low | (hi << 16));
+}
+
+static uint32_t
+mtkswitch_reg_write32_mt7621(struct mtkswitch_softc *sc, int reg, uint32_t val)
+{
+
+	MTKSWITCH_LOCK_ASSERT(sc, MA_OWNED);
+	mtkswitch_phy_write_locked(sc, MTKSWITCH_GLOBAL_PHY,
+	    MTKSWITCH_GLOBAL_REG, MTKSWITCH_REG_ADDR(reg));
+	mtkswitch_phy_write_locked(sc, MTKSWITCH_GLOBAL_PHY,
+	    MTKSWITCH_REG_LO(reg), MTKSWITCH_VAL_LO(val));
+	mtkswitch_phy_write_locked(sc, 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));
+	if (MTKSWITCH_IS_HI16(reg))
+		return (MTKSWITCH_HI16(val));
+	return (MTKSWITCH_LO16(val));
+}
+
+static int
+mtkswitch_reg_write(device_t dev, int reg, int val)
+{
+	struct mtkswitch_softc *sc = device_get_softc(dev);
+	uint32_t tmp;
+
+	tmp = sc->hal.mtkswitch_read(sc, MTKSWITCH_REG32(reg));
+	if (MTKSWITCH_IS_HI16(reg)) {
+		tmp &= MTKSWITCH_LO16_MSK;
+		tmp |= MTKSWITCH_TO_HI16(val);
+	} else {
+		tmp &= MTKSWITCH_HI16_MSK;
+		tmp |= MTKSWITCH_TO_LO16(val);
+	}
+	sc->hal.mtkswitch_write(sc, MTKSWITCH_REG32(reg), tmp);
+
+	return (0);
+}
+
+static int
+mtkswitch_reset(struct mtkswitch_softc *sc)
+{
+
+	/* We don't reset the switch for now */
+	return (0);
+}
+
+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.
+	 */
+
+	/* 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 */
+	sc->hal.mtkswitch_write(sc, MTKSWITCH_PCR(port), PCR_PORT_VLAN_SECURE);
+
+	/* 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);
+
+	/* Set port's MAC to default settings */
+	sc->hal.mtkswitch_write(sc, MTKSWITCH_PMCR(port), PMCR_CFG_DEFAULT);
+}
+
+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);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list