Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/etherswitch/e6000sw/e6000sw.c
Show First 20 Lines • Show All 116 Lines • ▼ Show 20 Lines | |||||
static int e6000sw_setconf(device_t, etherswitch_conf_t *); | static int e6000sw_setconf(device_t, etherswitch_conf_t *); | ||||
static void e6000sw_lock(device_t); | static void e6000sw_lock(device_t); | ||||
static void e6000sw_unlock(device_t); | static void e6000sw_unlock(device_t); | ||||
static int e6000sw_getport(device_t, etherswitch_port_t *); | static int e6000sw_getport(device_t, etherswitch_port_t *); | ||||
static int e6000sw_setport(device_t, etherswitch_port_t *); | static int e6000sw_setport(device_t, etherswitch_port_t *); | ||||
static int e6000sw_set_vlan_mode(e6000sw_softc_t *, uint32_t); | static int e6000sw_set_vlan_mode(e6000sw_softc_t *, uint32_t); | ||||
static int e6000sw_readreg_wrapper(device_t, int); | static int e6000sw_readreg_wrapper(device_t, int); | ||||
static int e6000sw_writereg_wrapper(device_t, int, int); | static int e6000sw_writereg_wrapper(device_t, int, int); | ||||
static int e6000sw_readphy_wrapper(device_t, int, int); | |||||
static int e6000sw_writephy_wrapper(device_t, int, int, int); | |||||
static int e6000sw_getvgroup_wrapper(device_t, etherswitch_vlangroup_t *); | static int e6000sw_getvgroup_wrapper(device_t, etherswitch_vlangroup_t *); | ||||
static int e6000sw_setvgroup_wrapper(device_t, etherswitch_vlangroup_t *); | static int e6000sw_setvgroup_wrapper(device_t, etherswitch_vlangroup_t *); | ||||
static int e6000sw_setvgroup(device_t, etherswitch_vlangroup_t *); | static int e6000sw_setvgroup(device_t, etherswitch_vlangroup_t *); | ||||
static int e6000sw_getvgroup(device_t, etherswitch_vlangroup_t *); | static int e6000sw_getvgroup(device_t, etherswitch_vlangroup_t *); | ||||
static void e6000sw_setup(device_t, e6000sw_softc_t *); | static void e6000sw_setup(device_t, e6000sw_softc_t *); | ||||
static void e6000sw_tick(void *, int); | static void e6000sw_tick(void *, int); | ||||
static void e6000sw_set_atustat(device_t, e6000sw_softc_t *, int, int); | static void e6000sw_set_atustat(device_t, e6000sw_softc_t *, int, int); | ||||
static int e6000sw_atu_flush(device_t, e6000sw_softc_t *, int); | static int e6000sw_atu_flush(device_t, e6000sw_softc_t *, int); | ||||
Show All 34 Lines | static device_method_t e6000sw_methods[] = { | ||||
DEVMETHOD(etherswitch_getconf, e6000sw_getconf), | DEVMETHOD(etherswitch_getconf, e6000sw_getconf), | ||||
DEVMETHOD(etherswitch_setconf, e6000sw_setconf), | DEVMETHOD(etherswitch_setconf, e6000sw_setconf), | ||||
DEVMETHOD(etherswitch_lock, e6000sw_lock), | DEVMETHOD(etherswitch_lock, e6000sw_lock), | ||||
DEVMETHOD(etherswitch_unlock, e6000sw_unlock), | DEVMETHOD(etherswitch_unlock, e6000sw_unlock), | ||||
DEVMETHOD(etherswitch_getport, e6000sw_getport), | DEVMETHOD(etherswitch_getport, e6000sw_getport), | ||||
DEVMETHOD(etherswitch_setport, e6000sw_setport), | DEVMETHOD(etherswitch_setport, e6000sw_setport), | ||||
DEVMETHOD(etherswitch_readreg, e6000sw_readreg_wrapper), | DEVMETHOD(etherswitch_readreg, e6000sw_readreg_wrapper), | ||||
DEVMETHOD(etherswitch_writereg, e6000sw_writereg_wrapper), | DEVMETHOD(etherswitch_writereg, e6000sw_writereg_wrapper), | ||||
DEVMETHOD(etherswitch_readphyreg, e6000sw_readphy_wrapper), | DEVMETHOD(etherswitch_readphyreg, e6000sw_readphy), | ||||
DEVMETHOD(etherswitch_writephyreg, e6000sw_writephy_wrapper), | DEVMETHOD(etherswitch_writephyreg, e6000sw_writephy), | ||||
DEVMETHOD(etherswitch_setvgroup, e6000sw_setvgroup_wrapper), | DEVMETHOD(etherswitch_setvgroup, e6000sw_setvgroup_wrapper), | ||||
DEVMETHOD(etherswitch_getvgroup, e6000sw_getvgroup_wrapper), | DEVMETHOD(etherswitch_getvgroup, e6000sw_getvgroup_wrapper), | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static devclass_t e6000sw_devclass; | static devclass_t e6000sw_devclass; | ||||
▲ Show 20 Lines • Show All 271 Lines • ▼ Show 20 Lines | e6000sw_attach(device_t dev) | ||||
taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", | taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s taskq", | ||||
device_get_nameunit(dev)); | device_get_nameunit(dev)); | ||||
TIMEOUT_TASK_INIT(sc->sc_tq, sc->sc_tt, 0, e6000sw_tick, NULL); | TIMEOUT_TASK_INIT(sc->sc_tq, sc->sc_tt, 0, e6000sw_tick, NULL); | ||||
if (ports == 0) { | if (ports == 0) { | ||||
device_printf(dev, "failed to parse DTS: no ports found for " | device_printf(dev, "failed to parse DTS: no ports found for " | ||||
"switch\n"); | "switch\n"); | ||||
E6000SW_UNLOCK(sc); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
for (child = OF_child(ports); child != 0; child = OF_peer(child)) { | for (child = OF_child(ports); child != 0; child = OF_peer(child)) { | ||||
err = e6000sw_parse_child_fdt(sc, child, &port); | err = e6000sw_parse_child_fdt(sc, child, &port); | ||||
if (err != 0) { | if (err != 0) { | ||||
device_printf(sc->dev, "failed to parse DTS\n"); | device_printf(sc->dev, "failed to parse DTS\n"); | ||||
goto out_fail; | goto out_fail; | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | if (e6000sw_is_fixedport(sc, port)) { | ||||
e6000sw_serdes_power(sc->dev, port, sgmii); | e6000sw_serdes_power(sc->dev, port, sgmii); | ||||
} | } | ||||
} | } | ||||
/* Don't attach miibus at CPU/fixed ports */ | /* Don't attach miibus at CPU/fixed ports */ | ||||
if (!e6000sw_is_phyport(sc, port)) | if (!e6000sw_is_phyport(sc, port)) | ||||
continue; | continue; | ||||
/* | |||||
* It's necessary to unlock mutex, because e6000sw_attach_miibus | |||||
* calls functions, which try to lock mutex.That leads | |||||
* to recursive lock on non recursive mutex. | |||||
*/ | |||||
E6000SW_UNLOCK(sc); | |||||
err = e6000sw_attach_miibus(sc, port); | err = e6000sw_attach_miibus(sc, port); | ||||
if (err != 0) { | if (err != 0) { | ||||
device_printf(sc->dev, "failed to attach miibus\n"); | device_printf(sc->dev, "failed to attach miibus\n"); | ||||
goto out_fail; | goto out_fail; | ||||
} | } | ||||
E6000SW_LOCK(sc); | |||||
} | } | ||||
etherswitch_info.es_nports = sc->num_ports; | etherswitch_info.es_nports = sc->num_ports; | ||||
/* Default to port vlan. */ | /* Default to port vlan. */ | ||||
e6000sw_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); | e6000sw_set_vlan_mode(sc, ETHERSWITCH_VLAN_PORT); | ||||
reg = e6000sw_readreg(sc, REG_GLOBAL, SWITCH_GLOBAL_STATUS); | reg = e6000sw_readreg(sc, REG_GLOBAL, SWITCH_GLOBAL_STATUS); | ||||
if (reg & SWITCH_GLOBAL_STATUS_IR) | if (reg & SWITCH_GLOBAL_STATUS_IR) | ||||
device_printf(dev, "switch is ready.\n"); | device_printf(dev, "switch is ready.\n"); | ||||
E6000SW_UNLOCK(sc); | E6000SW_UNLOCK(sc); | ||||
bus_generic_probe(dev); | bus_generic_probe(dev); | ||||
bus_generic_attach(dev); | bus_generic_attach(dev); | ||||
taskqueue_enqueue_timeout(sc->sc_tq, sc->sc_tt, hz); | taskqueue_enqueue_timeout(sc->sc_tq, sc->sc_tt, hz); | ||||
return (0); | return (0); | ||||
out_fail: | out_fail: | ||||
E6000SW_UNLOCK(sc); | |||||
e6000sw_detach(dev); | e6000sw_detach(dev); | ||||
return (err); | return (err); | ||||
} | } | ||||
static int | static int | ||||
e6000sw_waitready(e6000sw_softc_t *sc, uint32_t phy, uint32_t reg, | e6000sw_waitready(e6000sw_softc_t *sc, uint32_t phy, uint32_t reg, | ||||
uint32_t busybit) | uint32_t busybit) | ||||
▲ Show 20 Lines • Show All 88 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static int | static int | ||||
e6000sw_readphy(device_t dev, int phy, int reg) | e6000sw_readphy(device_t dev, int phy, int reg) | ||||
{ | { | ||||
e6000sw_softc_t *sc; | e6000sw_softc_t *sc; | ||||
uint32_t val; | uint32_t val; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); | |||||
if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { | if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { | ||||
device_printf(dev, "Wrong register address.\n"); | device_printf(dev, "Wrong register address.\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); | E6000SW_LOCK(sc); | ||||
if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { | if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { | ||||
device_printf(dev, "Timeout while waiting for switch\n"); | device_printf(dev, "Timeout while waiting for switch\n"); | ||||
E6000SW_UNLOCK(sc); | |||||
return (ETIMEDOUT); | return (ETIMEDOUT); | ||||
} | } | ||||
e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, | e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, | ||||
SMI_CMD_OP_C22_READ | (reg & SMI_CMD_REG_ADDR_MASK) | | SMI_CMD_OP_C22_READ | (reg & SMI_CMD_REG_ADDR_MASK) | | ||||
((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); | ((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); | ||||
if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { | if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { | ||||
device_printf(dev, "Timeout while waiting for switch\n"); | device_printf(dev, "Timeout while waiting for switch\n"); | ||||
E6000SW_UNLOCK(sc); | |||||
return (ETIMEDOUT); | return (ETIMEDOUT); | ||||
} | } | ||||
val = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG); | val = e6000sw_readreg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG); | ||||
E6000SW_UNLOCK(sc); | |||||
return (val & PHY_DATA_MASK); | return (val & PHY_DATA_MASK); | ||||
} | } | ||||
static int | static int | ||||
e6000sw_writephy(device_t dev, int phy, int reg, int data) | e6000sw_writephy(device_t dev, int phy, int reg, int data) | ||||
{ | { | ||||
e6000sw_softc_t *sc; | e6000sw_softc_t *sc; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); | |||||
if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { | if (!e6000sw_is_phyport(sc, phy) || reg >= E6000SW_NUM_PHY_REGS) { | ||||
device_printf(dev, "Wrong register address.\n"); | device_printf(dev, "Wrong register address.\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
E6000SW_LOCK_ASSERT(sc, SA_XLOCKED); | E6000SW_LOCK(sc); | ||||
if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { | if (E6000SW_WAITREADY2(sc, SMI_PHY_CMD_REG, SMI_CMD_BUSY)) { | ||||
device_printf(dev, "Timeout while waiting for switch\n"); | device_printf(dev, "Timeout while waiting for switch\n"); | ||||
E6000SW_UNLOCK(sc); | |||||
return (ETIMEDOUT); | return (ETIMEDOUT); | ||||
} | } | ||||
e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, | e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_DATA_REG, | ||||
data & PHY_DATA_MASK); | data & PHY_DATA_MASK); | ||||
e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, | e6000sw_writereg(sc, REG_GLOBAL2, SMI_PHY_CMD_REG, | ||||
SMI_CMD_OP_C22_WRITE | (reg & SMI_CMD_REG_ADDR_MASK) | | SMI_CMD_OP_C22_WRITE | (reg & SMI_CMD_REG_ADDR_MASK) | | ||||
((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); | ((phy << SMI_CMD_DEV_ADDR) & SMI_CMD_DEV_ADDR_MASK)); | ||||
E6000SW_UNLOCK(sc); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
e6000sw_detach(device_t dev) | e6000sw_detach(device_t dev) | ||||
{ | { | ||||
int phy; | int phy; | ||||
e6000sw_softc_t *sc; | e6000sw_softc_t *sc; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
if (device_is_attached(dev)) | if (device_is_attached(dev)) | ||||
taskqueue_drain_timeout(sc->sc_tq, sc->sc_tt); | taskqueue_drain_timeout(sc->sc_tq, sc->sc_tt); | ||||
if (sc->sc_tq != NULL) | if (sc->sc_tq != NULL) | ||||
taskqueue_free(sc->sc_tq); | taskqueue_free(sc->sc_tq); | ||||
bus_generic_detach(dev); | device_delete_children(dev); | ||||
sx_destroy(&sc->sx); | sx_destroy(&sc->sx); | ||||
for (phy = 0; phy < sc->num_ports; 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) | if (sc->ifp[phy] != NULL) | ||||
if_free(sc->ifp[phy]); | if_free(sc->ifp[phy]); | ||||
if (sc->ifname[phy] != NULL) | if (sc->ifname[phy] != NULL) | ||||
free(sc->ifname[phy], M_E6000SW); | free(sc->ifname[phy], M_E6000SW); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 303 Lines • ▼ Show 20 Lines | if ((addr_reg > (REG_GLOBAL2 * 32 + REG_NUM_MAX)) || | ||||
(addr_reg < (REG_PORT(sc, 0) * 32))) { | (addr_reg < (REG_PORT(sc, 0) * 32))) { | ||||
device_printf(dev, "Wrong register address.\n"); | device_printf(dev, "Wrong register address.\n"); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
e6000sw_writereg(device_get_softc(dev), addr_reg / 5, | e6000sw_writereg(device_get_softc(dev), addr_reg / 5, | ||||
addr_reg % 32, val); | addr_reg % 32, val); | ||||
return (0); | return (0); | ||||
} | |||||
/* | |||||
* These wrappers are necessary because PHY accesses from etherswitchcfg | |||||
* need to be synchronized with locks, while miibus PHY accesses do not. | |||||
*/ | |||||
static int | |||||
e6000sw_readphy_wrapper(device_t dev, int phy, int reg) | |||||
{ | |||||
e6000sw_softc_t *sc; | |||||
int ret; | |||||
sc = device_get_softc(dev); | |||||
E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); | |||||
E6000SW_LOCK(sc); | |||||
ret = e6000sw_readphy(dev, phy, reg); | |||||
E6000SW_UNLOCK(sc); | |||||
return (ret); | |||||
} | |||||
static int | |||||
e6000sw_writephy_wrapper(device_t dev, int phy, int reg, int data) | |||||
{ | |||||
e6000sw_softc_t *sc; | |||||
int ret; | |||||
sc = device_get_softc(dev); | |||||
E6000SW_LOCK_ASSERT(sc, SA_UNLOCKED); | |||||
E6000SW_LOCK(sc); | |||||
ret = e6000sw_writephy(dev, phy, reg, data); | |||||
E6000SW_UNLOCK(sc); | |||||
return (ret); | |||||
} | } | ||||
/* | /* | ||||
* setvgroup/getvgroup called from etherswitchfcg need to be locked, | * setvgroup/getvgroup called from etherswitchfcg need to be locked, | ||||
* while internal calls do not. | * while internal calls do not. | ||||
*/ | */ | ||||
static int | static int | ||||
e6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg) | e6000sw_setvgroup_wrapper(device_t dev, etherswitch_vlangroup_t *vg) | ||||
▲ Show 20 Lines • Show All 594 Lines • Show Last 20 Lines |