ive been hunting for a small box, ie, it has a case, with at least
two (non usb) gig interfaces for use/recommend as a cheap and
cheerful home router, and it feels like trying to find a unicorn
sometimes.

i was looking at ipq4018/ipq4019 devs for a while, but ive been
turned off those systems because i'd have to get a boot loader
working, and then write a ton of drivers to support the platform
before i can even move a packet.

the espressobin is the least worst thing ive settled on. it's not
too expensive, it has a case, it has multiple interfaces, and
kettenis and patrick have already worked on the platform stuff. the
only problem with it was that the interfaces are on a switch chip
supported by mvsw, and mvsw configures the switch as a switch. this
makes it hard to use as a router.

this diff makes mvsw present its ports as individual interfaces,
and configures the switch so each external port can only talk to
the host system. it's basically an ethernet mux instead of a switch.

this is inspired by the dsa framework in linux, which in turn seems
to have been inspired by and written for the link street family of
switch chips thats in the espressobin.

like dsa, mvsw now tries to understand the different roles of switch
ports and their topology. most ports are externally accessible, and one
is wired up internally to mvneta. the fdt describes these roles and
relationships, and now mvsw does different things depending on these
roles.

the internal port wired up to mvneta is a "cpu" interface. mvsw now
takes over mvneta, much like how aggr or trunk takes over an interface,
and unconditionally configures the switch port to tag packets so the
kernel can know which mvsw port a packet was received on.

the externally accessible ports are enumerated and attached as separate
network interfaces in the kernel, and configured on the switch so that
they can only talk to the cpu/mvneta switch port. packets sent out
a kernel mvsport interface are tagged and sent out mvneta instead,
kind of like vlan interfaces.

there is no longer any config that allows mvsw switch ports to
communicate directly, if you want to bridge them now you have to
do it in software with veb (or bridge or tpmr).

presenting the ports as separate interfaces allows them to fit in
nicely with the existing kernel functionality. in particular, it
lets us wire up ifmedia and mii so you can see the phy on each port,
which in turn will let ifconfig operate on them as you'd expect.

the diff below is very rough, but i think it's far enough along
that it demonstrates where im going. if the ideas are acceptable,
i'd like to commit it and hack on it in the tree.

there's some future work to be done. the mdio bus probably needs a lock
around it. i have no idea how mii/ifmedia works, so some pointers
there would be good. it would be nice to hack on another switch
chip at some point. recent banana pi routers might be a good candidate
for that. they have mediatek or realtek switches on them from what
i can tell, and they link to the doco for them. it might be possible
to factor the "port interface" code out for all these and just have
the switch drivers provide glue for them.

it would be interesting to teach veb and vlan how to offload switching
to a switch chip for port interfaces. there's a bunch of functionality
we offer in our virtual bridges that wouldn't work on a hardware
chip (eg, pf, bridge rules, ipsec, etc), but veb already special
cases vport interfaces so special casing these ports could work
too.

however, having routed interfaces is more useful to me than bridging
right now.

here's an espressobin dmesg.

OpenBSD 7.1-current (comp) #56: Fri May 27 17:49:35 AEST 2022
    [email protected]:/home/dlg/comp
real mem  = 1048031232 (999MB)
avail mem = 983277568 (937MB)
random: good seed from bootblocks
mainbus0 at root: Globalscale Marvell ESPRESSOBin Board V7
psci0 at mainbus0: PSCI 1.1, SMCCC 1.2
cpu0 at mainbus0 mpidr 0: ARM Cortex-A53 r0p4
cpu0: 32KB 64b/line 2-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache
cpu0: 256KB 64b/line 16-way L2 cache
cpu0: CRC32,SHA2,SHA1,AES+PMULL,ASID16
cpu1 at mainbus0 mpidr 1: ARM Cortex-A53 r0p4
cpu1: 32KB 64b/line 2-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache
cpu1: 256KB 64b/line 16-way L2 cache
cpu1: CRC32,SHA2,SHA1,AES+PMULL,ASID16
efi0 at mainbus0: UEFI 2.9
efi0: Das U-Boot rev 0x20220400
apm0 at mainbus0
agtimer0 at mainbus0: 12500 kHz
"pmu" at mainbus0 not configured
simplebus0 at mainbus0: "soc"
simplebus1 at simplebus0: "internal-regs"
syscon0 at simplebus1: "system-controller"
syscon1 at simplebus1: "avs"
mvclock0 at simplebus1
mvclock1 at simplebus1
mvclock2 at simplebus1
mvpinctrl0 at simplebus1
syscon2 at simplebus1: "syscon"
mvpinctrl1 at simplebus1
syscon3 at simplebus1: "system-controller"
syscon4 at simplebus1: "system-controller"
agintc0 at simplebus1 shift 4:3 nirq 224 nredist 2 ipi: 0, 1: 
"interrupt-controller"
mvdog0 at simplebus1
mvspi0 at simplebus1
mvuart0 at simplebus1
"phy" at simplebus1 not configured
mvneta0 at simplebus1
mvneta0: Ethernet address f0:ad:4e:1c:08:42
mvmdio0 at simplebus1: "mdio"
mvsw0 at mvmdio0 phy 1: 88E6341 rev 0
xhci0 at simplebus1, xHCI 1.0
usb0 at xhci0: USB revision 3.0
uhub0 at usb0 configuration 1 interface 0 "Generic xHCI root hub" rev 3.00/1.00 
addr 1
"phy" at simplebus1 not configured
ehci0 at simplebus1
usb1 at ehci0: USB revision 2.0
uhub1 at usb1 configuration 1 interface 0 "Generic EHCI root hub" rev 2.00/1.00 
addr 1
"phy" at simplebus1 not configured
"xor" at simplebus1 not configured
"crypto" at simplebus1 not configured
"mailbox" at simplebus1 not configured
sdhc0 at simplebus1
sdhc0: SDHC 3.0, 400 MHz base clock
sdmmc0 at sdhc0: 4-bit, sd high-speed, mmc high-speed, ddr52, dma
ahci0 at simplebus1: AHCI 1.3
scsibus0 at ahci0: 32 targets
mvkpcie0 at simplebus0
mvkpcie0: timeout
"firmware" at mainbus0 not configured
"regulator" at mainbus0 not configured
gpioleds0 at mainbus0: "led2"
mvsw0: mvneta0 at port 0
mvsport0 at mvsw0 port 1: address 00:51:82:11:22:03
eephy0 at mvsport0 phy 17: 88E1000 1 Gigabit PHY, rev. 1
mvsport1 at mvsw0 port 2: address 00:51:82:11:22:02
eephy1 at mvsport1 phy 18: 88E1000 1 Gigabit PHY, rev. 1
mvsport2 at mvsw0 port 3: address 00:51:82:11:22:01
eephy2 at mvsport2 phy 19: 88E1000 1 Gigabit PHY, rev. 1
scsibus1 at sdmmc0: 2 targets, initiator 0
sd0 at scsibus1 targ 1 lun 0: <SD/MMC, SH32G, 0080> removable
sd0: 30436MB, 512 bytes/sector, 62333952 sectors
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets
root on sd0a (d1ff8372fcd69512.a) swap on sd0b dump on sd0b

here's the interesting bits of the fdt:

            Node 0x16a0
                name: 'ethernet'
                local-mac-address: f0ad4e1c.0842
                compatible: 'marvell,armada-3700-neta'
                reg: 00030000.00004000
                interrupts: 00000000.0000002a.00000004
                clocks: 00000009.00000008
                status: 'okay'
                pinctrl-names: 'default'
                pinctrl-0: 0000000a.0000000b
                phy-mode: 'rgmii-id'
                phandle: 0000000c

                Node 0x1794
                    name: 'fixed-link'
                    speed: 000003e8
                    full-duplex: 

            Node 0x17c8
                name: 'mdio'
                #address-cells: 00000001
                #size-cells: 00000000
                compatible: 'marvell,orion-mdio'
                reg: 00032004.00000004

                Node 0x182c
                    name: 'switch0'
                    compatible: 'marvell,mv88e6085'
                    #address-cells: 00000001
                    #size-cells: 00000000
                    reg: 00000001
                    dsa,member: 00000000.00000000

                    Node 0x18a0
                        name: 'ports'
                        #address-cells: 00000001
                        #size-cells: 00000000

                        Node 0x18cc
                            name: 'port'
                            reg: 00000000
                            label: 'cpu'
                            ethernet: 0000000c
                            phy-mode: 'rgmii-id'

                            Node 0x1920
                                name: 'fixed-link'
                                speed: 000003e8
                                full-duplex: 

                        Node 0x1954
                            name: 'port'
                            local-mac-address: 00518211.2203
                            reg: 00000001
                            label: 'lan1'
                            phy-handle: 0000000d

                        Node 0x19ac
                            name: 'port'
                            local-mac-address: 00518211.2202
                            reg: 00000002
                            label: 'lan0'
                            phy-handle: 0000000e

                        Node 0x1a04
                            name: 'port'
                            local-mac-address: 00518211.2201
                            reg: 00000003
                            label: 'wan'
                            phy-handle: 0000000f

                    Node 0x1a5c
                        name: 'mdio'
                        #address-cells: 00000001
                        #size-cells: 00000000

                        Node 0x1a88
                            name: 'switch0phy0'
                            reg: 00000011
                            phandle: 0000000d

                        Node 0x1ac0
                            name: 'switch0phy1'
                            reg: 00000012
                            phandle: 0000000e

                        Node 0x1af8
                            name: 'switch0phy2'
                            reg: 00000013
                            phandle: 0000000f

and here's the diff:

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     27 May 2022 23:55:17 -0000
@@ -259,6 +259,7 @@ mvrtc*              at fdt?
 mvspi*         at fdt?
 moxtet*                at spi?
 mvsw*          at fdt?
+mvsport*       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   27 May 2022 23:55:19 -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 mvsport: ether, ifnet, mii, ifmedia
+attach mvsport at mvsw
+file   dev/fdt/mvsw.c                  mvsw | mvsport
 
 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.17
diff -u -p -r1.17 if_mvneta.c
--- dev/fdt/if_mvneta.c 24 Oct 2021 17:52:26 -0000      1.17
+++ dev/fdt/if_mvneta.c 27 May 2022 23:55:19 -0000
@@ -170,6 +170,8 @@ struct mvneta_softc {
        int                      sc_link;
        int                      sc_sfp;
        int                      sc_node;
+
+       struct if_device         sc_ifd;
 };
 
 
@@ -781,6 +783,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);
 }
 
 void
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      27 May 2022 23:55:19 -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,303 @@
 #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 mvsport_softc;
+
 struct mvsw_softc {
-       struct device   sc_dev;
+       struct device            sc_dev;
+
+       int                      sc_node;
+       struct mii_bus          *sc_mdio;
+       int                      sc_reg;
 
-       struct mii_bus  *sc_mdio;
-       int             sc_reg;
+       unsigned int             sc_nports;
+       struct mvsport_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 +390,26 @@ 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 *);
+
+static struct mbuf *
+               mvsport_input(struct mvsport_softc *, struct mbuf *);
+
 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 +417,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 +452,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,24 +473,136 @@ 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++) {
+//             mvsw_smi_write(sc, MVSW_G2, MVSW_G2_TRUNK_MASK,
+//                 MVSW_G2_TRUNK_MASK_UPDATE |
+//                 (i << MVSW_G2_TRUNK_MASK_SHIFT) | /* clear bitmap */ 0);
+       }
+       for (i = 0; i < MVSW_G2_TRUNK_MAPPING_ID_COUNT; i++) {
+//             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);
+       }
+
+       ports = OF_getnodebyname(sc->sc_node, "ports");
+       if (ports == 0)
+               goto done;
+
        for (port = OF_child(ports); port; port = OF_peer(port)) {
                phy = OF_getpropint(port, "phy-handle", 0);
                node = OF_getnodebyphandle(phy);
@@ -156,8 +611,274 @@ 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 mvsport_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 mvsport_softc *psc;
+
+       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;
+
+       psc = sc->sc_ports[port];
+       if (psc == NULL)
+               goto drop;
+
+       memmove(mtod(m, caddr_t) + diff, mtod(m, caddr_t),
+           offsetof(struct ether_header, ether_type));
+       m_adj(m, diff);
+
+       m = mvsport_input(psc, m);
+       if (m == NULL)
+               return;
+
+       ether_input(ifp0, 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 +940,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 +1076,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 +1108,352 @@ 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 mvsport_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     mvsport_match(struct device *, void *, void *);
+static void    mvsport_attach(struct device *, struct device *, void *);
+
+const struct cfattach mvsport_ca = {
+       sizeof (struct mvsport_softc), mvsport_match, mvsport_attach
+};
+
+struct cfdriver mvsport_cd = {
+       NULL, "mvsport", DV_DULL
+};
+
+static void    mvsport_start(struct ifqueue *);
+static int     mvsport_ioctl(struct ifnet *, u_long, caddr_t);
+
+static int     mvsport_up(struct mvsport_softc *);
+static int     mvsport_down(struct mvsport_softc *);
+
+static int     mvsport_miibus_readreg(struct device *, int, int);
+static void    mvsport_miibus_writereg(struct device *, int, int, int);
+static void    mvsport_miibus_statch(struct device *);
+
+static int     mvsport_media_upd(struct ifnet *);
+static void    mvsport_media_sts(struct ifnet *, struct ifmediareq *);
+
+static uint16_t        mvsport_smi_read(struct mvsport_softc *, int);
+static void    mvsport_smi_write(struct mvsport_softc *, int, uint16_t);
+
+static int
+mvsport_match(struct device *parent, void *match, void *aux)
+{
+       int node = *(int *)aux;
+       char buf[32];
+
+       if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 &&
+           strcmp(buf, "disabled") == 0)
+               return (0);
+
+       return (1);
+}
+
+static void
+mvsport_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct mvsport_softc *sc = (struct mvsport_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 = mvsport_ioctl;
+       ifp->if_qstart = mvsport_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) {
+               int phyloc = OF_getpropint(phynode, "reg", 0);
+               struct mii_data *mii = &sc->sc_mii;
+
+               mii->mii_ifp = ifp;
+               mii->mii_readreg = mvsport_miibus_readreg;
+               mii->mii_writereg = mvsport_miibus_writereg;
+               mii->mii_statchg = mvsport_miibus_statch;
+
+               ifmedia_init(&sc->sc_ifmedia, 0,
+                   mvsport_media_upd, mvsport_media_sts);
+
+               mii_attach(self, mii, 0xffffffff, phyloc, MII_OFFSET_ANY, 0);
+               if (LIST_FIRST(&mii->mii_phys) == NULL) {
+                       printf("%s: no PHY found!\n", DEVNAME(sc));
+                       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 = mvsport_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);
+       mvsport_smi_write(sc, MVSW_PORT_CTRL0, r);
+
+       r = mvsport_smi_read(sc, MVSW_PORT_CTRL2);
+       //SET(r, MVSW_PORT_CTRL2_MAP_DA);
+       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);
+       mvsport_smi_write(sc, MVSW_PORT_CTRL2, r);
+
+       mvsport_smi_write(sc, MVSW_PORT_ASSOC_VECTOR, 0);
+
+       mvsport_smi_write(sc, MVSW_PORT_ETH_TYPE, ETHERTYPE_MVSW_DEFAULT);
+}
+
+static uint16_t
+mvsport_smi_read(struct mvsport_softc *sc, int reg)
+{
+       return mvsw_smi_read((struct mvsw_softc *)sc->sc_dev.dv_parent,
+           MVSW_PORT(sc->sc_port), reg);
+}
+
+static void
+mvsport_smi_write(struct mvsport_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
+mvsport_miibus_readreg(struct device *dev, int phy, int reg)
+{
+       struct device *parent = dev->dv_parent;
+       struct mvsw_softc *sc = (struct mvsw_softc *)parent;
+
+       return (mvsw_phy_read(sc, phy, reg));
+}
+
+static void
+mvsport_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
+mvsport_miibus_statch(struct device *dev)
+{
+       printf("%s: %s[%u]\n", dev->dv_xname, __func__, __LINE__);
+}
+
+static int
+mvsport_media_upd(struct ifnet *ifp)
+{
+       struct mvsport_softc *sc = ifp->if_softc;
+
+       if (LIST_FIRST(&sc->sc_mii.mii_phys))
+               mii_mediachg(&sc->sc_mii);
+
+       return (0);
+}
+
+static void
+mvsport_media_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+       struct mvsport_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
+mvsport_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+       struct mvsport_softc *sc = ifp->if_softc;
+       struct ifreq *ifr = (struct ifreq *)data;
+       int error = 0;
+
+       switch (cmd) {
+       case SIOCSIFFLAGS:
+               if (ISSET(ifp->if_flags, IFF_UP)) {
+                       if (!ISSET(ifp->if_flags, IFF_RUNNING))
+                               error = mvsport_up(sc);
+               } else {
+                       if (ISSET(ifp->if_flags, IFF_RUNNING))
+                               error = mvsport_down(sc);
+               }
+               break;
+
+       case SIOCSIFMEDIA:
+       case SIOCGIFMEDIA:
+               error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, cmd);
+               break;
+
+       default:
+               error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
+               break;
+       }
+
+       if (error == ENETRESET) {
+               /* hardware doesnt need reprogramming */
+               error = 0;
+       }
+
+       return (error);
+}
+
+static int
+mvsport_up(struct mvsport_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_if;
+       uint16_t r;
+
+       r = mvsport_smi_read(sc, MVSW_PORT_CTRL0);
+       CLR(r, MVSW_PORT_CTRL0_STATE_MASK);
+       SET(r, MVSW_PORT_CTRL0_STATE_FORWARD);
+       mvsport_smi_write(sc, MVSW_PORT_CTRL0, r);
+
+       SET(ifp->if_flags, IFF_RUNNING);
+
+       return (0);
+}
+
+static int
+mvsport_down(struct mvsport_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_if;
+       uint16_t r;
+
+       CLR(ifp->if_flags, IFF_RUNNING);
+
+       r = mvsport_smi_read(sc, MVSW_PORT_CTRL0);
+       CLR(r, MVSW_PORT_CTRL0_STATE_MASK);
+       SET(r, MVSW_PORT_CTRL0_STATE_DISABLED);
+       mvsport_smi_write(sc, MVSW_PORT_CTRL0, r);
+
+       return (0);
+}
+
+static void
+mvsport_start(struct ifqueue *ifq)
+{
+       struct ifnet *ifp = ifq->ifq_if;
+       struct mvsport_softc *sc = ifp->if_softc;
+       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;
+       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) {
+               ifq_purge(ifq);
+               return;
+       }
+
+       while ((m = ifq_dequeue(ifq)) != NULL) {
+#if NBPFILTER > 0
+               {
+                       caddr_t if_bpf = ifp->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) {
+                       errors++;
+                       continue;
+               }
+               if (m->m_len < hlen) {
+                       m = m_pullup(m, hlen);
+                       if (m == NULL) {
+                               errors++;
+                               continue;
+                       }
+               }
+
+               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);
+
+               if (if_enqueue(p->p_ifp0, m) != 0)
+                       errors++;
+       }
+
+       if (errors)
+               counters_add(ifp->if_counters, ifc_oerrors, errors);
+}
+
+static struct mbuf *
+mvsport_input(struct mvsport_softc *sc, struct mbuf *m)
+{
+       struct ifnet *ifp = &sc->sc_if;
+
+       if_vinput(ifp, m);
+
+       return (NULL);
 }
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  27 May 2022 23:55:19 -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  27 May 2022 23:55:19 -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    27 May 2022 23:55:22 -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