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) */

Reply via email to