From: David Gwynne Subject: Re: Support RTL8367B-CG ethernet switch To: Georg Bege Cc: tech@openbsd.org Date: Mon, 22 Jul 2024 17:45:31 +1000 On Wed, Jul 17, 2024 at 05:24:42PM +0200, Georg Bege wrote: > Hello OpenBSD folks, > > Im interested in the RTL8367B-CG switch ethernet port. > This one is for example found on an Banana Pi R2 pro, however it seems that OpenBSD has no support for this driver (yet)? > > Anyone knows something about this? you're right, openbsd does not have support for that chip. the only switch it has any support for is a narrow set of the marvell switch chips as found on things like the espressobin, and that support is limited to using it as a dumb ethernet switch. i have a bpi r2 pro, but it's a revision using a mediatek switch chip. i got most of the way through a driver for it (and rejigged the marvell switch driver) so you can use the switch ports as separate ethernet interfaces in openbsd. it's mostly working, but i ran out of time to push it further and no one else seemed interested in it. banana pi linked to doco for the realtek chip iirc. if you have a board with the realtek chip you could probably hack the mvsw and mtsw code up and have it work them same. the bpi r2 pro with the mediatek switch looks like this. the diff is below. OpenBSD 7.3-current (GENERIC.MP) #395: Mon Apr 24 11:50:25 AEST 2023 dlg@o1000.eait.uq.edu.au:/home/dlg/src/sys/arch/arm64/compile/GENERIC.MP real mem = 2143793152 (2044MB) avail mem = 2041298944 (1946MB) random: good seed from bootblocks mainbus0 at root: Banana Pi BPI-R2 Pro (RK3568) psci0 at mainbus0: PSCI 1.1, SMCCC 1.2, SYSTEM_SUSPEND cpu0 at mainbus0 mpidr 0: ARM Cortex-A55 r2p0 cpu0: 32KB 64b/line 4-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache cpu0: 512KB 64b/line 16-way L2 cache cpu0: DP,RDM,Atomic,CRC32,SHA2,SHA1,AES+PMULL,LRCPC,DPB,ASID16,PAN+ATS1E1,LO,HPDS,HAFDBS,SBSS cpu1 at mainbus0 mpidr 100: ARM Cortex-A55 r2p0 cpu1: 32KB 64b/line 4-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache cpu1: 512KB 64b/line 16-way L2 cache cpu1: DP,RDM,Atomic,CRC32,SHA2,SHA1,AES+PMULL,LRCPC,DPB,ASID16,PAN+ATS1E1,LO,HPDS,HAFDBS,SBSS cpu2 at mainbus0 mpidr 200: ARM Cortex-A55 r2p0 cpu2: 32KB 64b/line 4-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache cpu2: 512KB 64b/line 16-way L2 cache cpu2: DP,RDM,Atomic,CRC32,SHA2,SHA1,AES+PMULL,LRCPC,DPB,ASID16,PAN+ATS1E1,LO,HPDS,HAFDBS,SBSS cpu3 at mainbus0 mpidr 300: ARM Cortex-A55 r2p0 cpu3: 32KB 64b/line 4-way L1 VIPT I-cache, 32KB 64b/line 4-way L1 D-cache cpu3: 512KB 64b/line 16-way L2 cache cpu3: DP,RDM,Atomic,CRC32,SHA2,SHA1,AES+PMULL,LRCPC,DPB,ASID16,PAN+ATS1E1,LO,HPDS,HAFDBS,SBSS efi0 at mainbus0: UEFI 2.10 efi0: Das U-Boot rev 0x20230400 smbios0 at efi0: SMBIOS 3.0 smbios0: vendor U-Boot version "2023.04-rc5-00057-g455f346de6-dirty" date 04/01/2023 smbios0: Banana Pi BPI-R2 Pro scmi0 at mainbus0: SCMI 2.0 apm0 at mainbus0 agintc0 at mainbus0 mbi shift 4:4 nirq 352 nredist 4 ipi: 0, 1, 2: "interrupt-controller" syscon0 at mainbus0: "syscon" rkiovd0 at syscon0 syscon1 at mainbus0: "syscon" syscon2 at mainbus0: "syscon" syscon3 at mainbus0: "syscon" syscon4 at mainbus0: "syscon" syscon5 at mainbus0: "syscon" syscon6 at mainbus0: "syscon" rkclock0 at mainbus0: PMUCRU rkclock1 at mainbus0: CRU syscon7 at mainbus0: "power-management" rkpmu0 at syscon7: pwr 007f status 007f req 0000 idle 01ee ack 01ee syscon8 at mainbus0: "qos" syscon9 at mainbus0: "qos" syscon10 at mainbus0: "qos" syscon11 at mainbus0: "qos" syscon12 at mainbus0: "qos" syscon13 at mainbus0: "qos" syscon14 at mainbus0: "qos" syscon15 at mainbus0: "qos" syscon16 at mainbus0: "qos" syscon17 at mainbus0: "qos" syscon18 at mainbus0: "qos" syscon19 at mainbus0: "qos" syscon20 at mainbus0: "qos" syscon21 at mainbus0: "qos" syscon22 at mainbus0: "qos" syscon23 at mainbus0: "qos" syscon24 at mainbus0: "qos" syscon25 at mainbus0: "qos" syscon26 at mainbus0: "qos" syscon27 at mainbus0: "qos" syscon28 at mainbus0: "qos" syscon29 at mainbus0: "qos" syscon30 at mainbus0: "qos" syscon31 at mainbus0: "qos" rkcomphy0 at mainbus0 rkcomphy1 at mainbus0 rkusbphy0 at mainbus0: phy 0 rkusbphy1 at mainbus0: phy 1 rkpinctrl0 at mainbus0: "pinctrl" rkgpio0 at rkpinctrl0 rkgpio1 at rkpinctrl0 rkgpio2 at rkpinctrl0 rkgpio3 at rkpinctrl0 rkgpio4 at rkpinctrl0 syscon32 at mainbus0: "syscon" syscon33 at mainbus0: "qos" syscon34 at mainbus0: "qos" syscon35 at mainbus0: "qos" syscon36 at mainbus0: "syscon" rkpciephy0 at mainbus0 rkcomphy2 at mainbus0 "fit-images" at mainbus0 not configured "opp-table-0" at mainbus0 not configured rkdrm0 at mainbus0 drm0 at rkdrm0 "firmware" at mainbus0 not configured "opp-table-1" at mainbus0 not configured simpleaudio0 at mainbus0 "pmu" at mainbus0 not configured agtimer0 at mainbus0: 24000 kHz "xin24m" at mainbus0 not configured "xin32k" at mainbus0 not configured ahci0 at mainbus0: AHCI 1.3 ahci0: port 0: 6.0Gb/s scsibus0 at ahci0: 32 targets sd0 at scsibus0 targ 0 lun 0: naa.5001b444a777478f sd0: 1907729MB, 512 bytes/sector, 3907029168 sectors, thin xhci0 at mainbus0, xHCI 1.10 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 xhci1 at mainbus0, xHCI 1.10 usb1 at xhci1: USB revision 3.0 uhub1 at usb1 configuration 1 interface 0 "Generic xHCI root hub" rev 3.00/1.00 addr 1 ehci0 at mainbus0 usb2 at ehci0: USB revision 2.0 uhub2 at usb2 configuration 1 interface 0 "Generic EHCI root hub" rev 2.00/1.00 addr 1 ohci0 at mainbus0: version 1.0 ehci1 at mainbus0 usb3 at ehci1: USB revision 2.0 uhub3 at usb3 configuration 1 interface 0 "Generic EHCI root hub" rev 2.00/1.00 addr 1 ohci1 at mainbus0: version 1.0 rkiic0 at mainbus0 iic0 at rkiic0 rkpmic0 at iic0 addr 0x20: RK809 "gpu" at mainbus0 not configured "video-codec" at mainbus0 not configured "iommu" at mainbus0 not configured "video-codec" at mainbus0 not configured "iommu" at mainbus0 not configured dwqe0 at mainbus0 gmac 1: rev 0x00, address 5a:29:04:e3:56:68 rgephy0 at dwqe0 phy 0: RTL8169S/8110S/8211 PHY, rev. 6 "vop" at mainbus0 not configured "iommu" at mainbus0 not configured "hdmi" at mainbus0 not configured dwmmc0 at mainbus0: 50 MHz base clock sdmmc0 at dwmmc0: 4-bit, sd high-speed, dma dwmshc0 at mainbus0 dwmshc0: SDHC 6.0, 200 MHz base clock sdmmc1 at dwmshc0: 8-bit, sd high-speed, mmc high-speed, dma "i2s" at mainbus0 not configured "dma-controller" at mainbus0 not configured "dma-controller" at mainbus0 not configured rkiic1 at mainbus0 iic1 at rkiic1 pcxrtc0 at iic1 addr 0x51: battery ok "watchdog" at mainbus0 not configured com0 at mainbus0: dw16550, 64 byte fifo com0: console rktemp0 at mainbus0 "saradc" at mainbus0 not configured rkpwm0 at mainbus0rkclock_get_frequency(rkclock1, 349) : no clock rkrng0 at mainbus0: ver 2 dwpcie0 at mainbus0 dwpcie1 at mainbus0 dwqe1 at mainbus0 gmac 0: rev 0x00, address 5a:29:04:e3:56:69 dwmdio0 at mainbus0 mtsw0 at dwmdio0 addr 31: MT7531 rev 1 gpioleds0 at mainbus0: "status", "power" "dc-12v-regulator" at mainbus0 not configured "hdmi-con" at mainbus0 not configured "ir-receiver" at mainbus0 not configured "vcc3v3-sys-regulator" at mainbus0 not configured "vcc5v0-sys-regulator" at mainbus0 not configured "pcie30-avdd0v9-regulator" at mainbus0 not configured "pcie30-avdd1v8-regulator" at mainbus0 not configured "vcc3v3-pi6c-05-regulator" at mainbus0 not configured "vcc3v3-minipcie-regulator" at mainbus0 not configured "vcc3v3-ngff-regulator" at mainbus0 not configured "vcc5v0-usb-regulator" at mainbus0 not configured "vcc5v0-usb-host-regulator" at mainbus0 not configured "vcc5v0-usb-otg-regulator" at mainbus0 not configured "binman" at mainbus0 not configured "dmc" at mainbus0 not configured "nvmem" at mainbus0 not configured "smbios" at mainbus0 not configured usb4 at ohci0: USB revision 1.0 uhub4 at usb4 configuration 1 interface 0 "Generic OHCI root hub" rev 1.00/1.00 addr 1 usb5 at ohci1: USB revision 1.0 uhub5 at usb5 configuration 1 interface 0 "Generic OHCI root hub" rev 1.00/1.00 addr 1 dwpcie0: can't initialize hardware dwpcie1: can't initialize hardware scsibus1 at sdmmc0: 2 targets, initiator 0 sd1 at scsibus1 targ 1 lun 0: removable sd1: 15193MB, 512 bytes/sector, 31116288 sectors mtsport0 at mtsw0 port 1: address fe:e1:ba:d0:cf:d9 ukphy0 at mtsport0 phy 1: Generic IEEE 802.3u media interface, rev. 1: OUI 0x00e8a5, model 0x0004 mtsport1 at mtsw0 port 2: address fe:e1:ba:d1:35:15 ukphy1 at mtsport1 phy 2: Generic IEEE 802.3u media interface, rev. 1: OUI 0x00e8a5, model 0x0004 mtsport2 at mtsw0 port 3: address fe:e1:ba:d2:0b:a1 ukphy2 at mtsport2 phy 3: Generic IEEE 802.3u media interface, rev. 1: OUI 0x00e8a5, model 0x0004 mtsport3 at mtsw0 port 4: address fe:e1:ba:d3:df:9d ukphy3 at mtsport3 phy 4: Generic IEEE 802.3u media interface, rev. 1: OUI 0x00e8a5, model 0x0004 mtsw0: dwqe1 at port 5 scsibus2 at sdmmc1: 2 targets, initiator 0 sd2 at scsibus2 targ 1 lun 0: sd2: 14910MB, 512 bytes/sector, 30535680 sectors ure0 at uhub1 port 2 configuration 1 interface 0 "Realtek USB 10/100/1G/2.5G LAN" rev 3.20/31.00 addr 2 ure0: RTL8156B (0x7410), address a0:ce:c8:f7:94:72 vscsi0 at root scsibus3 at vscsi0: 256 targets softraid0 at root scsibus4 at softraid0: 256 targets root on sd1a (4d451033c2c69cc9.a) swap on sd1b dump on sd1b rkdrm0: no display interface ports configured you need "up" in /etc/hostname.dwqe1 before you can move packets on the switch ports. ifconfig looks like this: dlg@bpi ~$ ifconfig lo0: flags=8049 mtu 32768 index 4 priority 0 llprio 3 groups: lo inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4 inet 127.0.0.1 netmask 0xff000000 dwqe0: flags=8802 mtu 1500 lladdr 5a:29:04:e3:56:68 description: wan index 1 priority 0 llprio 3 media: Ethernet autoselect (none) status: no carrier dwqe1: flags=8b43 mtu 1500 lladdr 5a:29:04:e3:56:69 index 2 priority 0 llprio 3 trunk: trunkdev mtsw0 status: active enc0: flags=0<> index 3 priority 0 llprio 3 groups: enc status: active mtsport0: flags=8002 mtu 1500 lladdr fe:e1:ba:d0:cf:d9 description: lan0 index 5 priority 0 llprio 3 media: Ethernet autoselect (none) status: no carrier mtsport1: flags=808043 mtu 1500 lladdr fe:e1:ba:d1:84:62 description: lan1 index 6 priority 0 llprio 3 groups: egress media: Ethernet autoselect (1000baseT full-duplex) status: active inet 10.0.127.197 netmask 0xffffff00 broadcast 10.0.127.255 mtsport2: flags=1008043 mtu 1500 lladdr fe:e1:ba:d2:0b:a1 description: lan2 index 7 priority 0 llprio 3 media: Ethernet autoselect (none) status: no carrier mtsport3: flags=8002 mtu 1500 lladdr fe:e1:ba:d3:df:9d description: lan3 index 8 priority 0 llprio 3 media: Ethernet autoselect (none) status: no carrier ure0: flags=1008843 mtu 1500 lladdr a0:ce:c8:f7:94:72 index 9 priority 0 llprio 3 media: Ethernet autoselect status: no carrier pflog0: flags=141 mtu 33136 index 10 priority 0 llprio 3 groups: pflog you use mtsport interfaces like you would any other ethernet interface in openbsd. that also means all packets are processed in software on the cpu, even if they're moving between mtsport interfaces. here's the diff. it almost certainly does not apply to current any more though. Index: fdt/dwmdio.c =================================================================== RCS file: fdt/dwmdio.c diff -N fdt/dwmdio.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fdt/dwmdio.c 24 Apr 2023 02:08:20 -0000 @@ -0,0 +1,102 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +/* + * the dwmdio node is a child of an ethernet/gmac node in the device + * tree. this grafts the dwmdio kernel device to the gmac parent bus + * to avoid confusing kernel autoconf, which can't tell the difference + * between the mii devices we usually want to attach and this dwmdio + * device we sometimes want to attach. + */ + +extern int simplebus_print(void *, const char *); + +struct device * +dwmdio_found(struct device *dv, int node) +{ + char name[32]; + struct fdt_attach_args faa = { + .fa_node = node, + .fa_name = name, + }; + + OF_getpropstr(node, "name", name, sizeof(name)); + + return (config_found(dv->dv_parent, &faa, simplebus_print)); +} + +struct dwmdio_softc { + struct device sc_dev; +}; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + +static int dwmdio_match(struct device *, void *, void *); +static void dwmdio_attach(struct device *, struct device *, void *); + +struct cfdriver dwmdio_cd = { + NULL, "dwmdio", DV_DULL +}; + +const struct cfattach dwmdio_ca = { + sizeof (struct dwmdio_softc), dwmdio_match, dwmdio_attach, +}; + +static int +dwmdio_match(struct device *parent, void *cfdata, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return (OF_is_compatible(faa->fa_node, "snps,dwmac-mdio")); +} + +static void +dwmdio_attach(struct device *parent, struct device *self, void *aux) +{ + struct fdt_attach_args *faa = aux; + int node; + struct mii_bus *mii; + + /* find the mii ops the "parent" gmac device provides */ + node = OF_parent(faa->fa_node); + mii = mii_bynode(node); + if (mii == NULL) { + printf(": mdio operations not found\n"); + return; + } + + printf("\n"); + + mdio_attach(self, mii, faa->fa_node); +} Index: fdt/dwmdiovar.h =================================================================== RCS file: fdt/dwmdiovar.h diff -N fdt/dwmdiovar.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fdt/dwmdiovar.h 24 Apr 2023 02:08:20 -0000 @@ -0,0 +1,19 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct device *dwmdio_found(struct device *, int); Index: fdt/files.fdt =================================================================== RCS file: /cvs/src/sys/dev/fdt/files.fdt,v retrieving revision 1.186 diff -u -p -r1.186 files.fdt --- fdt/files.fdt 19 Apr 2023 00:19:17 -0000 1.186 +++ fdt/files.fdt 24 Apr 2023 02:08:20 -0000 @@ -3,6 +3,7 @@ # Config file and device description for machine-independent FDT code. # Included by ports that need it. +define mdio {} define spmi {} device iicmux: i2cbus @@ -216,6 +217,11 @@ file dev/fdt/if_dwge.c dwge attach dwqe at fdt with dwqe_fdt file dev/fdt/if_dwqe_fdt.c dwqe_fdt +# Synopsis Designware MAC MDIO +device dwmdio: mdio +attach dwmdio at fdt +file dev/fdt/dwmdio.c dwmdio needs-flag + attach ehci at fdt with ehci_fdt file dev/fdt/ehci_fdt.c ehci_fdt @@ -475,7 +493,7 @@ device mvpinctrl attach mvpinctrl at fdt file dev/fdt/mvpinctrl.c mvpinctrl -device mvmdio: fdt +device mvmdio: mdio attach mvmdio at fdt file dev/fdt/mvmdio.c mvmdio @@ -501,10 +519,6 @@ device mvspi: spi attach mvspi at fdt file dev/fdt/mvspi.c mvspi -device mvsw -attach mvsw at fdt -file dev/fdt/mvsw.c mvsw - device mvtemp attach mvtemp at fdt file dev/fdt/mvtemp.c mvtemp @@ -720,3 +734,20 @@ file dev/fdt/qcrtc.c qcrtc device tipd attach tipd at i2c file dev/fdt/tipd.c tipd + +# mdio support + +file dev/fdt/mdio.c mdio + +device mvsw {} +attach mvsw at mdio +device mvsport: ether, ifnet, mii, ifmedia +attach mvsport at mvsw +file dev/fdt/mvsw.c mvsw | mvsport + +# Mediatek MT7531 switch +device mtsw {} +attach mtsw at mdio +device mtsport: ether, ifnet, mii, ifmedia +attach mtsport at mtsw +file dev/fdt/mtsw.c mtsw | mtsport Index: fdt/if_dwqe_fdt.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/if_dwqe_fdt.c,v retrieving revision 1.11 diff -u -p -r1.11 if_dwqe_fdt.c --- fdt/if_dwqe_fdt.c 24 Apr 2023 01:33:32 -0000 1.11 +++ fdt/if_dwqe_fdt.c 24 Apr 2023 02:08:20 -0000 @@ -21,6 +21,7 @@ */ #include "bpfilter.h" +#include "dwmdio.h" #include #include @@ -51,6 +52,10 @@ #include #include +#if NDWMDIO > 0 +#include +#endif + #if NBPFILTER > 0 #include #endif @@ -236,13 +241,31 @@ dwqe_fdt_attach(struct device *parent, s if (sc->sc_ih == NULL) printf("%s: can't establish interrupt\n", sc->sc_dev.dv_xname); + OF_getpropstr(faa->fa_node, "label", + ifp->if_description, sizeof(ifp->if_description)); + sc->sc_ifd.if_node = faa->fa_node; sc->sc_ifd.if_ifp = ifp; if_register(&sc->sc_ifd); - /* force a configuraton of the clocks/mac */ - if (sc->sc_fixed_link) + if (sc->sc_fixed_link) { + /* force a configuraton of the clocks/mac */ sc->sc_mii.mii_statchg(self); + +#if NDWMDIO > 0 + node = OF_getnodebyname(sc->sc_node, "mdio"); + if (node == 0) + return; + + sc->sc_mdio.md_node = sc->sc_node; + sc->sc_mdio.md_cookie = self; + sc->sc_mdio.md_readreg = dwqe_mii_readreg; + sc->sc_mdio.md_writereg = dwqe_mii_writereg; + mii_register(&sc->sc_mdio); + + dwmdio_found(self, node); +#endif + } } void Index: fdt/if_mvneta.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/if_mvneta.c,v retrieving revision 1.30 diff -u -p -r1.30 if_mvneta.c --- fdt/if_mvneta.c 13 Apr 2023 02:19:05 -0000 1.30 +++ fdt/if_mvneta.c 24 Apr 2023 02:08:20 -0000 @@ -808,6 +808,9 @@ mvneta_attach_deferred(struct device *se if_attach(ifp); ether_ifattach(ifp); + OF_getpropstr(sc->sc_node, "label", + ifp->if_description, sizeof(ifp->if_description)); + sc->sc_ifd.if_node = sc->sc_node; sc->sc_ifd.if_ifp = ifp; if_register(&sc->sc_ifd); Index: fdt/mdio.c =================================================================== RCS file: fdt/mdio.c diff -N fdt/mdio.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fdt/mdio.c 24 Apr 2023 02:08:20 -0000 @@ -0,0 +1,72 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +static int mdio_print(void *, const char *); + +void +mdio_attach(struct device *self, struct mii_bus *mii, int bnode) +{ + struct mdio_attach_args maa; + char name[32]; + int addr; + int node; + + for (node = OF_child(bnode); node != 0; node = OF_peer(node)) { + addr = OF_getpropint(node, "reg", -1); + if (addr == -1) + continue; + + OF_getpropstr(node, "name", name, sizeof(name)); + + memset(&maa, 0, sizeof(maa)); + maa.maa_bus = mii; + maa.maa_addr = addr; + maa.maa_name = name; + maa.maa_node = node; + + config_found(self, &maa, mdio_print); + } +} + +static int +mdio_print(void *aux, const char *pnp) +{ + struct mdio_attach_args *maa = aux; + + if (pnp != NULL) + printf("\"%s\" at %s", maa->maa_name, pnp); + printf(" addr %d", maa->maa_addr); + + return (UNCONF); +} Index: fdt/mdiovar.h =================================================================== RCS file: fdt/mdiovar.h diff -N fdt/mdiovar.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fdt/mdiovar.h 24 Apr 2023 02:08:20 -0000 @@ -0,0 +1,27 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct mdio_attach_args { + struct mii_bus *maa_bus; + const char *maa_bname; + int maa_node; + int maa_addr; + char *maa_name; +}; + +void mdio_attach(struct device *, struct mii_bus *, int); Index: fdt/mtsw.c =================================================================== RCS file: fdt/mtsw.c diff -N fdt/mtsw.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ fdt/mtsw.c 24 Apr 2023 02:08:20 -0000 @@ -0,0 +1,1766 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2023 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Mediatek MT7531 switch driver + */ + +#include "bpfilter.h" +#include "kstat.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +/* for mtsw_p_ioctl trunk handling */ +#include /* if_trunk.h uses siphash bits */ +#include + +#if NBPFILTER > 0 +#include +#endif + +#if NKSTAT > 0 +#include +#endif + +#define MTSW_PHY_ADDR_MASK 0x1f /* host talking to mtsw */ + +#define MTSW_PORT_MIN 0 +#define MTSW_PORT_MAX 6 +#define MTSW_PORT_COUNT 7 + +#define MTSW_PORT_PHY_MIN 0 +#define MTSW_PORT_PHY_MAX 4 +#define MTSW_PORT_PHY_COUNT 5 + +#define MTSW_PORT_EXT 5 +#define MTSW_PORT_CPU 6 + +/* + * actual mtsw register accesses involve a bunch of mdio ops + */ + +#define MTSW_MDIO_HI 0x10 +#define MTSW_MDIO_PAGE_REG 0x1f + +#define MTSW_MDIO_PAGE_SHIFT 6 +#define MTSW_MDIO_PAGE_MASK 0x3ff +#define MTSW_MDIO_REG_SHIFT 2 +#define MTSW_MDIO_REG_MASK 0xf + +/* + * registers + */ + +/* Address Resolution Logic (ARL) Registers */ +#define MTSW_R_ATC 0x0080 /* Address Table Control */ +#define MTSW_R_ATC_AC_CMD_MASK (0x7 << 0) +#define MTSW_R_ATC_AC_CMD_READ (0x0 << 0) +#define MTSW_R_ATC_AC_CMD_WRITE (0x1 << 0) +#define MTSW_R_ATC_AC_CMD_CLEAN (0x2 << 0) +#define MTSW_R_ATC_AC_CMD_SEARCH_START (0x4 << 0) +#define MTSW_R_ATC_AC_CMD_SEARCH_NEXT (0x5 << 0) +#define MTSW_R_ATC_AC_CAE (1U << 3) +#define MTSW_R_ATC_AC_SAT (0x3 << 4) +#define MTSW_R_ATC_AC_SAT_MAC (0x0 << 4) +#define MTSW_R_ATC_AC_SAT_DIP (0x1 << 4) +#define MTSW_R_ATC_AC_SAT_SIP (0x2 << 4) +#define MTSW_R_ATC_AC_SAT_ADDRESS (0x3 << 4) +#define MTSW_R_ATC_AC_MAT (0xf << 8) +#define MTSW_R_ATC_AC_MAT_ALL_MAC (0x0 << 8) +#define MTSW_R_ATC_AC_MAT_ALL_DIP_GA (0x1 << 8) +#define MTSW_R_ATC_AC_MAT_ALL_SIP (0x2 << 8) +#define MTSW_R_ATC_AC_MAT_ALL_VALID (0x3 << 8) +#define MTSW_R_ATC_AC_MAT_ALL_DYN_MAC (0x4 << 8) +#define MTSW_R_ATC_AC_MAT_ALL_DYN_DIP (0x5 << 8) +#define MTSW_R_ATC_AC_MAT_ALL_STATIC_MAC (0x6 << 8) +#define MTSW_R_ATC_AC_MAT_ALL_STATIC_DIP (0x7 << 8) +#define MTSW_R_ATC_ADDR_INVLD (1U << 12) +#define MTSW_R_ATC_SRCH_HIT (1U << 13) +#define MTSW_R_ATC_SRCH_END (1U << 14) +#define MTSW_R_ATC_BUSY (1U << 15) +#define MTSW_R_ATC_ADDR_SHIFT 16 +#define MTSW_R_ATC_ADDR_MASK 0xfff +#define MTSW_R_ATC_COL_ACC (1U << 28) +#define MTSW_R_VTCR 0x0090 /* VLAN Table Control */ +#define MTSW_R_VTCR_VID_SHIFT 0 +#define MTSW_R_VTCR_VID_MASK 0xfff +#define MTSW_R_VTCR_FUNC_MASK (0xf << 12) +#define MTSW_R_VTCR_FUNC_READ_VID (0x0 << 12) +#define MTSW_R_VTCR_FUNC_WRITE_VID (0x1 << 12) +#define MTSW_R_VTCR_IDX_INVLD (1U << 16) +#define MTSW_R_VTCR_BUSY (1U << 31) +#define MTSW_R_VAWD1 0x0094 /* VLAN and ACL Write Data I */ +#define MTSW_R_VAWD2 0x0098 /* VLAN and ACL Write Data II */ + +/* Address Resolution Logic (ARL) Port-Based Registers */ +#define MTSW_R_SSC(_p) (0x2000 + (_p) * 0x100) +#define MTSW_R_PCR(_p) (0x2004 + (_p) * 0x100) +#define MTSW_R_PCR_PORT_VLAN_MASK (0x3 << 0) +#define MTSW_R_PCR_PORT_VLAN_MATRIX (0x0 << 0) +#define MTSW_R_PCR_PORT_VLAN_FALLBACK (0x1 << 0) +#define MTSW_R_PCR_PORT_VLAN_CHECK (0x2 << 0) +#define MTSW_R_PCR_PORT_VLAN_SECURITY (0x3 << 0) +#define MTSW_R_PCR_VLAN_MIS (1U << 2) +#define MTSW_R_PCR_MIS_PORT_FW_MASK (0x7 << 4) +#define MTSW_R_PCR_MIS_PORT_FW_DFLT (0x0 << 4) +#define MTSW_R_PCR_MIS_PORT_FW_DFLT_CPU_EXC (0x4 << 4) +#define MTSW_R_PCR_MIS_PORT_FW_DFLT_CPU_INC (0x5 << 4) +#define MTSW_R_PCR_MIS_PORT_FW_CPU_ONLY (0x6 << 4) +#define MTSW_R_PCR_MIS_PORT_FW_DROP (0x7 << 4) +#define MTSW_R_PCR_ACL_MIR (1U << 7) +#define MTSW_R_PCR_PORT_RX_MIR (1U << 8) +#define MTSW_R_PCR_PORT_TX_MIR (1U << 9) +#define MTSW_R_PCR_ACL_EN (1U << 10) +#define MTSW_R_PCR_UP2TAG_EN (1U << 11) +#define MTSW_R_PCR_UP2DSCP_EN (1U << 12) +#define MTSW_R_PCR_PORT_MATRIX_SHIFT 16 +#define MTSW_R_PCR_PORT_MATRIX_MASK 0xff +#define MTSW_R_PCR_PORT_PRI_SHIFT 24 +#define MTSW_R_PCR_PORT_PRI_MASK 0x7 +#define MTSW_R_PCR_EG_TAG_MASK (0x3 << 28) +#define MTSW_R_PCR_EG_TAG_UNTAGGED (0x0 << 28) +#define MTSW_R_PCR_EG_TAG_SWAP (0x1 << 28) +#define MTSW_R_PCR_EG_TAG_TAGGED (0x2 << 28) +#define MTSW_R_PCR_EG_TAG_STACK (0x3 << 28) +#define MTSW_R_PCR_MDLV2_EN (1U << 30) + +#define MTSW_R_PSC(_p) (0x200c + (_p) * 0x100) +#define MTSW_R_PSC_RX_PORT_LOCK (1U << 0) +#define MTSW_R_PSC_TX_PORT_LOCK (1U << 1) +#define MTSW_R_PSC_SA_LOCK_MASK (0x3 << 2) +#define MTSW_R_PSC_SA_DIS (1U << 4) +#define MTSW_R_PSC_SA_CNT_EN (1U << 5) +#define MTSW_R_PSC_MAC_SA_LRN_SHIFT 8 +#define MTSW_R_PSC_MAC_SA_LRN_MASK 0xfff +#define MTSW_R_PSC_MAC_SA_LRN_DIS 0x0 +#define MTSW_R_PSC_MAC_SA_LRN_UNLIMITED 0xfff +#define MTSW_R_PSC_SA_LRN_CNT_SHIFT 20 +#define MTSW_R_PVC(_p) (0x2010 + (_p) * 0x100) +#define MTSW_R_PVC_ACC_FRM_MASK (0x3 << 0) +#define MTSW_R_PVC_ACC_FRM_MASK_ALL (0x0 << 0) +#define MTSW_R_PVC_ACC_FRM_MASK_ONLY_VLAN (0x1 << 0) +#define MTSW_R_PVC_ACC_FRM_MASK_ONLY_VLAN_PRI (0x2 << 0) +#define MTSW_R_PVC_UC_LKYV_EN (1U << 2) +#define MTSW_R_PVC_MC_LKYV_EN (1U << 3) +#define MTSW_R_PVC_IPM_LKYV_EN (1U << 4) +#define MTSW_R_PVC_PORT_STAG (1U << 5) +#define MTSW_R_PVC_VLAN_ATTR_MASK (0x3 << 6) +#define MTSW_R_PVC_VLAN_ATTR_USER_PORT (0x0 << 6) +#define MTSW_R_PVC_VLAN_ATTR_STACK_PORT (0x1 << 6) +#define MTSW_R_PVC_VLAN_ATTR_XLAT_PORT (0x2 << 6) +#define MTSW_R_PVC_VLAN_ATTR_TRANS_PORT (0x3 << 6) +#define MTSW_R_PVC_EG_TAG_MASK (0x7 << 8) +#define MTSW_R_PVC_EG_TAG_CONSISTENT (0x0 << 8) +#define MTSW_R_PVC_EG_TAG_UNTAGGED (0x4 << 8) +#define MTSW_R_PVC_EG_TAG_SWAP (0x5 << 8) +#define MTSW_R_PVC_EG_TAG_TAGGED (0x6 << 8) +#define MTSW_R_PVC_EG_TAG_STACK (0x7 << 8) +#define MTSW_R_PVC_PT_OPTION (1U << 11) +#define MTSW_R_PVC_BC_LKYV_EN (1U << 13) +#define MTSW_R_PVC_FORCE_PVID (1U << 14) +#define MTSW_R_PVC_DIS_PVID (1U << 15) +#define MTSW_R_PVC_STAG_VPID_MASK 0xffff +#define MTSW_R_PVC_STAG_VPID_SHIFT 16 + +#define MTSW_R_PPBV1(_p) (0x2014 + (_p) * 0x100) +#define MTSW_R_PPBV1_G0_PORT_VID_SHIFT 0 + +#define MTSW_R_PPBV1(_p) (0x2014 + (_p) * 0x100) +#define MTSW_R_PPBV1_G0_PORT_VID_SHIFT 0 +#define MTSW_R_PPBV1_G0_PORT_VID_MASK 0xfff +#define MTSW_R_PPBV1_G0_PORT_PRI_SHIFT 13 +#define MTSW_R_PPBV1_G0_PORT_PRI_MASK 0x7 +#define MTSW_R_PPBV1_G1_PORT_VID_SHIFT 16 +#define MTSW_R_PPBV1_G1_PORT_VID_MASK 0xfff +#define MTSW_R_PPBV1_G1_PORT_PRI_SHIFT 29 +#define MTSW_R_PPBV1_G1_PORT_PRI_MASK 0x7 + +/* MAC */ +#define MTSW_R_PMCR(_p) (0x3000 + (_p) * 0x100) +#define MTSW_R_PMCR_FORCE_LINK_MASK (0x1 << 0) +#define MTSW_R_PMCR_FORCE_LINK_DOWN (0x0 << 0) +#define MTSW_R_PMCR_FORCE_LINK_UP (0x1 << 0) +#define MTSW_R_PMCR_FORCE_DPX_MASK (0x1 << 1) +#define MTSW_R_PMCR_FORCE_DPX_HALF (0x0 << 1) +#define MTSW_R_PMCR_FORCE_DPX_FULL (0x1 << 1) +#define MTSW_R_PMCR_FORCE_SPD_MASK (0x3 << 2) +#define MTSW_R_PMCR_FORCE_SPD_10M (0x0 << 2) +#define MTSW_R_PMCR_FORCE_SPD_100M (0x1 << 2) +#define MTSW_R_PMCR_FORCE_SPD_1000M (0x2 << 2) +#define MTSW_R_PMCR_FORCE_TX_FC (0x1 << 4) +#define MTSW_R_PMCR_FORCE_RX_FC (0x1 << 5) +#define MTSW_R_PMCR_FORCE_EEE100 (0x1 << 6) +#define MTSW_R_PMCR_FORCE_EEE1G (0x1 << 7) +#define MTSW_R_PMCR_BACKPR_EN (0x1 << 8) +#define MTSW_R_PMCR_BKOFF_EN (0x1 << 9) +#define MTSW_R_PMCR_MAC_PRE (0x1 << 11) +#define MTSW_R_PMCR_MAC_RX_EN (0x1 << 13) +#define MTSW_R_PMCR_MAC_TX_EN (0x1 << 14) +#define MTSW_R_PMCR_MAC_MODE_MASK (0x1 << 16) +#define MTSW_R_PMCR_MAC_MODE_PHY (0x0 << 16) +#define MTSW_R_PMCR_MAC_MODE_MAC (0x1 << 16) +#define MTSW_R_PMCR_EXT_PHY (0x1 << 17) +#define MTSW_R_PMCR_IPG_CFG_MASK (0x3 << 18) +#define MTSW_R_PMCR_IPG_CFG_NORMAL (0x0 << 18) +#define MTSW_R_PMCR_IPG_CFG_SHORT_RND (0x1 << 18) +#define MTSW_R_PMCR_IPG_CFG_SHRINK (0x2 << 18) +#define MTSW_R_PMCR_FORCE_MODE_EEE1G (0x1 << 25) +#define MTSW_R_PMCR_FORCE_MODE_EEE100 (0x1 << 26) +#define MTSW_R_PMCR_FORCE_MODE_TX_FC (0x1 << 27) +#define MTSW_R_PMCR_FORCE_MODE_RX_FC (0x1 << 28) +#define MTSW_R_PMCR_FORCE_MODE_DPX (0x1 << 29) +#define MTSW_R_PMCR_FORCE_MODE_SPD (0x1 << 30) +#define MTSW_R_PMCR_FORCE_MODE_LNK (0x1 << 31) +#define MTSW_R_PMEEECR(_p) (0x3004 + (_p) * 0x100) +#define MTSW_R_PMSR(_p) (0x3008 + (_p) * 0x100) +#define MTSW_R_PINT_EN(_p) (0x3010 + (_p) * 0x100) +#define MTSW_R_PINT_STS(_p) (0x3014 + (_p) * 0x100) +#define MTSW_R_DBG_CNT(_p) (0x3018 + (_p) * 0x100) +#define MTSW_R_DBG_CNT_DIS_CLR (1U << 31) +#define MTSW_R_WOL(_p) (0x3020 + (_p) * 0x100) +#define MTSW_R_PFC_STS(_p) (0x3024 + (_p) * 0x100) +#define MTSW_R_PFC_RX_PSON_CNT_L(_p) (0x3030 + (_p) * 0x100) +#define MTSW_R_PFC_RX_PSON_CNT_H(_p) (0x3034 + (_p) * 0x100) +#define MTSW_R_PFC_RX_PSOFF_CNT_L(_p) (0x3038 + (_p) * 0x100) +#define MTSW_R_PFC_RX_PSOFF_CNT_H(_p) (0x303c + (_p) * 0x100) +#define MTSW_R_PFC_TX_PSON_CNT_L(_p) (0x3040 + (_p) * 0x100) +#define MTSW_R_PFC_TX_PSON_CNT_H(_p) (0x3044 + (_p) * 0x100) +#define MTSW_R_PFC_TX_PSOFF_CNT_L(_p) (0x3048 + (_p) * 0x100) +#define MTSW_R_PFC_TX_PSOFF_CNT_H(_p) (0x304c + (_p) * 0x100) + +#define MTSW_R_PFC_CTRL 0x30b0 +#define MTSW_R_PFC_AUTO_SYNC_DLY_SEL 0x30b4 +#define MTSW_R_SGMII_2P5G_SPD_CTRL 0x30b8 +#define MTSW_R_LPDET_CTRL 0x30c0 +#define MTSW_R_LPDET_SA_MSB 0x30c8 +#define MTSW_R_LPDET_SA_LSB 0x30cc +#define MTSW_R_LPDET_RXSA_MSB 0x30d0 +#define MTSW_R_LPDET_RXSA_MSB 0x30d0 +#define MTSW_R_GMACCR 0x30e0 +#define MTSW_R_GMACCR_MAX_RX_PKT_LEN_MASK (0x3 << 0) +#define MTSW_R_GMACCR_MAX_RX_PKT_LEN_1518 (0x0 << 0) +#define MTSW_R_GMACCR_MAX_RX_PKT_LEN_1536 (0x1 << 0) +#define MTSW_R_GMACCR_MAX_RX_PKT_LEN_1552 (0x2 << 0) +#define MTSW_R_GMACCR_MAX_RX_PKT_LEN_JUMBO (0x3 << 0) +#define MTSW_R_GMACCR_MAX_RX_JUMBO_MASK (0xf << 2) +#define MTSW_R_GMACCR_MAX_RX_JUMBO(_k) ((_k) << 2) +#define MTSW_R_GMACCR_MTCC_LMT_MASK (0xf << 9) +#define MTSW_R_GMACCR_MTCC_LMT_DISABLE (0x0 << 9) +#define MTSW_R_GMACCR_MTCC_LMT(_l) ((_l) << 9) +#define MTSW_R_GMACCR_PRMBL_LMT_EN (1U << 17) +#define MTSW_R_GMACCR_RXCRC_EN (1U << 18) +#define MTSW_R_GMACCR_TXCRC_EN (1U << 19) +#define MTSW_R_SMACCR0 0x30e4 +#define MTSW_R_SMACCR1 0x30e8 +#define MTSW_R_CKGCR 0x30f0 +#define MTSW_R_GPINT_EN 0x30f4 +#define MTSW_R_GPINT_STS 0x30f8 + +#define MTSW_R_MIB_BASE(_p) (0x4000 + (_p) * 0x100) +#define MTSW_R_AECNT(_ae) (0x4f00 + (_ae) * 4) /* 0 .. 7 */ +#define MTSW_R_MIBCCR 0x4fe0 /* MIB Counter Control */ +#define MTSW_R_MIBCCR_TX_OCT_CNT_BAD (1U << 4) +#define MTSW_R_MIBCCR_TX_OCT_CNT_GOOD (1U << 5) +#define MTSW_R_MIBCCR_RX_OCT_CNT_BAD (1U << 6) +#define MTSW_R_MIBCCR_RX_OCT_CNT_GOOD (1U << 7) +#define MTSW_R_MIBCCR_MIB_ENABLE (1U << 31) +#define MTSW_R_AECCR 0x4fe0 /* ARL Event Counter Control */ + +#define MTSW_R_SYS_CTRL 0x7000 /* System Control */ +#define MTSW_R_SYS_CTRL_SW_REG_RST (1U << 0) +#define MTSW_R_SYS_CTRL_SW_SYS_RST (1U << 1) +#define MTSW_R_SYS_CTRL_SW_PHY_RST (1U << 2) +#define MTSW_R_SYS_CTRL_MBIST_EN (1U << 4) /* "No used" */ +#define MTSW_R_SYS_CTRL_MBIST_CMP (1U << 5) +#define MTSW_R_SYS_CTRL_FL_BIST_STS (1U << 6) +#define MTSW_R_SYS_CTRL_PL_BIST_STS (1U << 7) +#define MTSW_R_SYS_CTRL_PB_BIST_STS (1U << 8) +#define MTSW_R_SYS_CTRL_MIB_BIST_STS (1U << 9) +#define MTSW_R_SYS_CTRL_VLN_BIST_STS (1U << 10) +#define MTSW_R_SYS_CTRL_ADDR_BIST_STS (1U << 11) +#define MTSW_R_SYS_CTRL_CTRL_BIST_STS (1U << 12) +#define MTSW_R_SYS_CTRL_MASK_BIST_STS (1U << 14) +#define MTSW_R_SYS_CTRL_COL_BIST_STS (1U << 16) +#define MTSW_R_SYS_CTRL_VLAN_TAB_INIT (1U << 20) +#define MTSW_R_SYS_CTRL_MAC_TAB_INIT (1U << 21) +#define MTSW_R_SYS_CTRL_ACL_TAB_INIT (1U << 22) + +#define MTSW_R_SYS_INT_EN 0x7008 /* System Interrupt Enable */ +#define MTSW_R_SYS_INT_STS 0x700c /* System Interrupt Status */ +#define MTSW_R_PMDC_CFG 0x7014 /* PMDC Control Register */ +#define MTSW_R_PHY_POLL 0x7018 /* PHY Polling + SMI Master */ +#define MTSW_R_PHY_IAC 0x701c /* PHY Indirect Access Control */ +#define MTSW_R_PHY_IAC_MDIO_DATA_SHIFT 0 +#define MTSW_R_PHY_IAC_MDIO_DATA_MASK 0xffff +#define MTSW_R_PHY_IAC_MDIO_ST_C45 (0x0 << 16) +#define MTSW_R_PHY_IAC_MDIO_ST_C22 (0x1 << 16) +#define MTSW_R_PHY_IAC_MDIO_CMD_C45_ADDR (0x0 << 18) +#define MTSW_R_PHY_IAC_MDIO_CMD_WRITE (0x1 << 18) +#define MTSW_R_PHY_IAC_MDIO_CMD_READ (0x2 << 18) +#define MTSW_R_PHY_IAC_MDIO_CMD_READ_C45 (0x3 << 18) +#define MTSW_R_PHY_IAC_MDIO_PHY_ADDR(_a) ((_a) << 20) +#define MTSW_R_PHY_IAC_MDIO_REG_ADDR(_r) ((_r) << 25) +#define MTSW_R_PHY_IAC_PHY_ACS_ST (1U << 31) +#define MTSW_R_PSR_P3_P0 0x7020 /* PHY Status Reg for P3-P0 */ +#define MTSW_R_PSR_P6_P4 0x7024 /* PHY Status Reg for P6-P4 */ +#define MTSW_R_PAUSE_CAP_P6_P0 0x7028 /* MAC Pause TX/RX Cap */ +#define MTSW_R_EEPR_IND 0x7120 /* EEPROM Indirect Access */ +#define MTSW_R_EEPR_STS 0x7124 /* EEPROM Indirect Status */ +#define MTSW_R_EEPR_IND_ADDR 0x7128 /* EEPROM Indirect Addres */ + +#define MTSW_R_CLKGEN_CTRL 0x7500 +#define MTSW_R_CLKGEN_CTRL_EN (1 << 0) +#define MTSW_R_CLKGEN_CTRL_MODE_MASK (0x3 << 1) +#define MTSW_R_CLKGEN_CTRL_MODE_RGMII (0x0 << 1) +#define MTSW_R_CLKGEN_CTRL_MODE_MII (0x1 << 1) +#define MTSW_R_CLKGEN_CTRL_MODE_REV_MII (0x2 << 1) +#define MTSW_R_CLKGEN_CTRL_NO_REVERSE (1 << 4) +#define MTSW_R_CLKGEN_CTRL_NO_DELAY (1 << 5) +#define MTSW_R_CLKGEN_CTRL_SKEW_IN_MASK (0x3 << 6) +#define MTSW_R_CLKGEN_CTRL_SKEW_IN_NO_CHANGE (0x0 << 6) +#define MTSW_R_CLKGEN_CTRL_SKEW_IN_100PPS (0x1 << 6) +#define MTSW_R_CLKGEN_CTRL_SKEW_IN_200PPS (0x2 << 6) +#define MTSW_R_CLKGEN_CTRL_SKEW_IN_REVERSE (0x3 << 6) +#define MTSW_R_CLKGEN_CTRL_SKEW_OUT_MASK (0x3 << 8) +#define MTSW_R_CLKGEN_CTRL_SKEW_OUT_NO_CHANGE (0x0 << 8) +#define MTSW_R_CLKGEN_CTRL_SKEW_OUT_100PPS (0x1 << 8) +#define MTSW_R_CLKGEN_CTRL_SKEW_OUT_200PPS (0x2 << 8) +#define MTSW_R_CLKGEN_CTRL_SKEW_OUT_REVERSE (0x3 << 8) + +/* TOP */ +#define MTSW_R_STRAP 0x7800 /* Strap Status */ +#define MTSW_R_STRAP_TM_DIS (1U << 0) +#define MTSW_R_STRAP_EEP_MODE (1U << 1) +#define MTSW_R_STRAP_PON_LT (1U << 2) +#define MTSW_R_STRAP_PLL_SW (1U << 3) +#define MTSW_R_STRAP_EEE_DIS (1U << 4) +#define MTSW_R_STRAP_EEP_DIS (1U << 5) +#define MTSW_R_STRAP_PHY_EN (1U << 6) +#define MTSW_R_STRAP_XTAL (1U << 7) +#define MTSW_R_STRAP_XTAL25 (1U << 7) +#define MTSW_R_STRAP_XTAL40 (0U << 7) +#define MTSW_R_SWSTRAP 0x7804 /* Software Strap Status */ +#define MTSW_R_SWSTRAP_TM_DIS (1U << 0) +#define MTSW_R_SWSTRAP_EEP_MODE (1U << 1) +#define MTSW_R_SWSTRAP_EEP_MODE_LE16K (0U << 1) +#define MTSW_R_SWSTRAP_EEP_MODE_GT16K (1U << 1) +#define MTSW_R_SWSTRAP_PON_LT (1U << 2) +#define MTSW_R_SWSTRAP_PLL_SW (1U << 3) +#define MTSW_R_SWSTRAP_EEE_DIS (1U << 4) +#define MTSW_R_SWSTRAP_EEP_DIS (1U << 5) +#define MTSW_R_SWSTRAP_PHY_EN (1U << 6) +#define MTSW_R_SWSTRAP_XTAL (1U << 7) +#define MTSW_R_SWSTRAP_XTAL40 (0U << 7) +#define MTSW_R_SWSTRAP_XTAL25 (1U << 7) +#define MTSW_R_SWSTRAP_CHG_STRAP (1U << 8) +#define MTSW_R_SIG_SR 0x780c +#define MTSW_R_SIG_SR_PAD_MCM_SMI_EN (1U << 0) +#define MTSW_R_SIG_SR_PAD_DUAL_SGMII_EN (1U << 1) +#define MTSW_R_LED_SRC 0x7818 /* LED Source Selection */ +#define MTSW_R_CREV 0x781c /* Chip Revision */ +#define MTSW_R_CREV_NAME_SHIFT 16 +#define MTSW_R_CREV_NAME_MASK 0xffff +#define MTSW_R_CREV_NAME_7531 0x7531 +#define MTSW_R_CREV_REV_SHIFT 0 +#define MTSW_R_CREV_REV_MASK 0xf +#define MTSW_R_PLLGP_EN 0x7820 +#define MTSW_R_PLLGP_EN_SW_CLKSW (1 << 0) +#define MTSW_R_PLLGP_EN_SW_PLLGP (1 << 1) +#define MTSW_R_PLLGP_EN_COREPLL_EN (1 << 2) +#define MTSW_R_PLLGP_CR0 0x7830 +#define MTSW_R_PLLGP_CR0_SDM_PCW_CHG (1 << 0) +#define MTSW_R_PLLGP_CR0_SDM_PCW_SHIFT 1 +#define MTSW_R_PLLGP_CR0_SDM_PCW_MASK 0x1fffff +#define MTSW_R_PLLGP_CR0_EN (1 << 22) +#define MTSW_R_PLLGP_CR0_POS_DIV_SHIFT 27 +#define MTSW_R_PLLGP_CR0_POS_DIV_MASK 0x7 + +#define MTSW_R_ANA_PLLGP_CR2 0x78b0 +#define MTSW_R_ANA_PLLGP_CR5 0x78bc + +/* + * VLAN Table + */ +#define MTSW_VT_VALID (1U << 0) +#define MTSW_VT_FID_MASK (0x7 << 1) +#define MTSW_VT_FID_DEFAULT (0x0 << 1) +#define MTSW_VT_FID_BRIDGED (0x1 << 1) +#define MTSW_VT_S_TAG1_SHIFT 4 +#define MTSW_VT_PORT_MEM_SHIFT 16 +#define MTSW_VT_PORT_MEM_MASK 0xff +#define MTSW_VT_PORT_MEM_ALL 0xff +#define MTSW_VT_USER_PRI_SHIFT 24 +#define MTSW_VT_USER_PRI_MASK 0x7 +#define MTSW_VT_COPY_PRI (1U << 27) +#define MTSW_VT_VTAG_EN (1U << 28) +#define MTSW_VT_EG_CON (1U << 29) +#define MTSW_VT_IVL_MAC (1U << 30) +#define MTSW_VT_PORT_STAG (1U << 31) + +/* + * phy modes. note tricky bits. + */ +#define MTSW_PHY_UNKNOWN (1U << 0) +#define MTSW_PHY_RGMII (1U << 1) +#define MTSW_PHY_RGMII_TXID (MTSW_PHY_RGMII | (1U << 2)) +#define MTSW_PHY_RGMII_RXID (MTSW_PHY_RGMII | (1U << 3)) +#define MTSW_PHY_RGMII_ID (MTSW_PHY_RGMII_TXID | MTSW_PHY_RGMII_RXID) +#define MTSW_PHY_SGMII (1U << 5) + +struct mtsw_softc; +struct mtsport_softc; + +struct mtsw_cpu_port { + int p_port; + struct ifnet *p_ifp0; + struct mtsw_softc *p_sc; + + 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(mtsw_cpu_port) + p_entry; +}; + +TAILQ_HEAD(mtsw_cpu_ports, mtsw_cpu_port); + +struct mtsw_softc { + struct device sc_dev; + struct mii_bus *sc_md; + unsigned int sc_addr; + int sc_node; + struct mutex sc_reg_mtx; + uint32_t sc_reg_page; + + struct mutex sc_mii_mtx; + + unsigned int sc_flags; +#define MTSW_F_DUAL_SGMII (1 << 0) + unsigned int sc_rev; + + struct mtsport_softc *sc_ports[MTSW_PORT_COUNT]; + struct mtsw_cpu_ports sc_cpu_ports; + unsigned int sc_cpu_pmask; + + struct rwlock sc_kstat_lock; +}; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + +struct mtsw_attach_args { + char *maa_name; + int maa_node; + int maa_port; +}; + +static int mtsw_match(struct device *, void *, void *); +static void mtsw_attach(struct device *, struct device *, void *); +static void mtsw_pll_init(struct mtsw_softc *, uint32_t); +static void mtsw_attached(struct mtsw_softc *); +static void mtsw_attach_cpu(struct mtsw_softc *, + const struct mtsw_attach_args *, uint32_t); +static int mtsw_print(void *, const char *); + +static uint32_t mtsw_rd(struct mtsw_softc *, uint16_t); +static void mtsw_wr(struct mtsw_softc *, uint16_t, uint32_t); + +static void mtsw_r_set(struct mtsw_softc *sc, uint16_t, uint32_t); +static void mtsw_r_clr(struct mtsw_softc *sc, uint16_t, uint32_t); + +static int mtsw_mii_rd(struct mtsw_softc *, int, int); +static void mtsw_mii_wr(struct mtsw_softc *, int, int, int); + +static int mtsw_at_control(struct mtsw_softc *, uint32_t); +static int mtsw_vt_control(struct mtsw_softc *, + uint32_t, uint32_t); + +static int mtsw_p_ioctl(struct ifnet *, u_long, caddr_t); +static void mtsw_p_input(struct ifnet *, struct mbuf *); +static int mtsw_p_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); + +static inline void mtsport_input(struct mtsport_softc *, struct mbuf *); + +struct mtsw_defer { + struct task d_task; + struct mtsw_softc *d_sc; +}; + +static void mtsw_defer_task(void *); + +#if NKSTAT > 0 +static void mtsw_kstat_attach_mib(struct mtsw_softc *sc, + int, const char *); +#endif + +const struct cfattach mtsw_ca = { + sizeof (struct mtsw_softc), mtsw_match, mtsw_attach +}; + +struct cfdriver mtsw_cd = { + NULL, "mtsw", DV_DULL +}; + +static int +mtsw_match(struct device *parent, void *match, void *aux) +{ + struct mdio_attach_args *maa = aux; + + if (!OF_is_compatible(maa->maa_node, "mediatek,mt7531")) + return (0); + + if (maa->maa_addr & ~MTSW_PHY_ADDR_MASK) + return (0); + + return (1); +} + +static void +mtsw_attach(struct device *parent, struct device *self, void *aux) +{ + struct mtsw_softc *sc = (struct mtsw_softc *)self; + struct mdio_attach_args *maa = aux; + uint32_t r, name; + int port; + struct mtsw_defer *d; + + mtx_init(&sc->sc_reg_mtx, IPL_NET); + sc->sc_reg_page = ~0U; + mtx_init(&sc->sc_mii_mtx, IPL_NET); + rw_init(&sc->sc_kstat_lock, "mtswkslk"); + TAILQ_INIT(&sc->sc_cpu_ports); + + sc->sc_md = maa->maa_bus; + sc->sc_addr = maa->maa_addr; + sc->sc_node = maa->maa_node; + + r = mtsw_rd(sc, MTSW_R_CREV); + name = (r >> MTSW_R_CREV_NAME_SHIFT) & MTSW_R_CREV_NAME_MASK; + if (name != MTSW_R_CREV_NAME_7531) { + printf(": unexpected chip revision name %x\n", name); + return; + } + + sc->sc_rev = (r >> MTSW_R_CREV_REV_SHIFT) & MTSW_R_CREV_REV_MASK; + printf(": MT7531 rev %u\n", sc->sc_rev); + + /* + * doco says "all MACs must be forced to link-down" before + * setting reset bits below. + */ + for (port = MTSW_PORT_PHY_MIN; port <= MTSW_PORT_PHY_MAX; port++) { + r = mtsw_mii_rd(sc, port, MII_BMCR); + SET(r, BMCR_PDOWN); + mtsw_mii_wr(sc, port, MII_BMCR, r); + } + for (port = MTSW_PORT_MIN; port <= MTSW_PORT_MAX; port++) + mtsw_wr(sc, MTSW_R_PMCR(port), MTSW_R_PMCR_FORCE_MODE_LNK); + + /* reset to default values */ + mtsw_wr(sc, MTSW_R_SYS_CTRL, MTSW_R_SYS_CTRL_SW_REG_RST | + MTSW_R_SYS_CTRL_SW_SYS_RST | MTSW_R_SYS_CTRL_SW_PHY_RST); + + r = mtsw_rd(sc, MTSW_R_SIG_SR); + if (ISSET(r, MTSW_R_SIG_SR_PAD_DUAL_SGMII_EN)) + SET(sc->sc_flags, MTSW_F_DUAL_SGMII); + else + mtsw_pll_init(sc, r); + + for (port = MTSW_PORT_MIN; port <= MTSW_PORT_MAX; port++) { + /* disable forwarding */ + r = mtsw_rd(sc, MTSW_R_PCR(port)); + CLR(r, MTSW_R_PCR_PORT_MATRIX_MASK << + MTSW_R_PCR_PORT_MATRIX_SHIFT); + CLR(r, MTSW_R_PCR_PORT_VLAN_MASK); + SET(r, MTSW_R_PCR_PORT_VLAN_MATRIX); + mtsw_wr(sc, MTSW_R_PCR(port), 0 /* r */); + + r = mtsw_rd(sc, MTSW_R_PSC(port)); + + r = mtsw_rd(sc, MTSW_R_PVC(port)); + r = 0x81000000; + mtsw_wr(sc, MTSW_R_PVC(port), r); + +// /* disable learning */ +// mtsw_r_set(sc, MTSW_R_PSC(port), MTSW_R_PSC_SA_DIS); +// mtsw_r_set(sc, MTSW_R_DBG_CNT(port), MTSW_R_DBG_CNT_DIS_CLR); + +// /* (clear) set pvid to 0 */ +// mtsw_r_clr(sc, MTSW_R_PPBV1(port), +// MTSW_R_PPBV1_G0_PORT_VID_MASK << +// MTSW_R_PPBV1_G0_PORT_VID_SHIFT); + } + + /* enable mib counters */ + mtsw_wr(sc, MTSW_R_MIBCCR, MTSW_R_MIBCCR_MIB_ENABLE | + MTSW_R_MIBCCR_TX_OCT_CNT_GOOD | MTSW_R_MIBCCR_TX_OCT_CNT_BAD | + MTSW_R_MIBCCR_RX_OCT_CNT_GOOD | MTSW_R_MIBCCR_RX_OCT_CNT_BAD); + + d = malloc(sizeof(*d), M_TEMP, M_WAITOK); + task_set(&d->d_task, mtsw_defer_task, d); + d->d_sc = sc; + + config_pending_incr(); + task_add(systq, &d->d_task); +} + +static void +mtsw_pll_init(struct mtsw_softc *sc, uint32_t sig_sr) +{ + uint32_t xtal, div; + uint32_t r; + + if (sc->sc_rev > 0) { + xtal = ISSET(sig_sr, MTSW_R_SIG_SR_PAD_MCM_SMI_EN) ? + MTSW_R_SWSTRAP_XTAL40 : MTSW_R_SWSTRAP_XTAL25; + } else + xtal = mtsw_rd(sc, MTSW_R_STRAP) & MTSW_R_SWSTRAP_XTAL; + + div = (xtal == MTSW_R_SWSTRAP_XTAL25) ? 0x140000 : 0x190000; + + /* 1. disable corepll */ + r = mtsw_rd(sc, MTSW_R_PLLGP_EN); + CLR(r, MTSW_R_PLLGP_EN_COREPLL_EN); + mtsw_wr(sc, MTSW_R_PLLGP_EN, r); + + /* 2. switch to xtal output */ + r = mtsw_rd(sc, MTSW_R_PLLGP_EN); + SET(r, MTSW_R_PLLGP_EN_SW_CLKSW); + mtsw_wr(sc, MTSW_R_PLLGP_EN, r); + + r = mtsw_rd(sc, MTSW_R_PLLGP_CR0); + CLR(r, MTSW_R_PLLGP_CR0_EN); + mtsw_wr(sc, MTSW_R_PLLGP_CR0, r); + + /* 3. disable pllgp and enable program pllgp */ + r = mtsw_rd(sc, MTSW_R_PLLGP_EN); + SET(r, MTSW_R_PLLGP_EN_SW_PLLGP); + mtsw_wr(sc, MTSW_R_PLLGP_EN, r); + + /* 4. program corepll output frequency to 500MHz */ + r = mtsw_rd(sc, MTSW_R_PLLGP_CR0); + CLR(r, + MTSW_R_PLLGP_CR0_POS_DIV_MASK << MTSW_R_PLLGP_CR0_POS_DIV_SHIFT); + SET(r, 2 << MTSW_R_PLLGP_CR0_POS_DIV_SHIFT); + mtsw_wr(sc, MTSW_R_PLLGP_CR0, r); + delay(25); + + r = mtsw_rd(sc, MTSW_R_PLLGP_CR0); + CLR(r, + MTSW_R_PLLGP_CR0_SDM_PCW_MASK << MTSW_R_PLLGP_CR0_SDM_PCW_SHIFT); + SET(r, div << MTSW_R_PLLGP_CR0_SDM_PCW_SHIFT); + mtsw_wr(sc, MTSW_R_PLLGP_CR0, r); + + /* update ratio */ + r = mtsw_rd(sc, MTSW_R_PLLGP_CR0); + SET(r, MTSW_R_PLLGP_CR0_SDM_PCW_CHG); + mtsw_wr(sc, MTSW_R_PLLGP_CR0, r); + delay(10); + + /* 5. clear update ratio */ + r = mtsw_rd(sc, MTSW_R_PLLGP_CR0); + CLR(r, MTSW_R_PLLGP_CR0_SDM_PCW_CHG); + mtsw_wr(sc, MTSW_R_PLLGP_CR0, r); + + /* enable 250SSC clock for rgmii */ + mtsw_wr(sc, MTSW_R_ANA_PLLGP_CR2, 0x4f40000); + /* enable 325M clock for sgmii */ + mtsw_wr(sc, MTSW_R_ANA_PLLGP_CR5, 0xad0000); + + /* 6. enable pll */ + r = mtsw_rd(sc, MTSW_R_PLLGP_CR0); + SET(r, MTSW_R_PLLGP_CR0_EN); + mtsw_wr(sc, MTSW_R_PLLGP_CR0, r); + + r = mtsw_rd(sc, MTSW_R_PLLGP_EN); + SET(r, MTSW_R_PLLGP_EN_COREPLL_EN); + mtsw_wr(sc, MTSW_R_PLLGP_EN, r); + + delay(25); +} + +static void +mtsw_defer_task(void *arg) +{ + struct mtsw_defer *d = arg; + struct mtsw_softc *sc = d->d_sc; + + free(d, M_TEMP, sizeof(*d)); + + mtsw_attached(sc); + + config_pending_decr(); +} + +struct mtsw_phy_map_entry { + const char *name; + unsigned int phy_mode; +}; + +static const struct mtsw_phy_map_entry mtsw_phy_map[] = { + { "rgmii", MTSW_PHY_RGMII }, + { "rgmii-txid", MTSW_PHY_RGMII_TXID }, + { "rgmii-rxid", MTSW_PHY_RGMII_RXID }, + { "rgmii-id", MTSW_PHY_RGMII_ID }, + { "sgmii", MTSW_PHY_SGMII }, +}; + +static unsigned int +mtsw_phy_mode(struct mtsw_softc *sc, int node) +{ + char phy_mode[32]; + size_t i; + + if (OF_getpropstr(node, "phy-mode", phy_mode, sizeof(phy_mode)) <= 0) + return (0); + + for (i = 0; i < nitems(mtsw_phy_map); i++) { + const struct mtsw_phy_map_entry *e = &mtsw_phy_map[i]; + if (strcmp(phy_mode, e->name) == 0) + return (e->phy_mode); + } + + return (MTSW_PHY_UNKNOWN); +} + +static void +mtsw_rgmii(struct mtsw_softc *sc, unsigned int phy_mode) +{ + uint32_t r; + + r = mtsw_rd(sc, MTSW_R_CLKGEN_CTRL); + SET(r, MTSW_R_CLKGEN_CTRL_EN); + + CLR(r, MTSW_R_CLKGEN_CTRL_MODE_MASK); + SET(r, MTSW_R_CLKGEN_CTRL_MODE_RGMII); + + CLR(r, MTSW_R_CLKGEN_CTRL_SKEW_IN_MASK); + SET(r, MTSW_R_CLKGEN_CTRL_SKEW_IN_NO_CHANGE); + CLR(r, MTSW_R_CLKGEN_CTRL_SKEW_OUT_MASK); + SET(r, MTSW_R_CLKGEN_CTRL_SKEW_OUT_NO_CHANGE); + + /* this seems round the wrong way */ + if (ISSET(phy_mode, MTSW_PHY_RGMII_TXID)) + CLR(r, MTSW_R_CLKGEN_CTRL_NO_DELAY); + else + SET(r, MTSW_R_CLKGEN_CTRL_NO_DELAY); + + /* this seems round the wrong way */ + if (ISSET(phy_mode, MTSW_PHY_RGMII_RXID)) + CLR(r, MTSW_R_CLKGEN_CTRL_NO_REVERSE); + else + SET(r, MTSW_R_CLKGEN_CTRL_NO_REVERSE); + + printf("%s: phy_mode %x CLKGEN_CTRL %08x\n", DEVNAME(sc), phy_mode, r); + + mtsw_wr(sc, MTSW_R_CLKGEN_CTRL, r); +} + +static void +mtsw_attached(struct mtsw_softc *sc) +{ + int pnode, node, port; + uint32_t pmask = 0; + uint32_t vt; + struct mtsw_cpu_port *p; + uint32_t r; + int rv; + + pnode = OF_getnodebyname(sc->sc_node, "ports"); + if (pnode == 0) { + printf("%s: no ports property\n", DEVNAME(sc)); + return; + }; + + for (node = OF_child(pnode); node != 0; node = OF_peer(node)) { + struct mtsw_attach_args maa; + char name[32]; + char status[16]; + uint32_t phandle; + struct device *child; + unsigned int phy_mode; + + port = OF_getpropint(node, "reg", -1); + if (port < MTSW_PORT_MIN || port > MTSW_PORT_MAX) + continue; + + if (OF_getpropstr(node, "status", status, sizeof(status)) > 0 && + strcmp(status, "disabled") == 0) + continue; + + if (ISSET(pmask, 1 << port)) { + printf("%s: port %u is already used\n", + DEVNAME(sc), port); + continue; + } + SET(pmask, 1 << port); + + phy_mode = mtsw_phy_mode(sc, node); + + switch (port) { + case 5: + if (ISSET(phy_mode, MTSW_PHY_RGMII)) { + mtsw_rgmii(sc, phy_mode); + break; + } + /* FALLTHROUGH */ + case 6: + if (!ISSET(phy_mode, MTSW_PHY_SGMII)) { + printf("%s: unsupported phy-mode on port %d\n", + DEVNAME(sc), port); + break; + } + break; + default: +#if 0 + if (phy_mode != 0) { + printf("%s: phy-mode specified on port %d\n", + DEVNAME(sc), port); + } +#endif + break; + } + + mtsw_r_clr(sc, MTSW_R_PMCR(port), + MTSW_R_PMCR_FORCE_LINK_MASK | + MTSW_R_PMCR_FORCE_DPX_MASK | + MTSW_R_PMCR_FORCE_SPD_MASK | + MTSW_R_PMCR_FORCE_TX_FC | + MTSW_R_PMCR_FORCE_RX_FC | + MTSW_R_PMCR_FORCE_EEE100 | + MTSW_R_PMCR_FORCE_EEE1G | + MTSW_R_PMCR_MAC_RX_EN | MTSW_R_PMCR_MAC_TX_EN); + + OF_getpropstr(node, "name", name, sizeof(name)); + memset(&maa, 0, sizeof(maa)); + maa.maa_name = name; + maa.maa_node = node; + maa.maa_port = port; + + phandle = OF_getpropint(node, "ethernet", 0); + if (phandle != 0) { + mtsw_attach_cpu(sc, &maa, phandle); + continue; + } + + child = config_found(&sc->sc_dev, &maa, mtsw_print); + if (child == NULL) + continue; + + sc->sc_ports[port] = (struct mtsport_softc *)child; + +#if NKSTAT > 0 + mtsw_kstat_attach_mib(sc, port, child->dv_xname); +#endif + } + + /* we know where the CPU ports are now */ + r = mtsw_rd(sc, 0x4); + CLR(r, 0xff); + SET(r, sc->sc_cpu_pmask); + mtsw_wr(sc, 0x4, r); + + /* add all these ports to vlan 0 */ + vt = MTSW_VT_VALID | MTSW_VT_FID_BRIDGED | + MTSW_VT_EG_CON | MTSW_VT_IVL_MAC; + SET(vt, pmask << MTSW_VT_PORT_MEM_SHIFT); + mtsw_wr(sc, MTSW_R_VAWD1, vt); + mtsw_wr(sc, MTSW_R_VAWD2, 0); + rv = mtsw_vt_control(sc, MTSW_R_VTCR_FUNC_WRITE_VID, 0); + if (rv != 0) { + printf("%s: vlan 0 table entry add failed (%d)\n", + DEVNAME(sc), rv); + } + + rv = mtsw_at_control(sc, MTSW_R_ATC_AC_CMD_CLEAN); + if (rv != 0) { + printf("%s: address table clean failed (%d)\n", + DEVNAME(sc), rv); + } + + CLR(pmask, sc->sc_cpu_pmask); + + TAILQ_FOREACH(p, &sc->sc_cpu_ports, p_entry) { + r = mtsw_rd(sc, MTSW_R_PCR(p->p_port)); + CLR(r, MTSW_R_PCR_PORT_MATRIX_MASK << + MTSW_R_PCR_PORT_MATRIX_SHIFT); + SET(r, pmask << MTSW_R_PCR_PORT_MATRIX_SHIFT); + mtsw_wr(sc, MTSW_R_PCR(p->p_port), r); + + /* enable the magic headers */ + mtsw_wr(sc, MTSW_R_PVC(p->p_port), MTSW_R_PVC_PORT_STAG); + + mtsw_r_set(sc, MTSW_R_PMCR(p->p_port), + MTSW_R_PMCR_MAC_RX_EN | MTSW_R_PMCR_MAC_TX_EN | + MTSW_R_PMCR_FORCE_MODE_LNK | MTSW_R_PMCR_FORCE_LINK_UP | + MTSW_R_PMCR_FORCE_MODE_SPD | MTSW_R_PMCR_FORCE_SPD_1000M | + MTSW_R_PMCR_FORCE_MODE_DPX | MTSW_R_PMCR_FORCE_DPX_FULL); + } +} + +static void +mtsw_attach_cpu(struct mtsw_softc *sc, const struct mtsw_attach_args *maa, + uint32_t phandle) +{ + struct mtsw_cpu_port *p; + struct ifnet *ifp0; + struct arpcom *ac0; + + ifp0 = if_byphandle(phandle); + if (ifp0 == NULL) { + printf("%s: cannot find cpu interface on port %u\n", + DEVNAME(sc), maa->maa_port); + return; + } + + if (ifp0->if_type != IFT_ETHER) { + printf("%s: unsupported type of cpu interface on port %u\n", + DEVNAME(sc), maa->maa_port); + return; + } + + printf("%s: %s at port %u\n", DEVNAME(sc), ifp0->if_xname, + maa->maa_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|M_ZERO); + + p->p_port = maa->maa_port; + p->p_ifp0 = ifp0; + p->p_sc = sc; + + p->p_ioctl = ifp0->if_ioctl; + p->p_input = ifp0->if_input; + p->p_output = ifp0->if_output; + + 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 = mtsw_p_ioctl; + ifp0->if_input = mtsw_p_input; + ifp0->if_output = mtsw_p_output; + NET_UNLOCK(); + + TAILQ_INSERT_TAIL(&sc->sc_cpu_ports, p, p_entry); + SET(sc->sc_cpu_pmask, 1 << maa->maa_port); + +#if NKSTAT > 0 + mtsw_kstat_attach_mib(sc, maa->maa_port, ifp0->if_xname); +#endif +} + +static int +mtsw_print(void *aux, const char *pnp) +{ + struct mtsw_attach_args *maa = aux; + + if (pnp != NULL) + printf("\"%s\" at %s", maa->maa_name, pnp); + + printf(" port %d", maa->maa_port); + + return (UNCONF); +} + +static uint32_t +mtsw_rd(struct mtsw_softc *sc, uint16_t reg) +{ + struct mii_bus *md = sc->sc_md; + uint16_t page, r; + uint32_t hi, lo; + + page = (reg >> MTSW_MDIO_PAGE_SHIFT) & MTSW_MDIO_PAGE_MASK; + r = (reg >> MTSW_MDIO_REG_SHIFT) & MTSW_MDIO_REG_MASK; + + mtx_enter(&sc->sc_reg_mtx); + if (sc->sc_reg_page != page) { + /* move the chip to the right page */ + md->md_writereg(md->md_cookie, sc->sc_addr, + MTSW_MDIO_PAGE_REG, page); + sc->sc_reg_page = page; + } + + lo = md->md_readreg(md->md_cookie, sc->sc_addr, r) & 0xffff; + hi = md->md_readreg(md->md_cookie, sc->sc_addr, 0x10) & 0xffff; + mtx_leave(&sc->sc_reg_mtx); + + return ((hi << 16) | lo); +} + +static void +mtsw_wr(struct mtsw_softc *sc, uint16_t reg, uint32_t val) +{ + struct mii_bus *md = sc->sc_md; + uint16_t page, r; + uint32_t hi, lo; + + page = (reg >> MTSW_MDIO_PAGE_SHIFT) & MTSW_MDIO_PAGE_MASK; + r = (reg >> MTSW_MDIO_REG_SHIFT) & MTSW_MDIO_REG_MASK; + + hi = val >> 16; + lo = val & 0xffff; + + mtx_enter(&sc->sc_reg_mtx); + if (sc->sc_reg_page != page) { + /* move the chip to the right page */ + md->md_writereg(md->md_cookie, sc->sc_addr, + MTSW_MDIO_PAGE_REG, page); + sc->sc_reg_page = page; + } + + md->md_writereg(md->md_cookie, sc->sc_addr, r, lo); + md->md_writereg(md->md_cookie, sc->sc_addr, 0x10, hi); + mtx_leave(&sc->sc_reg_mtx); +} + +static void +mtsw_r_set(struct mtsw_softc *sc, uint16_t reg, uint32_t bits) +{ + uint32_t r; + + r = mtsw_rd(sc, reg); + SET(r, bits); + mtsw_wr(sc, reg, r); +} + +static void +mtsw_r_clr(struct mtsw_softc *sc, uint16_t reg, uint32_t bits) +{ + uint32_t r; + + r = mtsw_rd(sc, reg); + CLR(r, bits); + mtsw_wr(sc, reg, r); +} + +static uint32_t +mtsw_mii_piac(struct mtsw_softc *sc) +{ + uint32_t r; + int n; + + for (n = 0; n < 5000; n++) { + r = mtsw_rd(sc, MTSW_R_PHY_IAC); + if (!ISSET(r, MTSW_R_PHY_IAC_PHY_ACS_ST)) + break; + + delay(20); + } + + return (r); +} + +static int +mtsw_mii_rd(struct mtsw_softc *sc, int phy, int reg) +{ + int rv = -1; + uint32_t r; + + mtx_enter(&sc->sc_mii_mtx); + r = mtsw_mii_piac(sc); + if (ISSET(r, MTSW_R_PHY_IAC_PHY_ACS_ST)) + goto leave; + + mtsw_wr(sc, MTSW_R_PHY_IAC, MTSW_R_PHY_IAC_MDIO_ST_C22 | + MTSW_R_PHY_IAC_MDIO_CMD_READ | + MTSW_R_PHY_IAC_MDIO_PHY_ADDR(phy) | + MTSW_R_PHY_IAC_MDIO_REG_ADDR(reg) | + MTSW_R_PHY_IAC_PHY_ACS_ST); + + r = mtsw_mii_piac(sc); + if (ISSET(r, MTSW_R_PHY_IAC_PHY_ACS_ST)) + goto leave; + + rv = (r >> MTSW_R_PHY_IAC_MDIO_DATA_SHIFT) & + MTSW_R_PHY_IAC_MDIO_DATA_MASK; +leave: + mtx_leave(&sc->sc_mii_mtx); + + return (rv); +} + +static void +mtsw_mii_wr(struct mtsw_softc *sc, int phy, int reg, int val) +{ + uint32_t r; + + mtx_enter(&sc->sc_mii_mtx); + r = mtsw_mii_piac(sc); + if (ISSET(r, MTSW_R_PHY_IAC_PHY_ACS_ST)) + goto leave; + + r = (val & MTSW_R_PHY_IAC_MDIO_DATA_MASK) << + MTSW_R_PHY_IAC_MDIO_DATA_SHIFT; + + mtsw_wr(sc, MTSW_R_PHY_IAC, r | MTSW_R_PHY_IAC_MDIO_ST_C22 | + MTSW_R_PHY_IAC_MDIO_CMD_WRITE | + MTSW_R_PHY_IAC_MDIO_REG_ADDR(reg) | + MTSW_R_PHY_IAC_MDIO_PHY_ADDR(phy) | + MTSW_R_PHY_IAC_PHY_ACS_ST); + + (void)mtsw_mii_piac(sc); /* XXX do we have to wait for this? */ +leave: + mtx_leave(&sc->sc_mii_mtx); +} + +static int +mtsw_at_control(struct mtsw_softc *sc, uint32_t cmd) +{ + unsigned int t; + + SET(cmd, MTSW_R_ATC_BUSY | MTSW_R_ATC_AC_MAT_ALL_MAC); + mtsw_wr(sc, MTSW_R_ATC, cmd); + + for (t = 0; t < 1000; t++) { + uint32_t r = mtsw_rd(sc, MTSW_R_ATC); + if (!ISSET(r, MTSW_R_ATC_BUSY)) + return (0); + + delay(200); + } + + return (ETIMEDOUT); +} + +static int +mtsw_vt_control(struct mtsw_softc *sc, uint32_t cmd, uint32_t vlan) +{ + unsigned int t; + + SET(cmd, vlan << MTSW_R_VTCR_VID_SHIFT); + SET(cmd, MTSW_R_VTCR_BUSY); + + mtsw_wr(sc, MTSW_R_VTCR, cmd); + + for (t = 0; t < 1000; t++) { + uint32_t r = mtsw_rd(sc, MTSW_R_VTCR); + if (!ISSET(r, MTSW_R_VTCR_BUSY)) { + if (ISSET(r, MTSW_R_VTCR_IDX_INVLD)) + return (EINVAL); + + return (0); + } + + delay(200); + } + + return (ETIMEDOUT); +} + +static int +mtsw_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mtsw_cpu_port *p = ac0->ac_trunkport; + int error = 0; + + switch (cmd) { + case SIOCGTRUNKPORT: { + struct trunk_reqport *rp = (struct trunk_reqport *)data; + struct mtsw_softc *sc = p->p_sc; + + 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 +mtsw_p_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mtsw_cpu_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 +mtsw_p_input(struct ifnet *ifp0, struct mbuf *m) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mtsw_cpu_port *p = ac0->ac_trunkport; + struct mtsw_softc *sc = p->p_sc; + struct ether_header *eh; + int hlen = sizeof(*eh) + sizeof(uint16_t); + int diff = hlen - offsetof(struct ether_header, ether_type); + uint16_t shim; + struct mtsport_softc *psc; + int port; + + if (m->m_len < hlen) { + m = m_pullup(m, hlen); + if (m == NULL) { + /* drop++ */ + return; + } + } + eh = mtod(m, struct ether_header *); + + shim = bemtoh16(&eh->ether_type); + + port = shim & 0x7; + if (port >= nitems(sc->sc_ports)) + 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); + + mtsport_input(psc, m); + return; + +drop: + m_freem(m); +} + +/* + * mtsw port driver + */ + +struct mtsport_softc { + struct device psc_dev; + int psc_node; + int psc_port; + + struct arpcom psc_ac; +#define psc_if psc_ac.ac_if + + struct mtsw_softc *psc_parent; + struct mii_data psc_mii; +#define psc_ifmedia psc_mii.mii_media + + struct if_device psc_ifd; +}; +#define PDEVNAME(_psc) ((_psc)->psc_dev.dv_xname) + +static int mtsport_match(struct device *, void *, void *); +static void mtsport_attach(struct device *, struct device *, + void *); + +static void mtsport_mii_attach(struct mtsport_softc *); + +static int mtsport_ioctl(struct ifnet *, u_long, caddr_t); +static void mtsport_start(struct ifqueue *); + +static int mtsport_up(struct mtsport_softc *sc); +static int mtsport_down(struct mtsport_softc *sc); + +static void mtsport_mii_attach(struct mtsport_softc *); +static int mtsport_miibus_readreg(struct device *, int, int); +static void mtsport_miibus_writereg(struct device *, int, int, int); +static void mtsport_miibus_statchg(struct device *); + +static int mtsport_media_upd(struct ifnet *); +static void mtsport_media_sts(struct ifnet *, struct ifmediareq *); + +const struct cfattach mtsport_ca = { + sizeof(struct mtsport_softc), mtsport_match, mtsport_attach +}; + +struct cfdriver mtsport_cd = { + NULL, "mtsport", DV_DULL +}; + +static int +mtsport_match(struct device *parent, void *match, void *aux) +{ + return (1); +} + +static void +mtsport_attach(struct device *parent, struct device *self, void *aux) +{ + struct mtsport_softc *psc = (struct mtsport_softc *)self; + struct mtsw_attach_args *maa = aux; + struct mii_data *mii = &psc->psc_mii; + struct ifnet *ifp; + uint32_t phandle; + uint16_t bmcr; + + psc->psc_node = maa->maa_node; + psc->psc_port = maa->maa_port; + psc->psc_parent = (struct mtsw_softc *)parent; + + LIST_INIT(&mii->mii_phys); + + ifp = &psc->psc_if; + (void)strlcpy(ifp->if_xname, PDEVNAME(psc), sizeof(ifp->if_xname)); + ifp->if_softc = psc; + ifp->if_ioctl = mtsport_ioctl; + ifp->if_qstart = mtsport_start; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST; + ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; + + if (OF_getprop(psc->psc_node, "local-mac-address", + &psc->psc_ac.ac_enaddr, sizeof(psc->psc_ac.ac_enaddr)) != + sizeof(psc->psc_ac.ac_enaddr)) + ether_fakeaddr(ifp); + + printf(": address %s\n", ether_sprintf(psc->psc_ac.ac_enaddr)); + + switch (psc->psc_port) { + case 5: + phandle = OF_getpropint(psc->psc_node, + "phy-handle", 0); + if (phandle != 0) + printf("%s: XXX external phy?\n", PDEVNAME(psc)); + break; + case 6: + printf("%s: sgmii?\n", PDEVNAME(psc)); + break; + default: + /* turn the phy back on since we're going to use it */ + bmcr = mtsw_mii_rd(psc->psc_parent, psc->psc_port, MII_BMCR); + CLR(bmcr, BMCR_PDOWN); + mtsw_mii_wr(psc->psc_parent, psc->psc_port, MII_BMCR, bmcr); + + mii->mii_ifp = ifp; + mii->mii_readreg = mtsport_miibus_readreg; + mii->mii_writereg = mtsport_miibus_writereg; + mii->mii_statchg = mtsport_miibus_statchg; + + mtsport_mii_attach(psc); + } + + if (LIST_FIRST(&mii->mii_phys) == NULL) { + printf("%s: no PHY found!\n", PDEVNAME(psc)); + ifmedia_add(&psc->psc_ifmedia, IFM_ETHER|IFM_MANUAL, + 0, NULL); + ifmedia_set(&psc->psc_ifmedia, IFM_ETHER|IFM_MANUAL); + } else + ifmedia_set(&psc->psc_ifmedia, IFM_ETHER|IFM_AUTO); + + if_counters_alloc(ifp); + if_attach(ifp); + ether_ifattach(ifp); + + OF_getpropstr(psc->psc_node, "label", + ifp->if_description, sizeof(ifp->if_description)); + + psc->psc_ifd.if_node = psc->psc_node; + psc->psc_ifd.if_ifp = ifp; + if_register(&psc->psc_ifd); +} + +static void +mtsport_mii_attach(struct mtsport_softc *psc) +{ + struct mii_data *mii = &psc->psc_mii; + + ifmedia_init(&psc->psc_ifmedia, 0, + mtsport_media_upd, mtsport_media_sts); + + mii_attach(&psc->psc_dev, mii, 0xffffffff, + psc->psc_port, MII_OFFSET_ANY, 0); +} + +static int +mtsport_miibus_readreg(struct device *dev, int phy, int reg) +{ + struct device *parent = dev->dv_parent; + struct mtsw_softc *sc = (struct mtsw_softc *)parent; + + return (mtsw_mii_rd(sc, phy, reg)); +} + +static void +mtsport_miibus_writereg(struct device *dev, int phy, int reg, int val) +{ + struct device *parent = dev->dv_parent; + struct mtsw_softc *sc = (struct mtsw_softc *)parent; + + return (mtsw_mii_wr(sc, phy, reg, val)); +} + +static void +mtsport_miibus_statchg(struct device *dev) +{ + printf("%s: %s[%u]\n", dev->dv_xname, __func__, __LINE__); +} + +static int +mtsport_media_upd(struct ifnet *ifp) +{ + struct mtsport_softc *psc = ifp->if_softc; + + if (LIST_FIRST(&psc->psc_mii.mii_phys)) + mii_mediachg(&psc->psc_mii); + + return (0); +} + +static void +mtsport_media_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct mtsport_softc *psc = ifp->if_softc; + + if (LIST_FIRST(&psc->psc_mii.mii_phys)) { + mii_pollstat(&psc->psc_mii); + ifmr->ifm_active = psc->psc_mii.mii_media_active; + ifmr->ifm_status = psc->psc_mii.mii_media_status; + } +} + +static int +mtsport_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct mtsport_softc *psc = 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 = mtsport_up(psc); + } else { + if (ISSET(ifp->if_flags, IFF_RUNNING)) + error = mtsport_down(psc); + } + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &psc->psc_ifmedia, cmd); + break; + + default: + error = ether_ioctl(ifp, &psc->psc_ac, cmd, data); + break; + } + + if (error == ENETRESET) { + /* hardware doesnt need reprogramming */ + error = 0; + } + + return (error); +} + +static int +mtsport_up(struct mtsport_softc *psc) +{ + struct ifnet *ifp = &psc->psc_if; + struct mtsw_softc *sc = psc->psc_parent; + uint32_t r; + + r = mtsw_rd(sc, MTSW_R_PCR(psc->psc_port)); + CLR(r, MTSW_R_PCR_PORT_MATRIX_MASK << MTSW_R_PCR_PORT_MATRIX_SHIFT); + SET(r, sc->sc_cpu_pmask << MTSW_R_PCR_PORT_MATRIX_SHIFT); + mtsw_wr(sc, MTSW_R_PCR(psc->psc_port), r); + + mtsw_r_set(sc, MTSW_R_PMCR(psc->psc_port), + MTSW_R_PMCR_MAC_RX_EN | MTSW_R_PMCR_MAC_TX_EN); + + SET(ifp->if_flags, IFF_RUNNING); + + return (0); +} + +static int +mtsport_down(struct mtsport_softc *psc) +{ + struct ifnet *ifp = &psc->psc_if; + struct mtsw_softc *sc = psc->psc_parent; + uint32_t r; + + CLR(ifp->if_flags, IFF_RUNNING); + + mtsw_r_clr(sc, MTSW_R_PMCR(psc->psc_port), + MTSW_R_PMCR_MAC_RX_EN | MTSW_R_PMCR_MAC_TX_EN); + + r = mtsw_rd(sc, MTSW_R_PCR(psc->psc_port)); + CLR(r, MTSW_R_PCR_PORT_MATRIX_MASK << MTSW_R_PCR_PORT_MATRIX_SHIFT); + mtsw_wr(sc, MTSW_R_PCR(psc->psc_port), r); + + return (0); +} + +static void +mtsport_start(struct ifqueue *ifq) +{ + struct ifnet *ifp = ifq->ifq_if; + struct mtsport_softc *psc = ifp->if_softc; + struct mbuf *m; + struct ether_header *eh; + uint16_t shim; + uint16_t *pad; + const int hlen = sizeof(*eh) + sizeof(*pad); + const int offs = offsetof(struct ether_header, ether_type); + const int diff = hlen - offs; + int errors = 0; + + struct mtsw_softc *sc = psc->psc_parent; + struct mtsw_cpu_port *p = TAILQ_FIRST(&sc->sc_cpu_ports); + + 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 *); + + shim = 1 << psc->psc_port; + htobem16(&eh->ether_type, shim); + + pad = (uint16_t *)(eh + 1); + *pad = htons(0); + + if (if_enqueue(p->p_ifp0, m) != 0) + errors++; + } + + if (errors) + counters_add(ifp->if_counters, ifc_oerrors, errors); +} + +static inline void +mtsport_input(struct mtsport_softc *psc, struct mbuf *m) +{ + if_vinput(&psc->psc_if, m); +} + +#if NKSTAT > 0 + +struct mtsw_mib_kstat { + unsigned int mib_addr; + enum kstat_kv_type mib_type; + enum kstat_kv_unit mib_unit; + const char *mib_name; +}; + +static const struct mtsw_mib_kstat mtsw_mib_kstats[] = { + { 0x00, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx drops" }, + { 0x04, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx crc" }, + { 0x08, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx unicast" }, + { 0x0c, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx multicast" }, + { 0x10, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx broadcast" }, + { 0x14, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_NONE, "tx collisions" }, + { 0x18, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_NONE, "tx single colls" }, + { 0x1c, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_NONE, "tx multi colls" }, + { 0x20, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_NONE, "tx deferred" }, + { 0x24, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_NONE, "tx late colls" }, + { 0x28, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_NONE, "tx excess colls" }, + { 0x2c, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx pause" }, + { 0x30, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx 64B" }, + { 0x34, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx 65B" }, + { 0x38, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx 128B" }, + { 0x3c, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx 256B" }, + { 0x40, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx 512B" }, + { 0x44, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "tx 1024B" }, + { 0x48, KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES, "tx" }, + + { 0x60, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx drops" }, + { 0x64, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx filtering" }, + { 0x68, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx unicast" }, + { 0x6c, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx multicast" }, + { 0x70, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx broadcast" }, + { 0x74, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx align err" }, + { 0x78, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx crc" }, + { 0x7c, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx undersize" }, + { 0x80, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx fragment" }, + { 0x84, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx oversize" }, + { 0x88, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx jabber" }, + { 0x90, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx 64B" }, + { 0x94, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx 65B" }, + { 0x98, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx 128B" }, + { 0x9c, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx 256B" }, + { 0xa0, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx 512B" }, + { 0xa4, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx 1024B" }, + { 0xa8, KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES, "rx" }, + + { 0xb0, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx ctrl drop" }, + { 0xb4, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx ingress drop" }, + { 0xb8, KSTAT_KV_T_COUNTER32, KSTAT_KV_U_PACKETS, "rx arl drop" }, +}; + +static uint64_t +mtsw_rd_c64(struct mtsw_softc *sc, uint16_t r) +{ + uint32_t lo, hi, ok; + + hi = mtsw_rd(sc, r + 4); + for (;;) { + lo = mtsw_rd(sc, r); + ok = mtsw_rd(sc, r + 4); + if (hi == ok) + break; + + hi = ok; + } + + return ((uint64_t)hi << 32) | (uint64_t)lo; +} + +static int +mtsw_kstat_read_mib(struct kstat *ks) +{ + struct mtsw_softc *sc = ks->ks_softc; + struct kstat_kv *kvs = ks->ks_data; + int port = (int)(intptr_t)ks->ks_ptr; + size_t i; + struct timespec now, diff; + + /* avoid hitting the mdio bus too hard */ + getnanouptime(&now); + timespecsub(&now, &ks->ks_updated, &diff); + if (timespeccmp(&diff, &ks->ks_interval, <)) + return (0); + + for (i = 0; i < nitems(mtsw_mib_kstats); i++) { + const struct mtsw_mib_kstat *mib = &mtsw_mib_kstats[i]; + struct kstat_kv *kv = &kvs[i]; + uint16_t r = MTSW_R_MIB_BASE(port) + mib->mib_addr; + + if (mib->mib_type == KSTAT_KV_T_COUNTER32) + kstat_kv_u32(kv) = mtsw_rd(sc, r); + else + kstat_kv_u64(kv) = mtsw_rd_c64(sc, r); + } + + ks->ks_updated = now; + return (0); +} + +static void +mtsw_kstat_attach_mib(struct mtsw_softc *sc, int port, const char *ifname) +{ + static const struct timespec ival = { 0, 500000000 }; + struct kstat *ks; + struct kstat_kv *kvs; + size_t i; + + ks = kstat_create(ifname, 0, "mtsw-mib", 0, KSTAT_T_KV, 0); + if (ks == NULL) + return; + + kvs = mallocarray(nitems(mtsw_mib_kstats), sizeof(*kvs), M_DEVBUF, + M_WAITOK|M_ZERO); + + for (i = 0; i < nitems(mtsw_mib_kstats); i++) { + const struct mtsw_mib_kstat *mib = &mtsw_mib_kstats[i]; + + kstat_kv_unit_init(&kvs[i], mib->mib_name, + mib->mib_type, mib->mib_unit); + } + + kstat_set_wlock(ks, &sc->sc_kstat_lock); + ks->ks_softc = sc; + ks->ks_ptr = (void *)(intptr_t)port; + ks->ks_data = kvs; + ks->ks_datalen = nitems(mtsw_mib_kstats) * sizeof(*kvs); + ks->ks_read = mtsw_kstat_read_mib; + ks->ks_interval = ival; + + kstat_install(ks); +} + +#endif /* NKSTAT > 0 */ Index: fdt/mvmdio.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/mvmdio.c,v retrieving revision 1.4 diff -u -p -r1.4 mvmdio.c --- fdt/mvmdio.c 24 Oct 2021 17:52:26 -0000 1.4 +++ fdt/mvmdio.c 24 Apr 2023 02:08:20 -0000 @@ -36,12 +36,6 @@ #include #include -#ifdef __armv7__ -#include -#else -#include -#endif - #include #include #include @@ -49,6 +43,7 @@ #include #include +#include #include @@ -58,7 +53,7 @@ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) struct mvmdio_softc { - struct simplebus_softc sc_sbus; + struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; @@ -118,7 +113,7 @@ mvmdio_attach(struct device *parent, str sc->sc_mii.md_writereg = mvmdio_smi_writereg; mii_register(&sc->sc_mii); - simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa); + mdio_attach(self, &sc->sc_mii, faa->fa_node); } int @@ -136,7 +131,7 @@ mvmdio_smi_readreg(struct device *dev, i break; } if (i == MVNETA_PHY_TIMEOUT) { - printf("%s: SMI busy timeout\n", sc->sc_sbus.sc_dev.dv_xname); + printf("%s: SMI busy timeout\n", sc->sc_dev.dv_xname); mtx_leave(&sc->sc_mtx); return -1; } @@ -174,7 +169,7 @@ mvmdio_smi_writereg(struct device *dev, break; } if (i == MVNETA_PHY_TIMEOUT) { - printf("%s: SMI busy timeout\n", sc->sc_sbus.sc_dev.dv_xname); + printf("%s: SMI busy timeout\n", sc->sc_dev.dv_xname); mtx_leave(&sc->sc_mtx); return; } @@ -192,5 +187,5 @@ mvmdio_smi_writereg(struct device *dev, mtx_leave(&sc->sc_mtx); if (i == MVNETA_PHY_TIMEOUT) - printf("%s: phy write timed out\n", sc->sc_sbus.sc_dev.dv_xname); + printf("%s: phy write timed out\n", sc->sc_dev.dv_xname); } Index: fdt/mvsw.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/mvsw.c,v retrieving revision 1.5 diff -u -p -r1.5 mvsw.c --- fdt/mvsw.c 6 Apr 2022 18:59:28 -0000 1.5 +++ fdt/mvsw.c 24 Apr 2023 02:08:21 -0000 @@ -15,10 +15,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "bpfilter.h" + #include #include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include @@ -26,8 +35,33 @@ #include #include #include +#include + +#include +#include +#include #include +#include + +#include +#include +#include + +#include +#include + +#include /* if_trunk.h uses siphash bits */ +#include + +#if NBPFILTER > 0 +#include +#endif + +#define MVSW_MAX_PORTS 11 + +#define ETHERTYPE_MVSW_ETAG ETHERTYPE_802_EX1 +#define ETHERTYPE_MVSW_DEFAULT 0x9000 /* who cares? */ /* Registers */ @@ -49,34 +83,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 */ #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 +391,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,59 +418,181 @@ 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); int mvsw_match(struct device *parent, void *match, void *aux) { - struct fdt_attach_args *faa = aux; + struct mdio_attach_args *maa = aux; - return OF_is_compatible(faa->fa_node, "marvell,mv88e6085"); + return OF_is_compatible(maa->maa_node, "marvell,mv88e6085"); } void mvsw_attach(struct device *parent, struct device *self, void *aux) { struct mvsw_softc *sc = (struct mvsw_softc *)self; - struct fdt_attach_args *faa = aux; - int ports, port, node; - uint32_t phy; - uint16_t swid; - - if (faa->fa_nreg < 1) { - printf(": no registers\n"); - return; - } - - sc->sc_reg = faa->fa_reg[0].addr; - printf(" phy %d", sc->sc_reg); + struct mdio_attach_args *maa = aux; + uint16_t r; + struct mvsw_defer *d; + + TAILQ_INIT(&sc->sc_cpus); + sc->sc_nports = nitems(sc->sc_ports); + + sc->sc_node = maa->maa_node; + sc->sc_reg = maa->maa_addr; + sc->sc_mdio = maa->maa_bus; - sc->sc_mdio = mii_bynode(OF_parent(faa->fa_node)); - if (sc->sc_mdio == NULL) { - printf(": can't find mdio bus\n"); - 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 +601,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 +930,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 +1066,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 +1098,358 @@ 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) { +#ifdef notyet + int phyloc = OF_getpropint(phynode, "reg", 0); +#endif + 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); + +#ifdef notyet + mii_attach(self, mii, 0xffffffff, phyloc, MII_OFFSET_ANY, 0); +#endif + if (LIST_FIRST(&mii->mii_phys) == NULL) { +#ifdef notyet + printf("%s: no PHY found!\n", DEVNAME(sc)); +#endif + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_MANUAL, + 0, NULL); + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_MANUAL); + } else + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO); + } + + if_counters_alloc(ifp); + if_attach(ifp); + ether_ifattach(ifp); + + sc->sc_ifd.if_node = sc->sc_node; + sc->sc_ifd.if_ifp = ifp; + if_register(&sc->sc_ifd); + + /* Configure port. */ + r = 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: ic/dwqe.c =================================================================== RCS file: /cvs/src/sys/dev/ic/dwqe.c,v retrieving revision 1.8 diff -u -p -r1.8 dwqe.c --- ic/dwqe.c 24 Apr 2023 01:33:32 -0000 1.8 +++ ic/dwqe.c 24 Apr 2023 02:08:21 -0000 @@ -1,4 +1,4 @@ -/* $OpenBSD: dwqe.c,v 1.8 2023/04/24 01:33:32 dlg Exp $ */ +/* $OpenBSD: dwqe.c,v 1.4 2023/04/07 08:53:03 kettenis Exp $ */ /* * Copyright (c) 2008, 2019 Mark Kettenis * Copyright (c) 2017, 2022 Patrick Wildt @@ -75,14 +75,11 @@ int dwqe_media_change(struct ifnet *); void dwqe_media_status(struct ifnet *, struct ifmediareq *); void dwqe_mii_attach(struct dwqe_softc *); -int dwqe_mii_readreg(struct device *, int, int); -void dwqe_mii_writereg(struct device *, int, int, int); -void dwqe_mii_statchg(struct device *); void dwqe_lladdr_read(struct dwqe_softc *, uint8_t *); void dwqe_lladdr_write(struct dwqe_softc *); -void dwqe_tick(void *); +void dwqe_mii_tick(void *); void dwqe_rxtick(void *); int dwqe_intr(void *); @@ -116,7 +113,7 @@ dwqe_attach(struct dwqe_softc *sc) for (i = 0; i < 4; i++) sc->sc_hw_feature[i] = dwqe_read(sc, GMAC_MAC_HW_FEATURE(i)); - timeout_set(&sc->sc_phy_tick, dwqe_tick, sc); + mtx_init(&sc->sc_mii_mtx, IPL_NET); timeout_set(&sc->sc_rxto, dwqe_rxtick, sc); ifp = &sc->sc_ac.ac_if; @@ -213,7 +203,9 @@ dwqe_attach(struct dwqe_softc *sc) dwqe_write(sc, GMAC_SYS_BUS_MODE, mode); } - if (!sc->sc_fixed_link) + if (sc->sc_fixed_link) + dwqe_mii_statchg(&sc->sc_dev); + else dwqe_mii_attach(sc); if_attach(ifp); @@ -441,8 +442,11 @@ int dwqe_mii_readreg(struct device *self, int phy, int reg) { struct dwqe_softc *sc = (void *)self; + int rv = 0; int n; + mtx_enter(&sc->sc_mii_mtx); + dwqe_write(sc, GMAC_MAC_MDIO_ADDR, sc->sc_clk << GMAC_MAC_MDIO_ADDR_CR_SHIFT | (phy << GMAC_MAC_MDIO_ADDR_PA_SHIFT) | @@ -450,14 +454,17 @@ dwqe_mii_readreg(struct device *self, in GMAC_MAC_MDIO_ADDR_GOC_READ | GMAC_MAC_MDIO_ADDR_GB); - for (n = 0; n < 2000; n++) { + for (n = 0; n < 10000; n++) { delay(10); - if ((dwqe_read(sc, GMAC_MAC_MDIO_ADDR) & GMAC_MAC_MDIO_ADDR_GB) == 0) - return dwqe_read(sc, GMAC_MAC_MDIO_DATA); + if ((dwqe_read(sc, GMAC_MAC_MDIO_ADDR) & GMAC_MAC_MDIO_ADDR_GB) == 0) { + rv = dwqe_read(sc, GMAC_MAC_MDIO_DATA); + break; + } } - printf("%s: mii_read timeout\n", sc->sc_dev.dv_xname); - return (0); + mtx_leave(&sc->sc_mii_mtx); + + return (rv); } void @@ -466,6 +473,8 @@ dwqe_mii_writereg(struct device *self, i struct dwqe_softc *sc = (void *)self; int n; + mtx_enter(&sc->sc_mii_mtx); + dwqe_write(sc, GMAC_MAC_MDIO_DATA, val); dwqe_write(sc, GMAC_MAC_MDIO_ADDR, sc->sc_clk << GMAC_MAC_MDIO_ADDR_CR_SHIFT | @@ -474,13 +483,13 @@ dwqe_mii_writereg(struct device *self, i GMAC_MAC_MDIO_ADDR_GOC_WRITE | GMAC_MAC_MDIO_ADDR_GB); - for (n = 0; n < 2000; n++) { + for (n = 0; n < 10000; n++) { delay(10); if ((dwqe_read(sc, GMAC_MAC_MDIO_ADDR) & GMAC_MAC_MDIO_ADDR_GB) == 0) - return; + break; } - printf("%s: mii_write timeout\n", sc->sc_dev.dv_xname); + mtx_leave(&sc->sc_mii_mtx); } void @@ -521,7 +530,7 @@ dwqe_mii_statchg(struct device *self) } void -dwqe_tick(void *arg) +dwqe_mii_tick(void *arg) { struct dwqe_softc *sc = arg; int s; Index: ic/dwqevar.h =================================================================== RCS file: /cvs/src/sys/dev/ic/dwqevar.h,v retrieving revision 1.6 diff -u -p -r1.6 dwqevar.h --- ic/dwqevar.h 24 Apr 2023 01:33:32 -0000 1.6 +++ ic/dwqevar.h 24 Apr 2023 02:08:21 -0000 @@ -1,4 +1,4 @@ -/* $OpenBSD: dwqevar.h,v 1.6 2023/04/24 01:33:32 dlg Exp $ */ +/* $OpenBSD: dwqevar.h,v 1.4 2023/04/07 09:33:51 dlg Exp $ */ /* * Copyright (c) 2008, 2019 Mark Kettenis * Copyright (c) 2017, 2022 Patrick Wildt @@ -54,11 +54,13 @@ struct dwqe_softc { void *sc_ih; struct if_device sc_ifd; + struct mii_bus sc_mdio; struct arpcom sc_ac; #define sc_lladdr sc_ac.ac_enaddr struct mii_data sc_mii; #define sc_media sc_mii.mii_media + struct mutex sc_mii_mtx; int sc_link; int sc_phyloc; enum dwqe_phy_mode sc_phy_mode; @@ -117,4 +119,8 @@ uint32_t dwqe_read(struct dwqe_softc *, void dwqe_write(struct dwqe_softc *, bus_addr_t, uint32_t); void dwqe_lladdr_read(struct dwqe_softc *, uint8_t *); void dwqe_lladdr_write(struct dwqe_softc *); + +int dwqe_mii_readreg(struct device *, int, int); +void dwqe_mii_writereg(struct device *, int, int, int); void dwqe_mii_statchg(struct device *); +