here's an updated diff. the main changes are:
1. disable the phy code. letting eephy attach seems to break autonegotiation. it also looks like several generations of the marvell switches don't supply a proper model id for the builtin phy, so the linux driver fakes one and has extra code in their marvell phy driver to cope with it. the main justification for this seems to be so they can get to a temperature sensor, which i'm struggling to care about. however, breaking autoneg is annoying, so ive disabled the phy code for now. 2. rename mvsport to eport. if veb/vlan/etc are going to handle hardware switches, i think it would be easier to have them recognise one eport driver than many switch specific port drivers. on the other hand, i could use a bit in struct ifnet if_capabilities to identify a port driver. it's so hard to tell. im on the fence on this one. 3. provide optimised if_enqueue for port drivers mvsport^Weport can queue packets striaght onto the hardware driver now, skipping a trip through the ifq machinery when transmitting. that seemed to cut a lot of variability in some benchmark results, and is a little be faster overall. 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 <sys/param.h> #include <sys/systm.h> #include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/sockio.h> #include <sys/timeout.h> +#include <sys/percpu.h> #include <machine/bus.h> #include <machine/fdt.h> @@ -27,7 +36,31 @@ #include <dev/ofw/ofw_misc.h> #include <dev/ofw/fdt.h> +#include <net/if.h> +#include <net/if_media.h> +#include <net/if_types.h> + #include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> + +#include <netinet6/in6_var.h> +#include <netinet/ip6.h> + +#include <crypto/siphash.h> /* if_trunk.h uses siphash bits */ +#include <net/if_trunk.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#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 <dev/mii/mdio.h> */ #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) */