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)
This commit is contained in:
parent
c5d1ccc2ed
commit
a2c46b941e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=309113
@ -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_softc *);
|
||||
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 (ðerswitch_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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user