Index: arch/arm64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v retrieving revision 1.228 diff -u -p -r1.228 GENERIC --- arch/arm64/conf/GENERIC 31 Mar 2022 14:44:49 -0000 1.228 +++ arch/arm64/conf/GENERIC 2 Jun 2022 03:33:22 -0000 @@ -259,6 +259,7 @@ mvrtc* at fdt? mvspi* at fdt? moxtet* at spi? mvsw* at fdt? +eport* at mvsw? mvtemp* at fdt? mvuart* at fdt? sfp* at fdt? Index: dev/fdt/files.fdt =================================================================== RCS file: /cvs/src/sys/dev/fdt/files.fdt,v retrieving revision 1.162 diff -u -p -r1.162 files.fdt --- dev/fdt/files.fdt 30 Jan 2022 21:40:50 -0000 1.162 +++ dev/fdt/files.fdt 2 Jun 2022 03:33:24 -0000 @@ -462,9 +462,11 @@ device mvspi: spi attach mvspi at fdt file dev/fdt/mvspi.c mvspi -device mvsw +device mvsw {} attach mvsw at fdt -file dev/fdt/mvsw.c mvsw +device eport: ether, ifnet, mii, ifmedia +attach eport at mvsw +file dev/fdt/mvsw.c mvsw | eport device mvtemp attach mvtemp at fdt Index: dev/fdt/if_mvneta.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/if_mvneta.c,v retrieving revision 1.25 diff -u -p -r1.25 if_mvneta.c --- dev/fdt/if_mvneta.c 1 Jun 2022 08:19:15 -0000 1.25 +++ dev/fdt/if_mvneta.c 2 Jun 2022 03:33:24 -0000 @@ -173,6 +173,8 @@ struct mvneta_softc { int sc_sfp; int sc_node; + struct if_device sc_ifd; + #if NKSTAT > 0 struct mutex sc_kstat_lock; struct timeout sc_kstat_tick; @@ -789,6 +791,10 @@ mvneta_attach_deferred(struct device *se */ if_attach(ifp); ether_ifattach(ifp); + + sc->sc_ifd.if_node = sc->sc_node; + sc->sc_ifd.if_ifp = ifp; + if_register(&sc->sc_ifd); #if NKSTAT > 0 mvneta_kstat_attach(sc); Index: dev/fdt/mvsw.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/mvsw.c,v retrieving revision 1.5 diff -u -p -r1.5 mvsw.c --- dev/fdt/mvsw.c 6 Apr 2022 18:59:28 -0000 1.5 +++ dev/fdt/mvsw.c 2 Jun 2022 03:33:24 -0000 @@ -15,10 +15,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "bpfilter.h" + #include #include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include @@ -27,7 +36,31 @@ #include #include +#include +#include +#include + #include +#include + +#include +#include +#include + +#include +#include + +#include /* if_trunk.h uses siphash bits */ +#include + +#if NBPFILTER > 0 +#include +#endif + +#define MVSW_MAX_PORTS 11 + +#define ETHERTYPE_MVSW_ETAG ETHERTYPE_802_EX1 +#define ETHERTYPE_MVSW_DEFAULT 0x9000 /* who cares? */ /* Registers */ @@ -49,34 +82,304 @@ #define MVSW_SMI_TIMEOUT 1600 /* Switch registers */ -#define MVSW_PORT(x) (0x10 + (x)) -#define MVSW_G2 0x1c +#define MVSW_PORT(x) (0x10 + (x)) /* Port */ +#define MVSW_G1 0x1b /* Global1 */ +#define MVSW_G2 0x1c /* Global2 */ +/* + * Port registers */ #define MVSW_PORT_SWITCHID 0x03 #define MVSW_PORT_SWITCHID_PROD_MASK 0xfff0 #define MVSW_PORT_SWITCHID_PROD_88E6141 0x3400 #define MVSW_PORT_SWITCHID_PROD_88E6341 0x3410 #define MVSW_PORT_SWITCHID_REV_MASK 0x000f -#define MVSW_PORT_CTRL 0x04 -#define MVSW_PORT_CTRL_STATE_MASK 0x0003 -#define MVSW_PORT_CTRL_STATE_FORWARD 0x0003 +#define MVSW_PORT_CTRL0 0x04 +#define MVSW_PORT_CTRL0_STATE_MASK (0x3 << 0) +#define MVSW_PORT_CTRL0_STATE_DISABLED (0x0 << 0) +#define MVSW_PORT_CTRL0_STATE_BLOCKING (0x1 << 0) +#define MVSW_PORT_CTRL0_STATE_LEARNING (0x2 << 0) +#define MVSW_PORT_CTRL0_STATE_FORWARD (0x3 << 0) +#define MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST (0x1 << 2) +#define MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST (0x1 << 3) +#define MVSW_PORT_CTRL0_TAG_IF_BOTH (0x1 << 6) +#define MVSW_PORT_CTRL0_VLAN_TUNNEL (0x1 << 7) +#define MVSW_PORT_CTRL0_FRAME_MODE_MASK (0x3 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_NORMAL (0x0 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_TAG (0x1 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_PROVIDER (0x2 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_ETAG (0x3 << 8) +#define MVSW_PORT_CTRL0_IGMP_MLD_SNOOP (0x1 << 10) +#define MVSW_PORT_CTRL0_HEADER (0x1 << 11) +#define MVSW_PORT_CTRL0_EGRESS_MODE_MASK (0x3 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_UNMODIFIED (0x0 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_UNTAGGED (0x1 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_TAGGED (0x2 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_ETAG (0x3 << 12) +#define MVSW_PORT_CTRL0_SAFILTER_MASK (0x3 << 14) +#define MVSW_PORT_CTRL0_SAFILTER_DROP_ON_LOCK (0x1 << 14) +#define MVSW_PORT_CTRL0_SAFILTER_DROP_ON_UNLOCK (0x2 << 14) +#define MVSW_PORT_CTRL0_SAFILTER_DROP_TO_CPU (0x3 << 14) + +#define MVSW_PORT_CTRL1 0x05 +#define MVSW_PORT_CTRL1_FID_HI_SHIFT 0 +#define MVSW_PORT_CTRL1_FID_HI_MASK 0xff +#define MVSW_PORT_CTRL1_TRUNK_ID_SHIFT 8 +#define MVSW_PORT_CTRL1_TRUNK_ID_MASK 0x0f +#define MVSW_PORT_CTRL1_TRUNK_PORT (0x1 << 14) +#define MVSW_PORT_CTRL1_MESSAGE_PORT (0x1 << 15) + +#define MVSW_PORT_BASED_VLAN 0x06 +#define MVSW_PORT_BASED_VLAN_FID_LO_SHIFT 0 +#define MVSW_PORT_BASED_VLAN_FID_LO_MASK 0 + +/* Default Port VLAN */ +#define MVSW_PORT_DEFAULT_VLAN 0x07 +#define MVSW_PORT_DEVAULT_VLAN_VID_SHIFT 0 +#define MVSW_PORT_DEVAULT_VLAN_VID_MASK 0xfff + +/* Port Control 2 */ +#define MVSW_PORT_CTRL2 0x08 +#define MVSW_PORT_CTRL2_JUMBO_MODE_MASK (0x3 << 12) +#define MVSW_PORT_CTRL2_JUMBO_MODE_1522 (0x0 << 12) +#define MVSW_PORT_CTRL2_JUMBO_MODE_2048 (0x1 << 12) +#define MVSW_PORT_CTRL2_JUMBO_MODE_10240 (0x2 << 12) +#define MVSW_PORT_CTRL2_8021Q_MODE_MASK (0x3 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_DISABLED (0x0 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_FALLBACK (0x1 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_CHECK (0x2 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_SECURE (0x3 << 10) +#define MVSW_PORT_CTRL2_DISCARD_TAGGED (0x1 << 9) +#define MVSW_PORT_CTRL2_DISCARD_UNTAGGED (0x1 << 8) +#define MVSW_PORT_CTRL2_MAP_DA (0x1 << 7) + +/* Port Association Vector */ +#define MVSW_PORT_ASSOC_VECTOR 0x0b +#define MVSW_PORT_ASSOC_VECTOR_HOLD_AT_1 (0x1 << 15) +#define MVSW_PORT_ASSOC_VECTOR_INT_AGE_OUT (0x1 << 14) +#define MVSW_PORT_ASSOC_VECTOR_LOCKED_PORT (0x1 << 13) +#define MVSW_PORT_ASSOC_VECTOR_IGNORE_WRONG (0x1 << 12) +#define MVSW_PORT_ASSOC_VECTOR_REFRESH_LOCKED (0x1 << 11) +/* i think low bits are a bitmap of relevant ports */ + +#define MVSW_PORT_ETH_TYPE 0x0f + +/* + * Global1 registers + */ + +/* ATU FID */ +#define MVSW_G1_ATU_FID 0x01 + +#define MVSW_G1_VTU_OP 0x05 +#define MVSW_G1_VTU_OP_BUSY (0x1 << 15) +#define MVSW_G1_VTU_OP_MASK (0x7 << 12) +#define MVSW_G1_VTU_OP_FLUSH_ALL (0x1 << 12) +#define MVSW_G1_VTU_OP_NOOP (0x2 << 12) +#define MVSW_G1_VTU_OP_VTU_LOAD_PURGE (0x3 << 12) +#define MVSW_G1_VTU_OP_VTU_GET_NEXT (0x4 << 12) +#define MVSW_G1_VTU_OP_STU_LOAD_PURGE (0x5 << 12) +#define MVSW_G1_VTU_OP_STU_GET_NEXT (0x6 << 12) +#define MVSW_G1_VTU_OP_GET_CLR_VIOLATION (0x7 << 12) +#define MVSW_G1_VTU_OP_MEMBER_VIOLATION (0x1 << 6) +#define MVSW_G1_VTU_OP_MISS_VIOLATION (0x1 << 5) +#define MVSW_G1_VTU_OP_SPID_MASK (0xf << 0) + +/* ATU Control */ +#define MVSW_G1_ATU_CTRL 0x0a +#define MVSW_G1_ATU_CTRL_LEARN2ALL (0x1 << 3) + +/* ATU Operation */ +#define MVSW_G1_ATU_OP 0x0a +#define MVSW_G1_ATU_OP_BUSY (0x1 << 15) +#define MVSW_G1_ATU_OP_MASK (0x7 << 12) +#define MVSW_G1_ATU_OP_NOOP (0x0 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_ALL (0x1 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_NON_STATIC (0x2 << 12) +#define MVSW_G1_ATU_OP_LOAD_DB (0x3 << 12) +#define MVSW_G1_ATU_OP_GET_NEXT_DB (0x4 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_ALL_DB (0x5 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB (0x6 << 12) +#define MVSW_G1_ATU_OP_GET_CLR_VIOLATION (0x7 << 12) +#define MVSW_G1_ATU_OP_AGE_OUT_VIOLATION (0x1 << 7) +#define MVSW_G1_ATU_OP_MEMBER_VIOLATION (0x1 << 6) +#define MVSW_G1_ATU_OP_MISS_VIOLATION (0x1 << 5) +#define MVSW_G1_ATU_OP_FULL_VIOLATION (0x1 << 4) + +/* ATU Data */ +#define MVSW_G1_ATU_DATA 0x0c +#define MVSW_G1_ATU_DATA_TRUNK (0x1 << 15) +#define MVSW_G1_ATU_DATA_TRUNK_ID_MASK (0xf << 4) +#define MVSW_G1_ATU_DATA_PORT_VECTOR_MASK (0x3ff << 4) +#define MVSW_G1_ATU_DATA_STATE_MASK (0xf << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_UNUSED (0x0 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST (0x1 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_2 (0x2 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_3 (0x3 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_4 (0x4 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_5 (0x5 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_6 (0x6 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST (0x7 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_POLICY (0x8 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO (0x9 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL (0xa << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO (0xb << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT (0xc << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO (0xd << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC (0xe << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_PO (0xf << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_UNUSED (0x0 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_POLICY (0x4 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL (0x5 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT (0x6 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC (0x7 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO (0xc << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO (0xd << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO (0xe << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_PO (0xf << 0) + +#define MVSW_G1_ATU_MAC_BASE 0x0d +#define MVSW_G1_ATU_MAC_01 (MVSW_G1_ATU_MAC_BASE + 0) +#define MVSW_G1_ATU_MAC_23 (MVSW_G1_ATU_MAC_BASE + 1) +#define MVSW_G1_ATU_MAC_45 (MVSW_G1_ATU_MAC_BASE + 2) + +/* Monitor & MGMT Control */ +#define MVSW_G1_MONITOR_MGMT_CTL 0x1a +#define MVSW_G1_MONITOR_MGMT_CTL_UPDATE (0x1 << 15) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_MASK (0x3f << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200000XLO (0x00 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200000XHI (0x01 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200002XLO (0x02 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200002XHI (0x03 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST (0x20 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST (0x21 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST (0x30 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_DATA_SHIFT 0 +#define MVSW_G1_MONITOR_MGMT_CTL_DATA_MASK 0xff +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI 0xe0 + +/* Control2 */ +#define MVSW_G1_CTRL2 0x1c +#define MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT 0 +#define MVSW_G1_CTRL2_DEVICE_NUMBER_MASK 0x1f +#define MVSW_G1_CTRL2_RMU_MODE_MASK (0x7 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_PORT_0 (0x0 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_PORT_1 (0x1 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_ALL_DSA (0x6 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_DISABLED (0x7 << 8) + +/* + * Global2 registers + */ + +/* Trunk Mask Table */ +#define MVSW_G2_TRUNK_MASK 0x07 +#define MVSW_G2_TRUNK_MASK_UPDATE (0x1 << 15) +#define MVSW_G2_TRUNK_MASK_SHIFT 12 +#define MVSW_G2_TRUNK_MASK_COUNT 8 /* 0x0 to 0x7 */ +#define MVSW_G2_TRUNK_MASK_HASH (0x1 << 11) +/* low bits are a bitmap of ports in the trunk i think */ + +/* Trunk Mapping Table */ +#define MVSW_G2_TRUNK_MAPPING 0x08 +#define MVSW_G2_TRUNK_MAPPING_UPDATE (0x1 << 15) +#define MVSW_G2_TRUNK_MAPPING_ID_SHIFT 11 +#define MVSW_G2_TRUNK_MAPPING_ID_COUNT 16 /* 0x0 to 0xf */ +/* low bits are a bitmap of ports in the trunk i think */ + +/* Ingress Rate Command */ +#define MVSW_G2_IRL_CMD 0x09 +#define MVSW_G2_IRL_CMD_BUSY (0x1 << 15) +#define MVSW_G2_IRL_CMD_OP_MASK (0x7 << 12) +#define MVSW_G2_IRL_CMD_OP_NOOP (0x0 << 12) +#define MVSW_G2_IRL_CMD_OP_INIT_ALL (0x1 << 12) +#define MVSW_G2_IRL_CMD_OP_INIT_RES (0x2 << 12) +#define MVSW_G2_IRL_CMD_OP_WRITE_REG (0x3 << 12) +#define MVSW_G2_IRL_CMD_OP_READ_REG (0x4 << 12) +#define MVSW_G2_IRL_CMD_PORT_SHIFT 8 +#define MVSW_G2_IRL_CMD_PORT_MASK 0xf +#define MVSW_G2_IRL_CMD_RES_MASK (0x7 << 5) +#define MVSW_G2_IRL_CMD_REG_MASK (0xf << 0) + +/* Ingress Rate Data */ +#define MVSW_G2_IRL_DATA 0x0a + #define MVSW_G2_SMI_PHY_CMD 0x18 #define MVSW_G2_SMI_PHY_DATA 0x19 +/* Misc */ +#define MVSW_G2_MISC 0x1d +#define MVSW_G2_MISC_5BIT_PORT (0x1 << 14) + /* SERDES registers */ #define MVSW_SERDES(x) (0x10 + (x)) #define MVSW_SERDES_BMCR (0x2000 + MII_BMCR) +struct mvsw_tag { + uint16_t tag0; +#define MVSW_TAG_MODE_SHIFT 14 +#define MVSW_TAG_MODE_MASK (0x3 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_TO_CPU (0x0 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_FROM_CPU (0x1 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_TO_SNIFFER (0x2 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_TAG (0x3 << MVSW_TAG_MODE_SHIFT) + +#define MVSW_TAG_IEEE (1 << 13) + +#define MVSW_TAG_SWITCH_SHIFT 8 +#define MVSW_TAG_SWITCH_MASK 0x1f + +#define MVSW_TAG_PORT_SHIFT 3 +#define MVSW_TAG_PORT_MASK 0x1f + + uint16_t tag1; +}; + +struct mvsw_etag { + uint16_t reserved; + uint16_t tag0; + uint16_t tag1; +}; + /* XXX #include */ #define MDIO_MMD_PHYXS 4 +/* + * The driver. + */ + +struct mvsw_port { + int p_port; + struct mvsw_softc *p_softc; + struct ifnet *p_ifp0; + + int (*p_ioctl)(struct ifnet *, u_long, caddr_t); + void (*p_input)(struct ifnet *, struct mbuf *); + int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); + + TAILQ_ENTRY(mvsw_port) p_entry; +}; +TAILQ_HEAD(mvsw_ports, mvsw_port); + +struct eport_softc; +static void eport_input(struct eport_softc *, struct mbuf *); + struct mvsw_softc { - struct device sc_dev; + struct device sc_dev; - struct mii_bus *sc_mdio; - int sc_reg; + int sc_node; + struct mii_bus *sc_mdio; + int sc_reg; + + unsigned int sc_nports; + struct eport_softc *sc_ports[MVSW_MAX_PORTS]; + struct mvsw_ports sc_cpus; + + caddr_t sc_bpf; }; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + int mvsw_match(struct device *, void *, void *); void mvsw_attach(struct device *, struct device *, void *); @@ -88,6 +391,23 @@ struct cfdriver mvsw_cd = { NULL, "mvsw", DV_DULL }; +struct mvsw_defer { + struct task d_task; + struct mvsw_softc *d_sc; +}; + +static void mvsw_attach_deferred(void *); + +static void mvsw_attach_cpu(struct mvsw_softc *, int, uint32_t); +static void mvsw_config_cpu(struct mvsw_softc *, struct mvsw_port *); + +static int mvsw_p_ioctl(struct ifnet *, u_long, caddr_t); +static void mvsw_p_input(struct ifnet *, struct mbuf *); +static int mvsw_p_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); + +static int mvsw_print(void *, const char *); + int mvsw_smi_read(struct mvsw_softc *, int, int); void mvsw_smi_write(struct mvsw_softc *, int, int, int); int mvsw_phy_read(struct mvsw_softc *, int, int); @@ -95,7 +415,25 @@ void mvsw_phy_write(struct mvsw_softc *, int mvsw_serdes_read(struct mvsw_softc *, int, int, int); void mvsw_serdes_write(struct mvsw_softc *, int, int, int, int); -void mvsw_port_enable(struct mvsw_softc *, int); +static int mvsw_wait(struct mvsw_softc *, int, int, uint16_t, uint16_t, + const char *); + +#define mvsw_vtu_wait(_sc) \ + mvsw_wait((_sc), MVSW_G1, MVSW_G1_VTU_OP, \ + MVSW_G1_VTU_OP_BUSY, 0, "mvswvtu") + +#define mvsw_atu_wait(_sc) \ + mvsw_wait((_sc), MVSW_G1, MVSW_G1_ATU_OP, \ + MVSW_G1_ATU_OP_BUSY, 0, "mvswatu") + +#define mvsw_irl_wait(_sc) \ + mvsw_wait((_sc), MVSW_G2, MVSW_G2_IRL_CMD, \ + MVSW_G2_IRL_CMD_BUSY, 0, "mvswirl") + +static int mvsw_vtu_op(struct mvsw_softc *, uint16_t); +static int mvsw_atu_op(struct mvsw_softc *, uint16_t, uint16_t, uint16_t); +static int mvsw_irl_op(struct mvsw_softc *, uint16_t); + void mvsw_phy_enable(struct mvsw_softc *, int); void mvsw_serdes_enable(struct mvsw_softc *, int); @@ -112,15 +450,18 @@ mvsw_attach(struct device *parent, struc { struct mvsw_softc *sc = (struct mvsw_softc *)self; struct fdt_attach_args *faa = aux; - int ports, port, node; - uint32_t phy; - uint16_t swid; + uint16_t r; + struct mvsw_defer *d; + + TAILQ_INIT(&sc->sc_cpus); + sc->sc_nports = nitems(sc->sc_ports); if (faa->fa_nreg < 1) { printf(": no registers\n"); return; } + sc->sc_node = faa->fa_node; sc->sc_reg = faa->fa_reg[0].addr; printf(" phy %d", sc->sc_reg); @@ -130,25 +471,149 @@ mvsw_attach(struct device *parent, struc return; } - swid = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID); - switch (swid & MVSW_PORT_SWITCHID_PROD_MASK) { + r = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID); + switch (r & MVSW_PORT_SWITCHID_PROD_MASK) { case MVSW_PORT_SWITCHID_PROD_88E6141: + sc->sc_nports = 6; printf(": 88E6141"); break; case MVSW_PORT_SWITCHID_PROD_88E6341: + sc->sc_nports = 6; printf(": 88E6341"); break; default: printf(": unknown product 0x%04x\n", - swid & MVSW_PORT_SWITCHID_PROD_MASK); + r & MVSW_PORT_SWITCHID_PROD_MASK); return; } - printf(" rev %d\n", swid & MVSW_PORT_SWITCHID_REV_MASK); + printf(" rev %d\n", r & MVSW_PORT_SWITCHID_REV_MASK); - ports = OF_getnodebyname(faa->fa_node, "ports"); - if (ports == 0) + if (sc->sc_dev.dv_unit & ~MVSW_G1_CTRL2_DEVICE_NUMBER_MASK) { + printf("%s: too many switches\n", DEVNAME(sc)); return; + } + + /* + * wait until the cpu port is (probably) attached to wire things up. + */ + + d = malloc(sizeof(*d), M_TEMP, M_WAITOK); + task_set(&d->d_task, mvsw_attach_deferred, d); + d->d_sc = sc; + + config_pending_incr(); + task_add(systq, &d->d_task); +} + +static void +mvsw_attach_deferred(void *arg) +{ + struct mvsw_defer *d = arg; + struct mvsw_softc *sc = d->d_sc; + int ports, port, node, i; + uint32_t phy, phandle; + uint16_t r; + struct mvsw_port *p; + + free(d, M_TEMP, sizeof(*d)); + + for (port = 0; port < sc->sc_nports; port++) { + /* start with all ports disabled */ + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL0); + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); + SET(r, MVSW_PORT_CTRL0_STATE_DISABLED); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL0, r); + + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL1); + CLR(r, MVSW_PORT_CTRL1_MESSAGE_PORT); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL1, r); + + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_DEFAULT_VLAN, 0); + + /* reset ingress rate limiting (IRL) */ + if (mvsw_irl_op(sc, MVSW_G2_IRL_CMD_OP_INIT_ALL | + (port << MVSW_G2_IRL_CMD_PORT_SHIFT)) == -1) { + printf("%s: unable to reset ingress rate limiting " + "on port %u\n", DEVNAME(sc), port); + /* we can carry on */ + } + } + + /* flush the vlan translation unit */ + if (mvsw_vtu_wait(sc) == -1) { + printf("%s: VLAN Translation Unit busy\n", DEVNAME(sc)); + goto done; + } + + if (mvsw_vtu_op(sc, MVSW_G1_VTU_OP_FLUSH_ALL) == -1) { + printf("%s: VLAN Translation Unit flush timeout\n", + DEVNAME(sc)); + goto done; + } + + /* clear 5 bit port use in port vlan table (PVT) */ + r = mvsw_smi_read(sc, MVSW_G2, MVSW_G2_MISC); + CLR(r, MVSW_G2_MISC_5BIT_PORT); + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_MISC, r); + + /* XXX PVT clear/reset/setup? */ + + /* flush the address translation unit */ + if (mvsw_atu_wait(sc) == -1) { + printf("%s: Address Translation Unit busy\n", DEVNAME(sc)); + goto done; + } + + if (mvsw_atu_op(sc, 0, MVSW_G1_ATU_OP_FLUSH_MOVE_ALL, 0) == -1) { + printf("%s: Address Translation Unit flush timeout\n", + DEVNAME(sc)); + goto done; + } + + /* XXX clear priority overrite table */ + + r = mvsw_smi_read(sc, MVSW_G1, MVSW_G1_CTRL2); + /* set device number */ + CLR(r, MVSW_G1_CTRL2_DEVICE_NUMBER_MASK << + MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT); + SET(r, sc->sc_dev.dv_unit << + MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT); + + /* disable remote management */ + CLR(r, MVSW_G1_CTRL2_RMU_MODE_MASK); + SET(r, MVSW_G1_CTRL2_RMU_MODE_DISABLED); + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_CTRL2, r); + + /* clear trunk setup */ + for (i = 0; i < MVSW_G2_TRUNK_MASK_COUNT; i++) { + /* XXX dlg - this stops packets, needs more investigation. */ +#if 0 + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_TRUNK_MASK, + MVSW_G2_TRUNK_MASK_UPDATE | + (i << MVSW_G2_TRUNK_MASK_SHIFT) | /* clear bitmap */ 0); +#endif + } + for (i = 0; i < MVSW_G2_TRUNK_MAPPING_ID_COUNT; i++) { + /* XXX dlg - this stops packets, needs more investigation. */ +#if 0 + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_TRUNK_MAPPING, + MVSW_G2_TRUNK_MAPPING_UPDATE | + (i << MVSW_G2_TRUNK_MAPPING_ID_SHIFT) | + /* clear bitmap */ 0); +#endif + } + + ports = OF_getnodebyname(sc->sc_node, "ports"); + if (ports == 0) + goto done; + for (port = OF_child(ports); port; port = OF_peer(port)) { + char status[32]; + + if (OF_getprop(node, "status", status, sizeof(status)) > 0 && + strcmp(status, "disabled") == 0) + continue; + phy = OF_getpropint(port, "phy-handle", 0); node = OF_getnodebyphandle(phy); if (node) @@ -156,8 +621,270 @@ mvsw_attach(struct device *parent, struc else mvsw_serdes_enable(sc, port); - mvsw_port_enable(sc, port); + phandle = OF_getpropint(port, "ethernet", 0); + if (phandle != 0) + mvsw_attach_cpu(sc, port, phandle); + else { + uint32_t reg = OF_getpropint(port, "reg", + MVSW_MAX_PORTS); + struct device *child; + + if (reg < sc->sc_nports) { + child = config_found(&sc->sc_dev, &port, + mvsw_print); + sc->sc_ports[reg] = + (struct eport_softc *)child; + } + } } + + p = TAILQ_FIRST(&sc->sc_cpus); + if (p == NULL) { + printf("%s: no CPU ports found\n", DEVNAME(sc)); + goto done; + } + + mvsw_config_cpu(sc, p); + + r = 0x1 << p->p_port; + for (port = 0; port < sc->sc_nports; port++) { + if (sc->sc_ports[port] == NULL) + continue; + + mvsw_smi_write(sc, MVSW_PORT(port), + MVSW_PORT_BASED_VLAN, r); + } + +done: + config_pending_decr(); +} + +static void +mvsw_attach_cpu(struct mvsw_softc *sc, int node, uint32_t phandle) +{ + struct ifnet *ifp0; + struct arpcom *ac0; + struct mvsw_port *p; + int port; + uint16_t r; + + port = OF_getpropint(node, "reg", -1); + if (port == -1) { + printf("%s: can't find cpu interface port number\n", + DEVNAME(sc)); + return; + } + + ifp0 = if_byphandle(phandle); + if (ifp0 == NULL) { + printf("%s: unable to find cpu interface on port %u\n", + DEVNAME(sc), port); + return; + } + + if (ifp0->if_type != IFT_ETHER) { + printf("%s: unsupported type of cpu interface on port %u\n", + DEVNAME(sc), port); + return; + } + + printf("%s: %s at port %u\n", DEVNAME(sc), ifp0->if_xname, port); + + NET_LOCK(); + ac0 = (struct arpcom *)ifp0; + if (ac0->ac_trunkport != NULL) { + printf("%s: cpu interface %s is busy\n", + DEVNAME(sc), ifp0->if_xname); + NET_UNLOCK(); + return; + } + + p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK); + + p->p_softc = sc; + p->p_ifp0 = ifp0; + p->p_port = port; + p->p_ioctl = ifp0->if_ioctl; + p->p_input = ifp0->if_input; + p->p_output = ifp0->if_output; + + TAILQ_INSERT_TAIL(&sc->sc_cpus, p, p_entry); + + if (ifpromisc(ifp0, 1) != 0) + printf("%s: %s promisc error\n", DEVNAME(sc), ifp0->if_xname); + + ac0->ac_trunkport = p; + /* membar_producer()? */ + ifp0->if_ioctl = mvsw_p_ioctl; + ifp0->if_input = mvsw_p_input; + ifp0->if_output = mvsw_p_output; + NET_UNLOCK(); + + /* Enable port. */ + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL0); + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); + SET(r, MVSW_PORT_CTRL0_STATE_FORWARD); + CLR(r, MVSW_PORT_CTRL0_FRAME_MODE_MASK); + SET(r, MVSW_PORT_CTRL0_FRAME_MODE_ETAG); + CLR(r, MVSW_PORT_CTRL0_EGRESS_MODE_MASK); + SET(r, MVSW_PORT_CTRL0_EGRESS_MODE_ETAG); + SET(r, MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST | + MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL0, r); + + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL2); + CLR(r, MVSW_PORT_CTRL2_MAP_DA); + CLR(r, MVSW_PORT_CTRL2_JUMBO_MODE_MASK); + SET(r, MVSW_PORT_CTRL2_JUMBO_MODE_10240); + CLR(r, MVSW_PORT_CTRL2_8021Q_MODE_MASK); + SET(r, MVSW_PORT_CTRL2_8021Q_MODE_DISABLED); + CLR(r, MVSW_PORT_CTRL2_DISCARD_TAGGED); + CLR(r, MVSW_PORT_CTRL2_DISCARD_UNTAGGED); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL2, r); + + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_ASSOC_VECTOR, + 0x1 << port); + + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_ETH_TYPE, + ETHERTYPE_MVSW_ETAG); +} + +static void +mvsw_config_cpu(struct mvsw_softc *sc, struct mvsw_port *p) +{ + int port = p->p_port; + uint16_t r; + + /* tell the switch this is the cpu port */ + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | + MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST | + (port | MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI); + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); + + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | + MVSW_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST | + port; + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); + + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | + MVSW_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST | + port; + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); +} + +static int +mvsw_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mvsw_port *p = ac0->ac_trunkport; + int error = 0; + + switch (cmd) { + case SIOCGTRUNKPORT: { + struct trunk_reqport *rp = (struct trunk_reqport *)data; + struct mvsw_softc *sc = p->p_softc; + + if (strncmp(rp->rp_ifname, rp->rp_portname, + sizeof(rp->rp_ifname)) != 0) + return (EINVAL); + + (void)strlcpy(rp->rp_ifname, DEVNAME(sc), + sizeof(rp->rp_ifname)); + break; + } + + case SIOCSIFLLADDR: + error = EBUSY; + break; + + default: + error = (*p->p_ioctl)(ifp0, cmd, data); + break; + } + + return (error); +} + +static int +mvsw_p_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mvsw_port *p = ac0->ac_trunkport; + +#if 0 + /* restrict transmission to bpf only */ + if (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL) { + m_freem(m); + return (EBUSY); + } +#endif + + return ((*p->p_output)(ifp0, m, dst, rt)); +} + +static void +mvsw_p_input(struct ifnet *ifp0, struct mbuf *m) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mvsw_port *p = ac0->ac_trunkport; + struct mvsw_softc *sc = p->p_softc; + struct ether_header *eh; + struct mvsw_etag *etag; + int hlen = sizeof(*eh) + sizeof(*etag); + int diff = hlen - offsetof(struct ether_header, ether_type); + uint16_t tag0; + struct eport_softc *eport; + + eh = mtod(m, struct ether_header *); + if (eh->ether_type != htons(ETHERTYPE_MVSW_ETAG)) + goto drop; + + if (m->m_len < hlen) { + m = m_pullup(m, hlen); + if (m == NULL) { + /* drop++ */ + return; + } + + eh = mtod(m, struct ether_header *); + } + + etag = (struct mvsw_etag *)(eh + 1); + tag0 = bemtoh16(&etag->tag0); + + int port = (tag0 >> MVSW_TAG_PORT_SHIFT) & MVSW_TAG_PORT_MASK; + if (port >= sc->sc_nports) + goto drop; + + eport = sc->sc_ports[port]; + if (eport == NULL) + goto drop; + + memmove(mtod(m, caddr_t) + diff, mtod(m, caddr_t), + offsetof(struct ether_header, ether_type)); + m_adj(m, diff); + + eport_input(eport, m); + return; + +drop: + m_freem(m); +} + +static int +mvsw_print(void *aux, const char *pnp) +{ + int node = *(int *)aux; + int port; + + if (pnp != NULL) + printf("\"port\" at %s", pnp); + + port = OF_getpropint(node, "reg", 0); + printf(" port %d", port); + + return (UNCONF); } static inline int @@ -219,6 +946,53 @@ mvsw_smi_write(struct mvsw_softc *sc, in mvsw_smi_wait(sc); } +static int +mvsw_wait(struct mvsw_softc *sc, int phy, int reg, uint16_t mask, uint16_t v, + const char *wmesg) +{ + unsigned int i; + uint16_t r; + + for (i = 0; i < 16; i++) { + r = mvsw_smi_read(sc, phy, reg); + if ((r & mask) == v) + return (0); + + tsleep_nsec(&sc->sc_mdio, PPAUSE, wmesg, 1500000); + } + + return (-1); +} + +static int +mvsw_vtu_op(struct mvsw_softc *sc, uint16_t op) +{ + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_VTU_OP, + MVSW_G1_VTU_OP_BUSY | op); + + return (mvsw_vtu_wait(sc)); +} + +static int +mvsw_atu_op(struct mvsw_softc *sc, uint16_t fid, uint16_t op, uint16_t data) +{ + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_ATU_DATA, data); + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_ATU_FID, fid); + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_VTU_OP, + MVSW_G1_VTU_OP_BUSY | op); + + return (mvsw_atu_wait(sc)); +} + +static int +mvsw_irl_op(struct mvsw_softc *sc, uint16_t op) +{ + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_IRL_CMD, + MVSW_G2_IRL_CMD_BUSY | op); + + return (mvsw_irl_wait(sc)); +} + int mvsw_phy_wait(struct mvsw_softc *sc) { @@ -308,23 +1082,6 @@ mvsw_serdes_write(struct mvsw_softc *sc, } void -mvsw_port_enable(struct mvsw_softc *sc, int node) -{ - uint16_t val; - int port; - - port = OF_getpropint(node, "reg", -1); - if (port == -1) - return; - - /* Enable port. */ - val = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL); - val &= ~MVSW_PORT_CTRL_STATE_MASK; - val |= MVSW_PORT_CTRL_STATE_FORWARD; - mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL, val); -} - -void mvsw_phy_enable(struct mvsw_softc *sc, int node) { uint16_t val; @@ -357,4 +1114,395 @@ mvsw_serdes_enable(struct mvsw_softc *sc val |= BMCR_AUTOEN; mvsw_serdes_write(sc, MVSW_SERDES(port), MDIO_MMD_PHYXS, MVSW_SERDES_BMCR, val); +} + +struct eport_softc { + struct device sc_dev; + int sc_node; + int sc_port; + + struct arpcom sc_ac; +#define sc_if sc_ac.ac_if + + struct mii_bus *sc_mdio; + struct mii_data sc_mii; +#define sc_ifmedia sc_mii.mii_media + struct mvsw_softc *sc_parent; + + struct if_device sc_ifd; +}; + +static int eport_match(struct device *, void *, void *); +static void eport_attach(struct device *, struct device *, void *); + +const struct cfattach eport_ca = { + sizeof (struct eport_softc), eport_match, eport_attach +}; + +struct cfdriver eport_cd = { + NULL, "eport", DV_IFNET +}; + +static int eport_enqueue(struct ifnet *, struct mbuf *); +static void eport_start(struct ifqueue *); +static int eport_ioctl(struct ifnet *, u_long, caddr_t); + +static int eport_up(struct eport_softc *); +static int eport_down(struct eport_softc *); + +static int eport_miibus_readreg(struct device *, int, int); +static void eport_miibus_writereg(struct device *, int, int, int); +static void eport_miibus_statch(struct device *); + +static int eport_media_upd(struct ifnet *); +static void eport_media_sts(struct ifnet *, struct ifmediareq *); + +static uint16_t eport_smi_read(struct eport_softc *, int); +static void eport_smi_write(struct eport_softc *, int, uint16_t); + +static int +eport_match(struct device *parent, void *match, void *aux) +{ + return (1); +} + +static void +eport_attach(struct device *parent, struct device *self, void *aux) +{ + struct eport_softc *sc = (struct eport_softc *)self; + int node = *(int *)aux; + struct ifnet *ifp; + int phyph, phynode; + int port; + uint16_t r; + + sc->sc_node = node; + sc->sc_port = port = OF_getpropint(node, "reg", -1); + + ifp = &sc->sc_if; + (void)strlcpy(ifp->if_xname, DEVNAME(sc), sizeof(ifp->if_xname)); + ifp->if_softc = sc; + ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN; + ifp->if_ioctl = eport_ioctl; + ifp->if_enqueue = eport_enqueue; + ifp->if_qstart = eport_start; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; + ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; + + OF_getprop(node, "label", + ifp->if_description, sizeof(ifp->if_description)); + + if (OF_getprop(node, "local-mac-address", &sc->sc_ac.ac_enaddr, + sizeof(sc->sc_ac.ac_enaddr)) != sizeof(sc->sc_ac.ac_enaddr)) + ether_fakeaddr(ifp); + + printf(": address %s\n", ether_sprintf(sc->sc_ac.ac_enaddr)); + + phyph = OF_getpropint(node, "phy-handle", 0); + phynode = OF_getnodebyphandle(phyph); + if (phynode != 0) { +#ifdef notyet + int phyloc = OF_getpropint(phynode, "reg", 0); +#endif + struct mii_data *mii = &sc->sc_mii; + + mii->mii_ifp = ifp; + mii->mii_readreg = eport_miibus_readreg; + mii->mii_writereg = eport_miibus_writereg; + mii->mii_statchg = eport_miibus_statch; + + ifmedia_init(&sc->sc_ifmedia, 0, + eport_media_upd, eport_media_sts); + +#ifdef notyet + mii_attach(self, mii, 0xffffffff, phyloc, MII_OFFSET_ANY, 0); +#else + LIST_INIT(&mii->mii_phys); +#endif + if (LIST_FIRST(&mii->mii_phys) == NULL) { +#ifdef notyet + printf("%s: no PHY found!\n", DEVNAME(sc)); +#endif + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_MANUAL, + 0, NULL); + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_MANUAL); + } else + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO); + } + + if_counters_alloc(ifp); + if_attach(ifp); + ether_ifattach(ifp); + + sc->sc_ifd.if_node = sc->sc_node; + sc->sc_ifd.if_ifp = ifp; + if_register(&sc->sc_ifd); + + /* Configure port. */ + r = eport_smi_read(sc, MVSW_PORT_CTRL0); + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); + SET(r, MVSW_PORT_CTRL0_STATE_DISABLED); + CLR(r, MVSW_PORT_CTRL0_FRAME_MODE_MASK); + SET(r, MVSW_PORT_CTRL0_FRAME_MODE_NORMAL); + CLR(r, MVSW_PORT_CTRL0_EGRESS_MODE_MASK); + SET(r, MVSW_PORT_CTRL0_EGRESS_MODE_UNMODIFIED); + SET(r, MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST | + MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST); + eport_smi_write(sc, MVSW_PORT_CTRL0, r); + + r = eport_smi_read(sc, MVSW_PORT_CTRL2); + CLR(r, MVSW_PORT_CTRL2_MAP_DA); + CLR(r, MVSW_PORT_CTRL2_JUMBO_MODE_MASK); + SET(r, MVSW_PORT_CTRL2_JUMBO_MODE_10240); + CLR(r, MVSW_PORT_CTRL2_8021Q_MODE_MASK); + SET(r, MVSW_PORT_CTRL2_8021Q_MODE_DISABLED); + CLR(r, MVSW_PORT_CTRL2_DISCARD_TAGGED); + CLR(r, MVSW_PORT_CTRL2_DISCARD_UNTAGGED); + eport_smi_write(sc, MVSW_PORT_CTRL2, r); + + eport_smi_write(sc, MVSW_PORT_ASSOC_VECTOR, 0); + + eport_smi_write(sc, MVSW_PORT_ETH_TYPE, ETHERTYPE_MVSW_DEFAULT); +} + +static uint16_t +eport_smi_read(struct eport_softc *sc, int reg) +{ + return mvsw_smi_read((struct mvsw_softc *)sc->sc_dev.dv_parent, + MVSW_PORT(sc->sc_port), reg); +} + +static void +eport_smi_write(struct eport_softc *sc, int reg, uint16_t r) +{ + mvsw_smi_write((struct mvsw_softc *)sc->sc_dev.dv_parent, + MVSW_PORT(sc->sc_port), reg, r); +} + +static int +eport_miibus_readreg(struct device *dev, int phy, int reg) +{ + struct device *parent = dev->dv_parent; + struct mvsw_softc *sc = (struct mvsw_softc *)parent; + int v; + + v = mvsw_phy_read(sc, phy, reg); + + /* internal phy doesnt report a useful model number */ + if (reg == MII_PHYIDR2 && MII_MODEL(v) == 0) + v |= 0x0002 << 4; + + return (v); +} + +static void +eport_miibus_writereg(struct device *dev, int phy, int reg, int val) +{ + struct device *parent = dev->dv_parent; + struct mvsw_softc *sc = (struct mvsw_softc *)parent; + + return (mvsw_phy_write(sc, phy, reg, val)); +} + +static void +eport_miibus_statch(struct device *dev) +{ + printf("%s: %s[%u]\n", dev->dv_xname, __func__, __LINE__); +} + +static int +eport_media_upd(struct ifnet *ifp) +{ + struct eport_softc *sc = ifp->if_softc; + + if (LIST_FIRST(&sc->sc_mii.mii_phys)) + mii_mediachg(&sc->sc_mii); + + return (0); +} + +static void +eport_media_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct eport_softc *sc = ifp->if_softc; + + if (LIST_FIRST(&sc->sc_mii.mii_phys)) { + mii_pollstat(&sc->sc_mii); + ifmr->ifm_active = sc->sc_mii.mii_media_active; + ifmr->ifm_status = sc->sc_mii.mii_media_status; + } +} + +static int +eport_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct eport_softc *sc = ifp->if_softc; +#ifdef notyet + struct ifreq *ifr = (struct ifreq *)data; +#endif + int error = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + if (ISSET(ifp->if_flags, IFF_UP)) { + if (!ISSET(ifp->if_flags, IFF_RUNNING)) + error = eport_up(sc); + } else { + if (ISSET(ifp->if_flags, IFF_RUNNING)) + error = eport_down(sc); + } + break; + +#ifdef notyet + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd); + break; +#endif + + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); + break; + } + + if (error == ENETRESET) { + /* hardware doesnt need reprogramming */ + error = 0; + } + + return (error); +} + +static int +eport_up(struct eport_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + uint16_t r; + + r = eport_smi_read(sc, MVSW_PORT_CTRL0); + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); + SET(r, MVSW_PORT_CTRL0_STATE_FORWARD); + eport_smi_write(sc, MVSW_PORT_CTRL0, r); + + SET(ifp->if_flags, IFF_RUNNING); + + return (0); +} + +static int +eport_down(struct eport_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + uint16_t r; + + CLR(ifp->if_flags, IFF_RUNNING); + + r = eport_smi_read(sc, MVSW_PORT_CTRL0); + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); + SET(r, MVSW_PORT_CTRL0_STATE_DISABLED); + eport_smi_write(sc, MVSW_PORT_CTRL0, r); + + return (0); +} + +static unsigned int +eport_transmit(struct eport_softc *sc, struct mvsw_softc *ssc, + struct mvsw_port *p, struct mbuf *m) +{ + struct ether_header *eh; + struct mvsw_etag *etag; + const int hlen = sizeof(*eh) + sizeof(*etag); + const int offs = offsetof(struct ether_header, ether_type); + const int diff = hlen - offs; + +#if NBPFILTER > 0 + caddr_t if_bpf = sc->sc_if.if_bpf; + if (if_bpf) + bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT); +#endif + + m = m_prepend(m, diff, M_NOWAIT); + if (m == NULL) + return (ENOBUFS); + + if (m->m_len < hlen) { + m = m_pullup(m, hlen); + if (m == NULL) + return (ENOBUFS); + } + + memmove(mtod(m, caddr_t), mtod(m, caddr_t) + diff, offs); + eh = mtod(m, struct ether_header *); + eh->ether_type = htons(ETHERTYPE_MVSW_ETAG); + + etag = (struct mvsw_etag *)(eh + 1); + etag->reserved = htons(0); + etag->tag0 = htons(MVSW_TAG_MODE_FROM_CPU | + (ssc->sc_dev.dv_unit << MVSW_TAG_SWITCH_SHIFT) | + (sc->sc_port << MVSW_TAG_PORT_SHIFT)); + etag->tag1 = htons(0); + + return (if_enqueue(p->p_ifp0, m)); +} + +static int +eport_enqueue(struct ifnet *ifp, struct mbuf *m) +{ + struct eport_softc *sc = ifp->if_softc; + struct ifqueue *ifq = &ifp->if_snd; + struct mvsw_softc *ssc = (struct mvsw_softc *)sc->sc_dev.dv_parent; + struct mvsw_port *p = TAILQ_FIRST(&ssc->sc_cpus); + int error; + + if (p == NULL || !ISSET(p->p_ifp0->if_flags, IFF_RUNNING)) { + m_freem(m); + return (ENETDOWN); + } + + if (!ifq_is_priq(ifq)) + return (if_enqueue_ifq(ifp, m)); + + counters_pkt(ifp->if_counters, + ifc_opackets, ifc_obytes, m->m_pkthdr.len); + + error = eport_transmit(sc, ssc, p, m); + if (error != 0) { + counters_inc(ifp->if_counters, ifc_oerrors); + return (error); + } + + return (0); +} + +static void +eport_start(struct ifqueue *ifq) +{ + struct ifnet *ifp = ifq->ifq_if; + struct eport_softc *sc = ifp->if_softc; + struct mbuf *m; + unsigned int errors = 0; + + struct mvsw_softc *ssc = (struct mvsw_softc *)sc->sc_dev.dv_parent; + struct mvsw_port *p = TAILQ_FIRST(&ssc->sc_cpus); + + if (p == NULL || !ISSET(p->p_ifp0->if_flags, IFF_RUNNING)) { + ifq_purge(ifq); + return; + } + + while ((m = ifq_dequeue(ifq)) != NULL) { + if (eport_transmit(sc, ssc, p, m) != 0) + errors++; + } + + if (errors) + counters_add(ifp->if_counters, ifc_oerrors, errors); +} + +static void +eport_input(struct eport_softc *sc, struct mbuf *m) +{ + struct ifnet *ifp = &sc->sc_if; + + if_vinput(ifp, m); } Index: dev/ofw/ofw_misc.c =================================================================== RCS file: /cvs/src/sys/dev/ofw/ofw_misc.c,v retrieving revision 1.36 diff -u -p -r1.36 ofw_misc.c --- dev/ofw/ofw_misc.c 25 Mar 2022 15:49:29 -0000 1.36 +++ dev/ofw/ofw_misc.c 2 Jun 2022 03:33:24 -0000 @@ -119,6 +119,46 @@ regmap_read_4(struct regmap *rm, bus_siz return bus_space_read_4(rm->rm_tag, rm->rm_handle, offset); } +/* + * Network interface support. + */ + +LIST_HEAD(, if_device) if_devices = + LIST_HEAD_INITIALIZER(if_devices); + +void +if_register(struct if_device *ifd) +{ + ifd->if_phandle = OF_getpropint(ifd->if_node, "phandle", 0); + + LIST_INSERT_HEAD(&if_devices, ifd, if_list); +} + +struct ifnet * +if_bynode(int node) +{ + struct if_device *ifd; + + LIST_FOREACH(ifd, &if_devices, if_list) { + if (ifd->if_node == node) + return (ifd->if_ifp); + } + + return (NULL); +} + +struct ifnet * +if_byphandle(uint32_t phandle) +{ + struct if_device *ifd; + + LIST_FOREACH(ifd, &if_devices, if_list) { + if (ifd->if_phandle == phandle) + return (ifd->if_ifp); + } + + return (NULL); +} /* * PHY support. Index: dev/ofw/ofw_misc.h =================================================================== RCS file: /cvs/src/sys/dev/ofw/ofw_misc.h,v retrieving revision 1.24 diff -u -p -r1.24 ofw_misc.h --- dev/ofw/ofw_misc.h 21 Mar 2022 19:22:40 -0000 1.24 +++ dev/ofw/ofw_misc.h 2 Jun 2022 03:33:24 -0000 @@ -30,6 +30,23 @@ struct regmap *regmap_byphandle(uint32_t uint32_t regmap_read_4(struct regmap *, bus_size_t); void regmap_write_4(struct regmap *, bus_size_t, uint32_t); +/* Interface support */ + +struct ifnet; + +struct if_device { + int if_node; + struct ifnet *if_ifp; + + LIST_ENTRY(if_device) if_list; + uint32_t if_phandle; +}; + +void if_register(struct if_device *); + +struct ifnet *if_bynode(int); +struct ifnet *if_byphandle(uint32_t); + /* PHY support */ #define PHY_NONE 0 Index: net/ethertypes.h =================================================================== RCS file: /cvs/src/sys/net/ethertypes.h,v retrieving revision 1.16 diff -u -p -r1.16 ethertypes.h --- net/ethertypes.h 5 Jan 2022 05:19:22 -0000 1.16 +++ net/ethertypes.h 2 Jun 2022 03:33:27 -0000 @@ -303,6 +303,8 @@ #define ETHERTYPE_AOE 0x88A2 /* ATA over Ethernet */ #define ETHERTYPE_QINQ 0x88A8 /* 802.1ad VLAN stacking */ #define ETHERTYPE_LLDP 0x88CC /* Link Layer Discovery Protocol */ +#define ETHERTYPE_802_EX1 0x88B5 /* IEEE Std 802 - Local Experimental */ +#define ETHERTYPE_802_EX2 0x88B6 /* IEEE Std 802 - Local Experimental */ #define ETHERTYPE_MACSEC 0x88e5 /* 802.1AE MACsec */ #define ETHERTYPE_PBB 0x88e7 /* 802.1Q Provider Backbone Bridging */ #define ETHERTYPE_NSH 0x984F /* Network Service Header (RFC8300) */