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 *); +