Index: sys/dev/etherswitch/e6000sw/e6000sw.c =================================================================== --- sys/dev/etherswitch/e6000sw/e6000sw.c +++ sys/dev/etherswitch/e6000sw/e6000sw.c @@ -59,6 +59,10 @@ #include #include +#include +#include +#include + #include "e6000swreg.h" #include "etherswitch_if.h" #include "miibus_if.h" @@ -78,23 +82,28 @@ typedef struct e6000sw_softc { device_t dev; + phandle_t node; struct sx sx; - struct ifnet *ifp[E6000SW_NUM_PHYS]; - char *ifname[E6000SW_NUM_PHYS]; - device_t miibus[E6000SW_NUM_PHYS]; - struct mii_data *mii[E6000SW_NUM_PHYS]; + struct ifnet *ifp[E6000SW_MAX_PORTS]; + char *ifname[E6000SW_MAX_PORTS]; + device_t miibus[E6000SW_MAX_PORTS]; + struct mii_data *mii[E6000SW_MAX_PORTS]; struct callout tick_callout; uint32_t cpuports_mask; + uint32_t fixed_mask; + int sw_addr; + int num_ports; + boolean_t multi_chip; int vid[E6000SW_NUM_VGROUPS]; int members[E6000SW_NUM_VGROUPS]; - int vgroup[E6000SW_NUM_PORTS]; + int vgroup[E6000SW_MAX_PORTS]; } e6000sw_softc_t; static etherswitch_info_t etherswitch_info = { - .es_nports = E6000SW_NUM_PORTS, + .es_nports = 0, .es_nvlangroups = E6000SW_NUM_VGROUPS, .es_name = "Marvell 6000 series switch" }; @@ -134,7 +143,9 @@ atu_opt *atu, int flag); static int e6000sw_get_pvid(e6000sw_softc_t *sc, int port, int *pvid); static int e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid); -static __inline int e6000sw_cpuport(e6000sw_softc_t *sc, int port); +static __inline int e6000sw_is_cpuport(e6000sw_softc_t *sc, int port); +static __inline int e6000sw_is_fixedport(e6000sw_softc_t *sc, int port); +static __inline int e6000sw_is_phyport(e6000sw_softc_t *sc, int port); static __inline struct mii_data *e6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy); @@ -181,6 +192,14 @@ DRIVER_MODULE(miibus, e6000sw, miibus_driver, miibus_devclass, 0, 0); MODULE_DEPEND(e6000sw, mdio, 1, 1, 1); +#define SMI_CMD 0 +#define SMI_CMD_BUSY (1<<15) +#define SMI_CMD_OP_READ ((2<<10)|SMI_CMD_BUSY|(1<<12)) +#define SMI_CMD_OP_WRITE ((1<<10)|SMI_CMD_BUSY|(1<<12)) +#define SMI_DATA 1 + +#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 void e6000sw_identify(driver_t *driver, device_t parent) { @@ -195,10 +214,37 @@ e6000sw_softc_t *sc; const char *description; unsigned int id; + uint16_t dev_addr; + phandle_t dsa_node, switch_node; + + dsa_node = fdt_find_compatible(OF_finddevice("/"), + "marvell,dsa", 0); + switch_node = OF_child(dsa_node); + + if (switch_node == 0) + return (ENXIO); sc = device_get_softc(dev); bzero(sc, sizeof(e6000sw_softc_t)); sc->dev = dev; + sc->node = switch_node; + + /* Read ADDR[4:1]n using indirect access */ + MDIO_WRITE(dev, REG_GLOBAL2, SCR_AND_MISC_REG, + SCR_AND_MISC_PTR_CFG); + dev_addr = MDIO_READ(dev, REG_GLOBAL2, SCR_AND_MISC_REG) & + SCR_AND_MISC_DATA_CFG_MASK; + if (dev_addr != 0) { + sc->multi_chip = true; + device_printf(dev, "multi-chip addresing mode\n"); + } else { + device_printf(dev, "single-chip addressing mode\n"); + } + + if (OF_getencprop(sc->node, "reg", &sc->sw_addr, + sizeof(sc->sw_addr)) < 0) + return (ENXIO); + /* Lock is necessary due to assertions. */ sx_init(&sc->sx, "e6000sw"); E6000SW_LOCK(sc); @@ -218,7 +264,7 @@ default: E6000SW_UNLOCK(sc); sx_destroy(&sc->sx); - device_printf(dev, "Unrecognized device.\n"); + device_printf(dev, "Unrecognized device, id 0x%x.\n", id); return (ENXIO); } @@ -230,48 +276,151 @@ } static int +e6000sw_parse_child_fdt(device_t dev, phandle_t child, uint32_t *fixed_mask, + uint32_t *cpu_mask, int *pport, int *pvlangroup) +{ + char portlabel[100]; + uint32_t port, vlangroup; + boolean_t fixed_link; + + if (fixed_mask == NULL || cpu_mask == NULL || pport == NULL) + return (ENXIO); + + OF_getprop(child, "label", (void *)portlabel, 100); + OF_getencprop(child, "reg", (void *)&port, sizeof(port)); + + if (OF_getencprop(child, "vlangroup", (void *)&vlangroup, + sizeof(vlangroup)) > 0) { + if (vlangroup >= E6000SW_NUM_VGROUPS) + return (ENXIO); + *pvlangroup = vlangroup; + } else { + *pvlangroup = -1; + } + + if (port >= E6000SW_MAX_PORTS) + return (ENXIO); + *pport = port; + + if (strncmp(portlabel, "cpu", 3) == 0) { + device_printf(dev, "CPU port at %d\n", port); + *cpu_mask |= (1 << port); + return (0); + } + + fixed_link = OF_child(child); + if (fixed_link) { + *fixed_mask |= (1 << port); + device_printf(dev, "fixed port at %d\n", port); + } else { + device_printf(dev, "PHY at %d\n", port); + } + + return (0); +} + +static int +e6000sw_init_interface(e6000sw_softc_t *sc, int port) +{ + char name[IFNAMSIZ]; + + snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev)); + + sc->ifp[port] = if_alloc(IFT_ETHER); + if (sc->ifp[port] == NULL) + return (ENOMEM); + sc->ifp[port]->if_softc = sc; + sc->ifp[port]->if_flags |= IFF_UP | IFF_BROADCAST | + IFF_DRV_RUNNING | IFF_SIMPLEX; + sc->ifname[port] = malloc(strlen(name) + 1, M_E6000SW, M_WAITOK); + if (sc->ifname[port] == NULL) + return (ENOMEM); + memcpy(sc->ifname[port], name, strlen(name) + 1); + if_initname(sc->ifp[port], sc->ifname[port], port); + + return (0); +} + +static int +e6000sw_attach_miibus(e6000sw_softc_t *sc, int port) +{ + int err; + + err = mii_attach(sc->dev, &sc->miibus[port], sc->ifp[port], + e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK, + port, MII_OFFSET_ANY, 0); + if (err != 0) + return (err); + + sc->mii[port] = device_get_softc(sc->miibus[port]); + return (0); +} + +static int e6000sw_attach(device_t dev) { e6000sw_softc_t *sc; - int phy, err, port; - char name[IFNAMSIZ]; + phandle_t child; + int err, port, vlangroup; + int member_ports[E6000SW_NUM_VGROUPS]; + etherswitch_vlangroup_t vg; err = 0; sc = device_get_softc(dev); + E6000SW_LOCK(sc); - sc->cpuports_mask = E6000SW_CPUPORTS_MASK; - for (port = 0; port < E6000SW_NUM_PORTS; port++) - sc->vgroup[port] = E6000SW_PORT_NO_VGROUP; e6000sw_setup(dev, sc); + bzero(member_ports, sizeof(member_ports)); - snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev)); - for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) { - sc->ifp[phy] = if_alloc(IFT_ETHER); - if (sc->ifp[phy] == NULL) + for (child = OF_child(sc->node); child != 0; child = OF_peer(child)) { + err = e6000sw_parse_child_fdt(dev, child, &sc->fixed_mask, + &sc->cpuports_mask, &port, &vlangroup); + if (err != 0) { + device_printf(sc->dev, "failed to parse DTS\n"); goto out_fail; - 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_E6000SW, M_WAITOK); - if (sc->ifname[phy] == NULL) + } + + if (vlangroup != -1) + member_ports[vlangroup] |= (1 << port); + + sc->num_ports++; + + err = e6000sw_init_interface(sc, port); + if (err != 0) { + device_printf(sc->dev, "failed to init interface\n"); goto out_fail; - bcopy(name, sc->ifname[phy], strlen(name) + 1); - if_initname(sc->ifp[phy], sc->ifname[phy], phy); - err = mii_attach(sc->dev, &sc->miibus[phy], sc->ifp[phy], - e6000sw_ifmedia_upd, e6000sw_ifmedia_sts, BMSR_DEFCAPMASK, - phy, MII_OFFSET_ANY, 0); + } + + /* Don't attach miibus at CPU/fixed ports */ + if (!e6000sw_is_phyport(sc, port)) + continue; + + err = e6000sw_attach_miibus(sc, port); if (err != 0) { - device_printf(sc->dev, - "attaching PHY %d failed\n", - phy); + device_printf(sc->dev, "failed to attach miibus\n"); goto out_fail; } - sc->mii[phy] = device_get_softc(sc->miibus[phy]); } + + etherswitch_info.es_nports = sc->num_ports; + for (port = 0; port < sc->num_ports; port++) + sc->vgroup[port] = E6000SW_PORT_NO_VGROUP; + + /* Set VLAN configuration */ + e6000sw_port_vlan_conf(sc); + + /* Set vlangroups */ + for (vlangroup = 0; vlangroup < E6000SW_NUM_VGROUPS; vlangroup++) + if (member_ports[vlangroup] != 0) { + vg.es_vlangroup = vg.es_vid = vlangroup; + vg.es_member_ports = vg.es_untagged_ports = + member_ports[vlangroup]; + e6000sw_setvgroup(dev, &vg); + } + E6000SW_UNLOCK(sc); bus_generic_probe(dev); - bus_enumerate_hinted_children(dev); bus_generic_attach(dev); kproc_create(e6000sw_tick, sc, &e6000sw_kproc, 0, 0, @@ -282,7 +431,7 @@ out_fail: e6000sw_detach(dev); - return (ENXIO); + return (err); } static __inline void @@ -294,7 +443,6 @@ continue; } - /* * PHY registers are paged. Put page index in reg 22 (accessible from every * page), then access specific register. @@ -308,7 +456,7 @@ sc = device_get_softc(dev); val = 0; - if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) { + if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { device_printf(dev, "Wrong register address.\n"); return (EINVAL); } @@ -338,7 +486,7 @@ sc = device_get_softc(dev); val = 0; - if (phy >= E6000SW_NUM_PHYS || reg >= E6000SW_NUM_PHY_REGS) { + if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { device_printf(dev, "Wrong register address.\n"); return (EINVAL); } @@ -368,7 +516,7 @@ sc = device_get_softc(dev); bus_generic_detach(dev); sx_destroy(&sc->sx); - for (phy = 0; phy < E6000SW_NUM_PHYS; phy++) { + for (phy = 0; phy < sc->num_ports; phy++) { if (sc->miibus[phy] != NULL) device_delete_child(dev, sc->miibus[phy]); if (sc->ifp[phy] != NULL) @@ -422,7 +570,7 @@ E6000SW_LOCK(sc); - if (p->es_port >= E6000SW_NUM_PORTS || + if (p->es_port >= sc->num_ports || p->es_port < 0) { err = EINVAL; goto out; @@ -430,7 +578,7 @@ e6000sw_get_pvid(sc, p->es_port, &p->es_pvid); - if (e6000sw_cpuport(sc, p->es_port)) { + if (e6000sw_is_cpuport(sc, p->es_port)) { p->es_flags |= ETHERSWITCH_PORT_CPU; ifmr = &p->es_ifmr; ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; @@ -438,6 +586,13 @@ ifmr->ifm_current = ifmr->ifm_active = IFM_ETHER | IFM_1000_T | IFM_FDX; ifmr->ifm_mask = 0; + } else if (e6000sw_is_fixedport(sc, p->es_port)) { + ifmr = &p->es_ifmr; + ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; + ifmr->ifm_count = 0; + ifmr->ifm_current = ifmr->ifm_active = + IFM_ETHER | IFM_1000_T | IFM_FDX; + ifmr->ifm_mask = 0; } else { mii = e6000sw_miiforphy(sc, p->es_port); err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, @@ -462,7 +617,7 @@ E6000SW_LOCK(sc); - if (p->es_port >= E6000SW_NUM_PORTS || + if (p->es_port >= sc->num_ports || p->es_port < 0) { err = EINVAL; goto out; @@ -470,7 +625,7 @@ if (p->es_pvid != 0) e6000sw_set_pvid(sc, p->es_port, p->es_pvid); - if (!e6000sw_cpuport(sc, p->es_port)) { + if (!e6000sw_is_cpuport(sc, p->es_port)) { mii = e6000sw_miiforphy(sc, p->es_port); err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media, SIOCSIFMEDIA); @@ -644,7 +799,7 @@ vg->es_untagged_ports &= PORT_VLAN_MAP_TABLE_MASK; fid = vg->es_vlangroup + 1; - for (port = 0; port < E6000SW_NUM_PORTS; port++) { + for (port = 0; port < sc->num_ports; port++) { if ((sc->members[vg->es_vlangroup] & (1 << port)) || (vg->es_untagged_ports & (1 << port))) e6000sw_flush_port(sc, port); @@ -679,7 +834,7 @@ e6000sw_miiforphy(e6000sw_softc_t *sc, unsigned int phy) { - if (phy >= E6000SW_NUM_PHYS) + if (!e6000sw_is_phyport(sc, phy)) return (NULL); return (device_get_softc(sc->miibus[phy])); @@ -717,13 +872,42 @@ ifmr->ifm_status = mii->mii_media_status; } + +static int +e6000sw_smi_waitready(e6000sw_softc_t *sc, int phy) +{ + int i; + + for (i = 0; i < E6000SW_SMI_TIMEOUT; i++) { + if ((MDIO_READ(sc->dev, phy, SMI_CMD) + & SMI_CMD_BUSY) == 0) + return 0; + } + + return 1; +} + static __inline uint32_t e6000sw_readreg(e6000sw_softc_t *sc, int addr, int reg) { E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); - return (MDIO_READREG(device_get_parent(sc->dev), addr, reg)); + if (!sc->multi_chip) + return (MDIO_READ(sc->dev, addr, reg) & 0xffff); + + if (e6000sw_smi_waitready(sc, sc->sw_addr)) { + printf("e6000sw: readreg timeout\n"); + return (0xffff); + } + MDIO_WRITE(sc->dev, sc->sw_addr, SMI_CMD, SMI_CMD_OP_READ | + (addr << 5) | reg); + if (e6000sw_smi_waitready(sc, sc->sw_addr)) { + printf("e6000sw: readreg timeout\n"); + return (0xffff); + } + + return (MDIO_READ(sc->dev, sc->sw_addr, SMI_DATA) & 0xffff); } static __inline void @@ -732,17 +916,50 @@ E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); - MDIO_WRITEREG(device_get_parent(sc->dev), addr, reg, val); + if (!sc->multi_chip) { + MDIO_WRITE(sc->dev, addr, reg, val); + return; + } + + if (e6000sw_smi_waitready(sc, sc->sw_addr)) { + printf("e6000sw: readreg timeout\n"); + return; + } + MDIO_WRITE(sc->dev, sc->sw_addr, SMI_DATA, val); + MDIO_WRITE(sc->dev, sc->sw_addr, SMI_CMD, SMI_CMD_OP_WRITE | + (addr << 5) | reg); + if (e6000sw_smi_waitready(sc, sc->sw_addr)) { + printf("e6000sw: readreg timeout\n"); + return; + } + + return; } static __inline int -e6000sw_cpuport(e6000sw_softc_t *sc, int port) +e6000sw_is_cpuport(e6000sw_softc_t *sc, int port) { return (sc->cpuports_mask & (1 << port)); } static __inline int +e6000sw_is_fixedport(e6000sw_softc_t *sc, int port) +{ + + return (sc->fixed_mask & (1 << port)); +} + +static __inline int +e6000sw_is_phyport(e6000sw_softc_t *sc, int port) +{ + uint32_t phy_mask; + phy_mask = ~(sc->fixed_mask | sc->cpuports_mask); + + return (phy_mask & (1 << port)); +} + +static __inline int e6000sw_set_pvid(e6000sw_softc_t *sc, int port, int pvid) { @@ -770,17 +987,20 @@ { e6000sw_softc_t *sc; struct mii_softc *miisc; - int i; + int port; sc = arg; E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); for (;;) { E6000SW_LOCK(sc); - for (i = 0; i < E6000SW_NUM_PHYS; i++) { - mii_tick(sc->mii[i]); - LIST_FOREACH(miisc, &sc->mii[i]->mii_phys, mii_list) { - if (IFM_INST(sc->mii[i]->mii_media.ifm_cur->ifm_media) + for (port = 0; port < sc->num_ports; port++) { + /* Tick only on PHY ports */ + if (!e6000sw_is_phyport(sc, port)) + continue; + mii_tick(sc->mii[port]); + LIST_FOREACH(miisc, &sc->mii[port]->mii_phys, mii_list) { + if (IFM_INST(sc->mii[port]->mii_media.ifm_cur->ifm_media) != miisc->mii_inst) continue; mii_phy_update(miisc, MII_POLLSTAT); @@ -815,9 +1035,6 @@ SWITCH_MGMT_FC_PRI_MASK | (1 << SWITCH_MGMT_FORCEFLOW)); - /* Set VLAN configuration */ - e6000sw_port_vlan_conf(sc); - e6000sw_atu_flush(dev, sc, NO_OPERATION); e6000sw_atu_mac_table(dev, sc, NULL, NO_OPERATION); e6000sw_set_atustat(dev, sc, 0, COUNT_ALL); @@ -837,36 +1054,25 @@ e6000sw_port_vlan_conf(e6000sw_softc_t *sc) { int port, ret; - etherswitch_vlangroup_t vg; device_t dev; dev = sc->dev; /* Disable all ports */ - for (port = 0; port < E6000SW_NUM_PORTS; port++) { + for (port = 0; port < sc->num_ports; port++) { ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, (ret & ~PORT_CONTROL_ENABLE)); } /* Set port priority */ - for (port = 0; port < E6000SW_NUM_PORTS; port++) { + for (port = 0; port < sc->num_ports; port++) { ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); ret &= ~PORT_VID_PRIORITY_MASK; e6000sw_writereg(sc, REG_PORT(port), PORT_VID, ret); } - vg.es_vlangroup = 0; - vg.es_vid = 0; - vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP0; - e6000sw_setvgroup(dev, &vg); - vg.es_vlangroup = 1; - vg.es_vid = 1; - vg.es_member_ports = vg.es_untagged_ports = E6000SW_DEF_VLANGROUP1; - e6000sw_setvgroup(dev, &vg); - - device_printf(dev, "Default vlangroups set.\n"); /* Set VID map */ - for (port = 0; port < E6000SW_NUM_PORTS; port++) { + for (port = 0; port < sc->num_ports; port++) { ret = e6000sw_readreg(sc, REG_PORT(port), PORT_VID); ret &= ~PORT_VID_DEF_VID_MASK; ret |= (port + 1); @@ -874,7 +1080,7 @@ } /* Enable all ports */ - for (port = 0; port < E6000SW_NUM_PORTS; port++) { + for (port = 0; port < sc->num_ports; port++) { ret = e6000sw_readreg(sc, REG_PORT(port), PORT_CONTROL); e6000sw_writereg(sc, REG_PORT(port), PORT_CONTROL, (ret | PORT_CONTROL_ENABLE)); Index: sys/dev/etherswitch/e6000sw/e6000swreg.h =================================================================== --- sys/dev/etherswitch/e6000sw/e6000swreg.h +++ sys/dev/etherswitch/e6000sw/e6000swreg.h @@ -42,8 +42,6 @@ * Definitions for the Marvell 88E6000 series Ethernet Switch. */ -#define CPU_PORT 0x5 - /* * Switch Registers */ @@ -167,19 +165,21 @@ #define PHY_PAGE_REG 22 -#define E6000SW_NUM_PHYS 5 +/* + * Scratch and Misc register accessed via + * 'Switch Global Registers' (REG_GLOBAL2) + */ +#define SCR_AND_MISC_REG 0x1a + +#define SCR_AND_MISC_PTR_CFG 0x7000 +#define SCR_AND_MISC_DATA_CFG_MASK 0xf0 + #define E6000SW_NUM_PHY_REGS 29 -#define E6000SW_CPUPORTS_MASK ((1 << 5) | (1 << 6)) #define E6000SW_NUM_VGROUPS 8 -#define E6000SW_NUM_PORTS 7 +#define E6000SW_MAX_PORTS 10 #define E6000SW_PORT_NO_VGROUP -1 #define E6000SW_DEFAULT_AGETIME 20 #define E6000SW_RETRIES 100 - - -/* Default vlangroups */ -#define E6000SW_DEF_VLANGROUP0 (1 | (1 << 1) | (1 << 2) | (1 << 3) | \ - (1 << 6)) -#define E6000SW_DEF_VLANGROUP1 ((1 << 4) | (1 << 5)) +#define E6000SW_SMI_TIMEOUT 16 #endif /* _E6000SWREG_H_ */