> Date: Sat, 28 May 2022 15:08:49 +1000
> From: David Gwynne <da...@gwynne.id.au>
> 
> 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.

So I have a Turris MOX that is based on the same SoC.  It is modular
and I have a switch module that integrates the same Marvell switch
chip.  That is the hardware I used to develop the current mvsw(4)
driver.  The interesting thing is that the Turris MOX actually has two
mvneta(4) interface; mvneta0 is connected to a normal Ethernet PHY,
whereas mvneta1 is connected to the switch chip.  In this setup it
makes sense to use the mvneta0 port as a "WAN" port and the swithc
ports as "LAN" ports and have let the hardware do the switching
between the "LAN" ports.

> 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.

How does configuring IP addresses work with your diff?  Do you still
configure and address on the mvneta(4) interface?  Or do you configure
addresses on the individual mvsport(4) interfaces?  And how does one
configure additional VLANs in this scenario?

> 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).

So this would "break" my current setup on the Turris MOX.  Now
actually I currently only use a single port on the switch, but my plan
was to connect some of my equipment directly to the MOX instead of my
24-port switch.  I just didn't get around to doing that.

> 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.

That *is* nice.

> 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.

I would like to understand things a bit better.  And make sure this
doesn't break things to badly for me.  That Turris MOX is my firewall
and wireless access point, so breaking things would be bad for OpenBSD
development ;).  Unfortunately, it also means it isn't trivial for me
to test things.

> 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.

The mvmdio(4) driver already uses a mutex to control access to the bus.

> 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.

The "Banana Pi BPI-R2 Pro Router" might be the most interesting one.
That one is based on a Rockchip RK3568 SoC for which we already have
some support in the tree and hardware in the hands of other developers
(me and patrick).  That one integrates a MediaTek MT7531BE switch chip.

The other Banana Pi router boards are based on SoCs that we don't
support.

> 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
>     d...@ebin.lan.animata.net:/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