Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 24 Nov 2016 20:14:43 +0000 (UTC)
From:      Luiz Otavio O Souza <loos@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r309113 - in head/sys: arm/ti/cpsw conf
Message-ID:  <201611242014.uAOKEhT9015336@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: loos
Date: Thu Nov 24 20:14:43 2016
New Revision: 309113
URL: https://svnweb.freebsd.org/changeset/base/309113

Log:
  Add the etherswitch(4) support for TI CPSW.
  
  Adds VLAN and port management abilities for etherswitchcfg(8).
  
  The code is conditionally enabled for now, because it is not necessary on
  single ethernet use cases.
  
  Obtained from:	pfSense
  MFC after:	2 weeks
  Sponsored by:	Rubicon Communications, LLC (Netgate)

Modified:
  head/sys/arm/ti/cpsw/if_cpsw.c
  head/sys/arm/ti/cpsw/if_cpswreg.h
  head/sys/arm/ti/cpsw/if_cpswvar.h
  head/sys/conf/options.arm

Modified: head/sys/arm/ti/cpsw/if_cpsw.c
==============================================================================
--- head/sys/arm/ti/cpsw/if_cpsw.c	Thu Nov 24 20:08:17 2016	(r309112)
+++ head/sys/arm/ti/cpsw/if_cpsw.c	Thu Nov 24 20:14:43 2016	(r309113)
@@ -47,6 +47,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_cpsw.h"
+
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/kernel.h>
@@ -78,6 +80,11 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
+ 
+#ifdef CPSW_ETHERSWITCH
+#include <dev/etherswitch/etherswitch.h>
+#include "etherswitch_if.h"
+#endif
 
 #include "if_cpswreg.h"
 #include "if_cpswvar.h"
@@ -142,6 +149,19 @@ static void cpsw_add_sysctls(struct cpsw
 static void cpsw_stats_collect(struct cpsw_softc *);
 static int cpsw_stats_sysctl(SYSCTL_HANDLER_ARGS);
 
+#ifdef CPSW_ETHERSWITCH
+static etherswitch_info_t *cpsw_getinfo(device_t);
+static int cpsw_getport(device_t, etherswitch_port_t *);
+static int cpsw_setport(device_t, etherswitch_port_t *);
+static int cpsw_getconf(device_t, etherswitch_conf_t *);
+static int cpsw_getvgroup(device_t, etherswitch_vlangroup_t *);
+static int cpsw_setvgroup(device_t, etherswitch_vlangroup_t *);
+static int cpsw_readreg(device_t, int);
+static int cpsw_writereg(device_t, int, int);
+static int cpsw_readphy(device_t, int, int);
+static int cpsw_writephy(device_t, int, int, int);
+#endif
+
 /*
  * Arbitrary limit on number of segments in an mbuf to be transmitted.
  * Packets with more segments than this will be defragmented before
@@ -158,8 +178,23 @@ static device_method_t cpsw_methods[] = 
 	DEVMETHOD(device_shutdown,	cpsw_shutdown),
 	DEVMETHOD(device_suspend,	cpsw_suspend),
 	DEVMETHOD(device_resume,	cpsw_resume),
+	/* Bus interface */
+	DEVMETHOD(bus_add_child,	device_add_child_ordered),
 	/* OFW methods */
 	DEVMETHOD(ofw_bus_get_node,	cpsw_get_node),
+#ifdef CPSW_ETHERSWITCH
+	/* etherswitch interface */
+	DEVMETHOD(etherswitch_getinfo,	cpsw_getinfo),
+	DEVMETHOD(etherswitch_readreg,	cpsw_readreg),
+	DEVMETHOD(etherswitch_writereg,	cpsw_writereg),
+	DEVMETHOD(etherswitch_readphyreg,	cpsw_readphy),
+	DEVMETHOD(etherswitch_writephyreg,	cpsw_writephy),
+	DEVMETHOD(etherswitch_getport,	cpsw_getport),
+	DEVMETHOD(etherswitch_setport,	cpsw_setport),
+	DEVMETHOD(etherswitch_getvgroup,	cpsw_getvgroup),
+	DEVMETHOD(etherswitch_setvgroup,	cpsw_setvgroup),
+	DEVMETHOD(etherswitch_getconf,	cpsw_getconf),
+#endif
 	DEVMETHOD_END
 };
 
@@ -194,11 +229,20 @@ static driver_t cpswp_driver = {
 
 static devclass_t cpswp_devclass;
 
+#ifdef CPSW_ETHERSWITCH
+DRIVER_MODULE(etherswitch, cpswss, etherswitch_driver, etherswitch_devclass, 0, 0);
+MODULE_DEPEND(cpswss, etherswitch, 1, 1, 1);
+#endif
+
 DRIVER_MODULE(cpsw, cpswss, cpswp_driver, cpswp_devclass, 0, 0);
 DRIVER_MODULE(miibus, cpsw, miibus_driver, miibus_devclass, 0, 0);
 MODULE_DEPEND(cpsw, ether, 1, 1, 1);
 MODULE_DEPEND(cpsw, miibus, 1, 1, 1);
 
+#ifdef CPSW_ETHERSWITCH
+static struct cpsw_vlangroups cpsw_vgroups[CPSW_VLANS];
+#endif
+
 static uint32_t slave_mdio_addr[] = { 0x4a100200, 0x4a100300 };
 
 static struct resource_spec irq_res_spec[] = {
@@ -576,7 +620,8 @@ cpsw_init(struct cpsw_softc *sc)
 	cpsw_write_4(sc, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0);
 
 	/* Initialize ALE: set host port to forwarding(3). */
-	cpsw_write_4(sc, CPSW_ALE_PORTCTL(0), 3);
+	cpsw_write_4(sc, CPSW_ALE_PORTCTL(0),
+	    ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
 
 	cpsw_write_4(sc, CPSW_SS_PTYPE, 0);
 
@@ -851,6 +896,11 @@ cpsw_attach(device_t dev)
 		return (ENXIO);
 	}
 
+#ifdef CPSW_ETHERSWITCH
+	for (i = 0; i < CPSW_VLANS; i++)
+		cpsw_vgroups[i].vid = -1;
+#endif
+
 	/* Reset the controller. */
 	cpsw_reset(sc);
 	cpsw_init(sc);
@@ -864,6 +914,7 @@ cpsw_attach(device_t dev)
 			return (ENXIO);
 		}
 	}
+	bus_generic_probe(dev);
 	bus_generic_attach(dev);
 
 	return (0);
@@ -918,7 +969,12 @@ cpsw_detach(device_t dev)
 	mtx_destroy(&sc->rx.lock);
 	mtx_destroy(&sc->tx.lock);
 
-	return (0);
+	/* Detach the switch device, if present. */
+	error = bus_generic_detach(dev);
+	if (error != 0)
+		return (error);
+        
+	return (device_delete_children(dev));
 }
 
 static phandle_t
@@ -1085,6 +1141,9 @@ cpswp_init(void *arg)
 static void
 cpswp_init_locked(void *arg)
 {
+#ifdef CPSW_ETHERSWITCH
+	int i;
+#endif
 	struct cpswp_softc *sc = arg;
 	struct ifnet *ifp;
 	uint32_t reg;
@@ -1115,8 +1174,9 @@ cpswp_init_locked(void *arg)
 	reg |= CPSW_SL_MACTL_GMII_ENABLE;
 	cpsw_write_4(sc->swsc, CPSW_SL_MACCONTROL(sc->unit), reg);
 
-	/* Initialize ALE: set port to forwarding(3), initialize addrs */
-	cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1), 3);
+	/* Initialize ALE: set port to forwarding, initialize addrs */
+	cpsw_write_4(sc->swsc, CPSW_ALE_PORTCTL(sc->unit + 1),
+	    ALE_PORTCTL_INGRESS | ALE_PORTCTL_FORWARD);
 	cpswp_ale_update_addresses(sc, 1);
 
 	if (sc->swsc->dualemac) {
@@ -1127,6 +1187,14 @@ cpswp_init_locked(void *arg)
 		    (1 << (sc->unit + 1)) | (1 << 0), /* Member list */
 		    (1 << (sc->unit + 1)) | (1 << 0), /* Untagged egress */
 		    (1 << (sc->unit + 1)) | (1 << 0), 0); /* mcast reg flood */
+#ifdef CPSW_ETHERSWITCH
+		for (i = 0; i < CPSW_VLANS; i++) {
+			if (cpsw_vgroups[i].vid != -1)
+				continue;
+			cpsw_vgroups[i].vid = sc->vlan;
+			break;
+		}
+#endif
 	}
 
 	mii_mediachg(sc->mii);
@@ -2699,3 +2767,229 @@ cpsw_add_sysctls(struct cpsw_softc *sc)
 	    CTLFLAG_RD, NULL, "Watchdog Statistics");
 	cpsw_add_watchdog_sysctls(ctx, node, sc);
 }
+
+#ifdef CPSW_ETHERSWITCH
+static etherswitch_info_t etherswitch_info = {
+	.es_nports =		CPSW_PORTS + 1,
+	.es_nvlangroups =	CPSW_VLANS,
+	.es_name =		"TI Common Platform Ethernet Switch (CPSW)",
+	.es_vlan_caps =		ETHERSWITCH_VLAN_DOT1Q,
+};
+
+static etherswitch_info_t *
+cpsw_getinfo(device_t dev)
+{
+	return (&etherswitch_info);
+}
+
+static int
+cpsw_getport(device_t dev, etherswitch_port_t *p)
+{
+	int err;
+	struct cpsw_softc *sc;
+	struct cpswp_softc *psc;
+	struct ifmediareq *ifmr;
+	uint32_t reg;
+
+	if (p->es_port < 0 || p->es_port > CPSW_PORTS)
+		return (ENXIO);
+
+	err = 0;
+	sc = device_get_softc(dev);
+	if (p->es_port == CPSW_CPU_PORT) {
+		p->es_flags |= ETHERSWITCH_PORT_CPU;
+ 		ifmr = &p->es_ifmr;
+		ifmr->ifm_current = ifmr->ifm_active =
+		    IFM_ETHER | IFM_1000_T | IFM_FDX;
+		ifmr->ifm_mask = 0;
+		ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
+		ifmr->ifm_count = 0;
+	} else {
+		psc = device_get_softc(sc->port[p->es_port - 1].dev);
+		err = ifmedia_ioctl(psc->ifp, &p->es_ifr,
+		    &psc->mii->mii_media, SIOCGIFMEDIA);
+	}
+	reg = cpsw_read_4(sc, CPSW_PORT_P_VLAN(p->es_port));
+	p->es_pvid = reg & ETHERSWITCH_VID_MASK;
+
+	reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
+	if (reg & ALE_PORTCTL_DROP_UNTAGGED)
+		p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED;
+	if (reg & ALE_PORTCTL_INGRESS)
+		p->es_flags |= ETHERSWITCH_PORT_INGRESS;
+
+	return (err);
+}
+
+static int
+cpsw_setport(device_t dev, etherswitch_port_t *p)
+{
+	struct cpsw_softc *sc;
+	struct cpswp_softc *psc;
+	struct ifmedia *ifm;
+	uint32_t reg;
+
+	if (p->es_port < 0 || p->es_port > CPSW_PORTS)
+		return (ENXIO);
+
+	sc = device_get_softc(dev);
+	if (p->es_pvid != 0) {
+		cpsw_write_4(sc, CPSW_PORT_P_VLAN(p->es_port),
+		    p->es_pvid & ETHERSWITCH_VID_MASK);
+	}
+
+	reg = cpsw_read_4(sc, CPSW_ALE_PORTCTL(p->es_port));
+	if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED)
+		reg |= ALE_PORTCTL_DROP_UNTAGGED;
+	else
+		reg &= ~ALE_PORTCTL_DROP_UNTAGGED;
+	if (p->es_flags & ETHERSWITCH_PORT_INGRESS)
+		reg |= ALE_PORTCTL_INGRESS;
+	else
+		reg &= ~ALE_PORTCTL_INGRESS;
+	cpsw_write_4(sc, CPSW_ALE_PORTCTL(p->es_port), reg);
+
+	/* CPU port does not allow media settings. */
+	if (p->es_port == CPSW_CPU_PORT)
+		return (0);
+
+	psc = device_get_softc(sc->port[p->es_port - 1].dev);
+	ifm = &psc->mii->mii_media;
+
+	return (ifmedia_ioctl(psc->ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
+}
+
+static int
+cpsw_getconf(device_t dev, etherswitch_conf_t *conf)
+{
+
+	/* Return the VLAN mode. */
+	conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
+	conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
+
+	return (0);
+}
+
+static int
+cpsw_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+	int i, vid;
+	uint32_t ale_entry[3];
+	struct cpsw_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	if (vg->es_vlangroup >= CPSW_VLANS)
+		return (EINVAL);
+
+	vg->es_vid = 0;
+	vid = cpsw_vgroups[vg->es_vlangroup].vid;
+	if (vid == -1)
+		return (0);
+
+	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+		cpsw_ale_read_entry(sc, i, ale_entry);
+		if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
+			continue;
+		if (vid != ALE_VLAN(ale_entry))
+			continue;
+
+		vg->es_fid = 0;
+		vg->es_vid = ALE_VLAN(ale_entry) | ETHERSWITCH_VID_VALID;
+		vg->es_member_ports = ALE_VLAN_MEMBERS(ale_entry);
+		vg->es_untagged_ports = ALE_VLAN_UNTAG(ale_entry);
+	}
+
+	return (0);
+}
+
+static void
+cpsw_remove_vlan(struct cpsw_softc *sc, int vlan)
+{
+	int i;
+	uint32_t ale_entry[3];
+
+	for (i = 0; i < CPSW_MAX_ALE_ENTRIES; i++) {
+		cpsw_ale_read_entry(sc, i, ale_entry);
+		if (ALE_TYPE(ale_entry) != ALE_TYPE_VLAN)
+			continue;
+		if (vlan != ALE_VLAN(ale_entry))
+			continue;
+		ale_entry[0] = ale_entry[1] = ale_entry[2] = 0;
+		cpsw_ale_write_entry(sc, i, ale_entry);
+		break;
+	}
+}
+
+static int
+cpsw_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
+{
+	int i;
+	struct cpsw_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	for (i = 0; i < CPSW_VLANS; i++) {
+		/* Is this Vlan ID in use by another vlangroup ? */
+		if (vg->es_vlangroup != i && cpsw_vgroups[i].vid == vg->es_vid)
+			return (EINVAL);
+	}
+
+	if (vg->es_vid == 0) {
+		if (cpsw_vgroups[vg->es_vlangroup].vid == -1)
+			return (0);
+		cpsw_remove_vlan(sc, cpsw_vgroups[vg->es_vlangroup].vid);
+		cpsw_vgroups[vg->es_vlangroup].vid = -1;
+		vg->es_untagged_ports = 0;
+		vg->es_member_ports = 0;
+		vg->es_vid = 0;
+		return (0);
+	}
+
+	vg->es_vid &= ETHERSWITCH_VID_MASK;
+	vg->es_member_ports &= CPSW_PORTS_MASK;
+	vg->es_untagged_ports &= CPSW_PORTS_MASK;
+
+	if (cpsw_vgroups[vg->es_vlangroup].vid != -1 &&
+	    cpsw_vgroups[vg->es_vlangroup].vid != vg->es_vid)
+		return (EINVAL);
+
+	cpsw_vgroups[vg->es_vlangroup].vid = vg->es_vid;
+	cpsw_ale_update_vlan_table(sc, vg->es_vid, vg->es_member_ports,
+	    vg->es_untagged_ports, vg->es_member_ports, 0);
+
+	return (0);
+}
+
+static int
+cpsw_readreg(device_t dev, int addr)
+{
+
+	/* Not supported. */
+	return (0);
+}
+
+static int
+cpsw_writereg(device_t dev, int addr, int value)
+{
+
+	/* Not supported. */
+	return (0);
+}
+
+static int
+cpsw_readphy(device_t dev, int phy, int reg)
+{
+
+	/* Not supported. */
+	return (0);
+}
+
+static int
+cpsw_writephy(device_t dev, int phy, int reg, int data)
+{
+
+	/* Not supported. */
+	return (0);
+}
+#endif

Modified: head/sys/arm/ti/cpsw/if_cpswreg.h
==============================================================================
--- head/sys/arm/ti/cpsw/if_cpswreg.h	Thu Nov 24 20:08:17 2016	(r309112)
+++ head/sys/arm/ti/cpsw/if_cpswreg.h	Thu Nov 24 20:14:43 2016	(r309113)
@@ -106,6 +106,14 @@
 #define	 ALE_VLAN_UNTAG(_a)		((_a[0] >> 24) & 7)
 #define	 ALE_VLAN_MEMBERS(_a)		(_a[0] & 7)
 #define	CPSW_ALE_PORTCTL(p)		(CPSW_ALE_OFFSET + 0x40 + ((p) * 0x04))
+#define	 ALE_PORTCTL_NO_SA_UPDATE	(1 << 5)
+#define	 ALE_PORTCTL_NO_LEARN		(1 << 4)
+#define	 ALE_PORTCTL_INGRESS		(1 << 3)
+#define	 ALE_PORTCTL_DROP_UNTAGGED	(1 << 2)
+#define	 ALE_PORTCTL_FORWARD		3
+#define	 ALE_PORTCTL_LEARN		2
+#define	 ALE_PORTCTL_BLOCKED		1
+#define	 ALE_PORTCTL_DISABLED		0
 
 /* SL1 is at 0x0D80, SL2 is at 0x0DC0 */
 #define	CPSW_SL_OFFSET			0x0D80

Modified: head/sys/arm/ti/cpsw/if_cpswvar.h
==============================================================================
--- head/sys/arm/ti/cpsw/if_cpswvar.h	Thu Nov 24 20:08:17 2016	(r309112)
+++ head/sys/arm/ti/cpsw/if_cpswvar.h	Thu Nov 24 20:14:43 2016	(r309113)
@@ -40,6 +40,16 @@
 
 #define	CPSW_SYSCTL_COUNT	34
 
+#ifdef CPSW_ETHERSWITCH
+#define	CPSW_CPU_PORT		0
+#define	CPSW_PORTS_MASK		0x7
+#define	CPSW_VLANS		128	/* Arbitrary number. */
+
+struct cpsw_vlangroups {
+	int vid;
+};
+#endif
+
 struct cpsw_slot {
 	uint32_t bd_offset;  /* Offset of corresponding BD within CPPI RAM. */
 	bus_dmamap_t dmamap;

Modified: head/sys/conf/options.arm
==============================================================================
--- head/sys/conf/options.arm	Thu Nov 24 20:08:17 2016	(r309112)
+++ head/sys/conf/options.arm	Thu Nov 24 20:14:43 2016	(r309113)
@@ -7,6 +7,7 @@ ARM_MANY_BOARD		opt_global.h
 NKPT2PG			opt_pmap.h
 ARM_WANT_TP_ADDRESS	opt_global.h
 COUNTS_PER_SEC		opt_timer.h
+CPSW_ETHERSWITCH	opt_cpsw.h
 CPU_ARM9		opt_global.h
 CPU_ARM9E		opt_global.h
 CPU_ARM1176		opt_global.h



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201611242014.uAOKEhT9015336>