Index: arch/arm64/arm64/machdep.c =================================================================== RCS file: /cvs/src/sys/arch/arm64/arm64/machdep.c,v retrieving revision 1.79 diff -u -p -r1.79 machdep.c --- arch/arm64/arm64/machdep.c 9 Jan 2023 20:32:21 -0000 1.79 +++ arch/arm64/arm64/machdep.c 29 Mar 2023 06:41:50 -0000 @@ -1041,6 +1041,29 @@ initarm(struct arm64_bootparams *abp) } } + /* Remove reserved memory. */ + node = fdt_find_node("/reserved-memory"); + if (node) { + for (node = fdt_child_node(node); node; + node = fdt_next_node(node)) { + char *no_map; + if (fdt_node_property(node, "no-map", &no_map) < 0) + continue; + if (fdt_get_reg(node, 0, ®)) + continue; + if (reg.size == 0) + continue; + memreg_remove(®); + } + } + + /* Remove shared-memory. */ + node = fdt_find_node("/sram"); + if (node && fdt_get_reg(node, 0, ®) == 0) { + reg.size = roundup(reg.size, PAGE_SIZE); + memreg_remove(®); + } + /* Remove the initial 64MB block. */ reg.addr = memstart; reg.size = memend - memstart; Index: arch/arm64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v retrieving revision 1.262 diff -u -p -r1.262 GENERIC --- arch/arm64/conf/GENERIC 19 Mar 2023 11:18:11 -0000 1.262 +++ arch/arm64/conf/GENERIC 29 Mar 2023 06:41:50 -0000 @@ -111,6 +111,7 @@ pwmleds* at fdt? pwmreg* at fdt? early 1 scmi* at fdt? syscon* at fdt? early 1 +vreg* at fdt? early 1 virtio* at fdt? virtio* at pci? @@ -282,6 +284,7 @@ mvrtc* at fdt? mvspi* at fdt? moxtet* at spi? mvsw* at fdt? +mvsport* at mvsw? mvtemp* at fdt? mvuart* at fdt? sfp* at fdt? @@ -290,9 +293,12 @@ sfp* at fdt? rkclock* at fdt? early 1 rkgrf* at fdt? early 1 rkpinctrl* at fdt? early 1 +rkpmu* at fdt? early 1 +rkvdom* at fdt? early 1 rkcomphy* at fdt? early 1 rkpciephy* at fdt? early 1 rktcphy* at fdt? early 1 +rkusbphy* at fdt? early 1 rkanxdp* at fdt? rkdrm* at fdt? drm* at rkdrm? Index: arch/arm64/conf/RAMDISK =================================================================== RCS file: /cvs/src/sys/arch/arm64/conf/RAMDISK,v retrieving revision 1.193 diff -u -p -r1.193 RAMDISK --- arch/arm64/conf/RAMDISK 19 Mar 2023 11:18:11 -0000 1.193 +++ arch/arm64/conf/RAMDISK 29 Mar 2023 06:41:50 -0000 @@ -222,6 +222,7 @@ rkclock* at fdt? early 1 rkgrf* at fdt? early 1 rkpinctrl* at fdt? early 1 rkpciephy* at fdt? early 1 +rkpmu* at fdt? early 1 rkcomphy* at fdt? early 1 rktcphy* at fdt? early 1 rkemmcphy* at fdt? Index: arch/arm64/dev/mainbus.c =================================================================== RCS file: /cvs/src/sys/arch/arm64/dev/mainbus.c,v retrieving revision 1.24 diff -u -p -r1.24 mainbus.c --- arch/arm64/dev/mainbus.c 21 Jan 2023 10:30:11 -0000 1.24 +++ arch/arm64/dev/mainbus.c 29 Mar 2023 06:41:50 -0000 @@ -185,6 +185,7 @@ mainbus_print(void *aux, const char *pnp strcmp(buf, "cpus") == 0 || strcmp(buf, "memory") == 0 || strcmp(buf, "reserved-memory") == 0 || + strcmp(buf, "sram") == 0 || strcmp(buf, "thermal-zones") == 0 || strncmp(buf, "__", 2) == 0) return (QUIET); Index: dev/fdt/files.fdt =================================================================== RCS file: /cvs/src/sys/dev/fdt/files.fdt,v retrieving revision 1.182 diff -u -p -r1.182 files.fdt --- dev/fdt/files.fdt 19 Mar 2023 11:17:16 -0000 1.182 +++ dev/fdt/files.fdt 29 Mar 2023 06:41:50 -0000 @@ -232,6 +232,10 @@ device syscon: fdt attach syscon at fdt file dev/fdt/syscon.c syscon +device vreg: fdt +attach vreg at fdt +file dev/fdt/vreg.c vreg + device pwmbl attach pwmbl at fdt file dev/fdt/pwmbl.c pwmbl @@ -345,6 +349,10 @@ device rkcomphy attach rkcomphy at fdt file dev/fdt/rkcomphy.c rkcomphy +device rkusbphy +attach rkusbphy at fdt +file dev/fdt/rkusbphy.c rkusbphy + device rkdrm: drmbase, wsemuldisplaydev, rasops15, rasops16, rasops24, rasops32 attach rkdrm at fdt file dev/fdt/rkdrm.c rkdrm @@ -394,6 +402,14 @@ device rkpmic attach rkpmic at i2c file dev/fdt/rkpmic.c rkpmic +device rkpmu +attach rkpmu at fdt +file dev/fdt/rkpmu.c rkpmu + +device rkvdom +attach rkvdom at fdt +file dev/fdt/rkvdom.c rkvdom + device rkpwm attach rkpwm at fdt file dev/fdt/rkpwm.c rkpwm @@ -489,9 +509,11 @@ device mvspi: spi attach mvspi at fdt file dev/fdt/mvspi.c mvspi -device mvsw +device mvsw {} attach mvsw at fdt -file dev/fdt/mvsw.c mvsw +device mvsport: ether, ifnet, mii, ifmedia +attach mvsport at mvsw +file dev/fdt/mvsw.c mvsw | mvsport device mvtemp attach mvtemp at fdt Index: dev/fdt/if_dwqe_fdt.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/if_dwqe_fdt.c,v retrieving revision 1.5 diff -u -p -r1.5 if_dwqe_fdt.c --- dev/fdt/if_dwqe_fdt.c 26 Mar 2023 21:44:46 -0000 1.5 +++ dev/fdt/if_dwqe_fdt.c 29 Mar 2023 06:41:50 -0000 @@ -63,7 +63,7 @@ int dwqe_fdt_match(struct device *, void *, void *); void dwqe_fdt_attach(struct device *, struct device *, void *); -void dwqe_setup_rockchip(struct dwqe_softc *); +int dwqe_setup_rockchip(struct dwqe_softc *); void dwqe_mii_statchg_rk3568(struct device *); void dwqe_mii_statchg_rk3588(struct device *); @@ -137,8 +137,12 @@ dwqe_fdt_attach(struct device *parent, s delay(5000); /* Do hardware specific initializations. */ - if (OF_is_compatible(faa->fa_node, "rockchip,rk3568-gmac")) - dwqe_setup_rockchip(sc); + if (OF_is_compatible(faa->fa_node, "rockchip,rk3568-gmac")) { + if (dwqe_setup_rockchip(sc) == -1) { + /* dwqe_setup_rockchip prints the error and \n */ + return; + } + } /* Power up PHY. */ phy_supply = OF_getpropint(faa->fa_node, "phy-supply", 0); @@ -266,41 +270,70 @@ dwqe_reset_phy(struct dwqe_softc *sc, ui #define RK3568_GRF_GMACx_CON1(x) (0x0384 + (x) * 0x8) #define RK3568_GMAC_PHY_INTF_SEL_RGMII ((0x7 << 4) << 16 | (0x1 << 4)) #define RK3568_GMAC_PHY_INTF_SEL_RMII ((0x7 << 4) << 16 | (0x4 << 4)) -#define RK3568_GMAC_TXCLK_DLY_ENA ((1 << 0) << 16 | (1 << 0)) -#define RK3568_GMAC_RXCLK_DLY_ENA ((1 << 1) << 16 | (1 << 1)) +#define RK3568_GMAC_TXCLK_DLY_SET(_v) ((1 << 0) << 16 | ((_v) << 0)) +#define RK3568_GMAC_RXCLK_DLY_SET(_v) ((1 << 1) << 16 | ((_v) << 1)) void dwqe_mii_statchg_rk3568_task(void *); -void +int dwqe_setup_rockchip(struct dwqe_softc *sc) { + char phy_mode[32]; struct regmap *rm; uint32_t grf; int tx_delay, rx_delay; + uint32_t iface; grf = OF_getpropint(sc->sc_node, "rockchip,grf", 0); rm = regmap_byphandle(grf); - if (rm == NULL) - return; + if (rm == NULL) { + printf(": can't find rockchip,grf\n"); + return (-1); + } + + if (OF_getprop(sc->sc_node, "phy-mode", + phy_mode, sizeof(phy_mode)) <= 0) { + printf(": no phy-mode\n"); + return (-1); + } tx_delay = OF_getpropint(sc->sc_node, "tx_delay", 0x30); rx_delay = OF_getpropint(sc->sc_node, "rx_delay", 0x10); - if (OF_is_compatible(sc->sc_node, "rockchip,rk3568-gmac")) { - /* Program clock delay lines. */ - regmap_write_4(rm, RK3568_GRF_GMACx_CON0(sc->sc_gmac_id), - RK3568_GMAC_CLK_TX_DL_CFG(tx_delay) | - RK3568_GMAC_CLK_RX_DL_CFG(rx_delay)); - - /* Use RGMII interface and enable clock delay. */ - regmap_write_4(rm, RK3568_GRF_GMACx_CON1(sc->sc_gmac_id), - RK3568_GMAC_PHY_INTF_SEL_RGMII | - RK3568_GMAC_TXCLK_DLY_ENA | - RK3568_GMAC_RXCLK_DLY_ENA); - - task_set(&sc->sc_statchg_task, - dwqe_mii_statchg_rk3568_task, sc); + if (strcmp(phy_mode, "rgmii") == 0) { + iface = RK3568_GMAC_PHY_INTF_SEL_RGMII; + } else if (strcmp(phy_mode, "rgmii-id") == 0) { + iface = RK3568_GMAC_PHY_INTF_SEL_RGMII; + /* id is "internal delay" */ + tx_delay = rx_delay = 0; + } else if (strcmp(phy_mode, "rgmii-rxid") == 0) { + iface = RK3568_GMAC_PHY_INTF_SEL_RGMII; + rx_delay = 0; + } else if (strcmp(phy_mode, "rgmii-txid") == 0) { + iface = RK3568_GMAC_PHY_INTF_SEL_RGMII; + tx_delay = 0; + } else if (strcmp(phy_mode, "rmii") == 0) { + iface = RK3568_GMAC_PHY_INTF_SEL_RMII; + tx_delay = rx_delay = 0; + } else { + printf(": unknown phy-mode %s\n", phy_mode); + return (-1); } + + /* Program clock delay lines. */ + regmap_write_4(rm, RK3568_GRF_GMACx_CON0(sc->sc_gmac_id), + RK3568_GMAC_CLK_TX_DL_CFG(tx_delay) | + RK3568_GMAC_CLK_RX_DL_CFG(rx_delay)); + + /* Set interface and enable/disable clock delay. */ + regmap_write_4(rm, RK3568_GRF_GMACx_CON1(sc->sc_gmac_id), iface | + RK3568_GMAC_TXCLK_DLY_SET(tx_delay > 0 ? 1 : 0) | + RK3568_GMAC_RXCLK_DLY_SET(rx_delay > 0 ? 1 : 0)); + + task_set(&sc->sc_statchg_task, + dwqe_mii_statchg_rk3568_task, sc); + + return (0); } void Index: dev/fdt/if_mvneta.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/if_mvneta.c,v retrieving revision 1.28 diff -u -p -r1.28 if_mvneta.c --- dev/fdt/if_mvneta.c 27 Feb 2023 09:59:46 -0000 1.28 +++ dev/fdt/if_mvneta.c 29 Mar 2023 06:41:50 -0000 @@ -175,6 +175,8 @@ struct mvneta_softc { int sc_sfp; int sc_node; + struct if_device sc_ifd; + #if NKSTAT > 0 struct mutex sc_kstat_lock; struct timeout sc_kstat_tick; @@ -807,6 +809,10 @@ mvneta_attach_deferred(struct device *se */ if_attach(ifp); ether_ifattach(ifp); + + sc->sc_ifd.if_node = sc->sc_node; + sc->sc_ifd.if_ifp = ifp; + if_register(&sc->sc_ifd); #if NKSTAT > 0 mvneta_kstat_attach(sc); Index: dev/fdt/mvsw.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/mvsw.c,v retrieving revision 1.5 diff -u -p -r1.5 mvsw.c --- dev/fdt/mvsw.c 6 Apr 2022 18:59:28 -0000 1.5 +++ dev/fdt/mvsw.c 29 Mar 2023 06:41:50 -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 @@ -27,7 +36,31 @@ #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 +82,303 @@ #define MVSW_SMI_TIMEOUT 1600 /* Switch registers */ -#define MVSW_PORT(x) (0x10 + (x)) -#define MVSW_G2 0x1c +#define MVSW_PORT(x) (0x10 + (x)) /* Port */ +#define MVSW_G1 0x1b /* Global1 */ +#define MVSW_G2 0x1c /* Global2 */ +/* + * Port registers */ #define MVSW_PORT_SWITCHID 0x03 #define MVSW_PORT_SWITCHID_PROD_MASK 0xfff0 #define MVSW_PORT_SWITCHID_PROD_88E6141 0x3400 #define MVSW_PORT_SWITCHID_PROD_88E6341 0x3410 #define MVSW_PORT_SWITCHID_REV_MASK 0x000f -#define MVSW_PORT_CTRL 0x04 -#define MVSW_PORT_CTRL_STATE_MASK 0x0003 -#define MVSW_PORT_CTRL_STATE_FORWARD 0x0003 +#define MVSW_PORT_CTRL0 0x04 +#define MVSW_PORT_CTRL0_STATE_MASK (0x3 << 0) +#define MVSW_PORT_CTRL0_STATE_DISABLED (0x0 << 0) +#define MVSW_PORT_CTRL0_STATE_BLOCKING (0x1 << 0) +#define MVSW_PORT_CTRL0_STATE_LEARNING (0x2 << 0) +#define MVSW_PORT_CTRL0_STATE_FORWARD (0x3 << 0) +#define MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST (0x1 << 2) +#define MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST (0x1 << 3) +#define MVSW_PORT_CTRL0_TAG_IF_BOTH (0x1 << 6) +#define MVSW_PORT_CTRL0_VLAN_TUNNEL (0x1 << 7) +#define MVSW_PORT_CTRL0_FRAME_MODE_MASK (0x3 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_NORMAL (0x0 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_TAG (0x1 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_PROVIDER (0x2 << 8) +#define MVSW_PORT_CTRL0_FRAME_MODE_ETAG (0x3 << 8) +#define MVSW_PORT_CTRL0_IGMP_MLD_SNOOP (0x1 << 10) +#define MVSW_PORT_CTRL0_HEADER (0x1 << 11) +#define MVSW_PORT_CTRL0_EGRESS_MODE_MASK (0x3 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_UNMODIFIED (0x0 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_UNTAGGED (0x1 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_TAGGED (0x2 << 12) +#define MVSW_PORT_CTRL0_EGRESS_MODE_ETAG (0x3 << 12) +#define MVSW_PORT_CTRL0_SAFILTER_MASK (0x3 << 14) +#define MVSW_PORT_CTRL0_SAFILTER_DROP_ON_LOCK (0x1 << 14) +#define MVSW_PORT_CTRL0_SAFILTER_DROP_ON_UNLOCK (0x2 << 14) +#define MVSW_PORT_CTRL0_SAFILTER_DROP_TO_CPU (0x3 << 14) + +#define MVSW_PORT_CTRL1 0x05 +#define MVSW_PORT_CTRL1_FID_HI_SHIFT 0 +#define MVSW_PORT_CTRL1_FID_HI_MASK 0xff +#define MVSW_PORT_CTRL1_TRUNK_ID_SHIFT 8 +#define MVSW_PORT_CTRL1_TRUNK_ID_MASK 0x0f +#define MVSW_PORT_CTRL1_TRUNK_PORT (0x1 << 14) +#define MVSW_PORT_CTRL1_MESSAGE_PORT (0x1 << 15) + +#define MVSW_PORT_BASED_VLAN 0x06 +#define MVSW_PORT_BASED_VLAN_FID_LO_SHIFT 0 +#define MVSW_PORT_BASED_VLAN_FID_LO_MASK 0 + +/* Default Port VLAN */ +#define MVSW_PORT_DEFAULT_VLAN 0x07 +#define MVSW_PORT_DEVAULT_VLAN_VID_SHIFT 0 +#define MVSW_PORT_DEVAULT_VLAN_VID_MASK 0xfff + +/* Port Control 2 */ +#define MVSW_PORT_CTRL2 0x08 +#define MVSW_PORT_CTRL2_JUMBO_MODE_MASK (0x3 << 12) +#define MVSW_PORT_CTRL2_JUMBO_MODE_1522 (0x0 << 12) +#define MVSW_PORT_CTRL2_JUMBO_MODE_2048 (0x1 << 12) +#define MVSW_PORT_CTRL2_JUMBO_MODE_10240 (0x2 << 12) +#define MVSW_PORT_CTRL2_8021Q_MODE_MASK (0x3 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_DISABLED (0x0 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_FALLBACK (0x1 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_CHECK (0x2 << 10) +#define MVSW_PORT_CTRL2_8021Q_MODE_SECURE (0x3 << 10) +#define MVSW_PORT_CTRL2_DISCARD_TAGGED (0x1 << 9) +#define MVSW_PORT_CTRL2_DISCARD_UNTAGGED (0x1 << 8) +#define MVSW_PORT_CTRL2_MAP_DA (0x1 << 7) + +/* Port Association Vector */ +#define MVSW_PORT_ASSOC_VECTOR 0x0b +#define MVSW_PORT_ASSOC_VECTOR_HOLD_AT_1 (0x1 << 15) +#define MVSW_PORT_ASSOC_VECTOR_INT_AGE_OUT (0x1 << 14) +#define MVSW_PORT_ASSOC_VECTOR_LOCKED_PORT (0x1 << 13) +#define MVSW_PORT_ASSOC_VECTOR_IGNORE_WRONG (0x1 << 12) +#define MVSW_PORT_ASSOC_VECTOR_REFRESH_LOCKED (0x1 << 11) +/* i think low bits are a bitmap of relevant ports */ + +#define MVSW_PORT_ETH_TYPE 0x0f + +/* + * Global1 registers + */ + +/* ATU FID */ +#define MVSW_G1_ATU_FID 0x01 + +#define MVSW_G1_VTU_OP 0x05 +#define MVSW_G1_VTU_OP_BUSY (0x1 << 15) +#define MVSW_G1_VTU_OP_MASK (0x7 << 12) +#define MVSW_G1_VTU_OP_FLUSH_ALL (0x1 << 12) +#define MVSW_G1_VTU_OP_NOOP (0x2 << 12) +#define MVSW_G1_VTU_OP_VTU_LOAD_PURGE (0x3 << 12) +#define MVSW_G1_VTU_OP_VTU_GET_NEXT (0x4 << 12) +#define MVSW_G1_VTU_OP_STU_LOAD_PURGE (0x5 << 12) +#define MVSW_G1_VTU_OP_STU_GET_NEXT (0x6 << 12) +#define MVSW_G1_VTU_OP_GET_CLR_VIOLATION (0x7 << 12) +#define MVSW_G1_VTU_OP_MEMBER_VIOLATION (0x1 << 6) +#define MVSW_G1_VTU_OP_MISS_VIOLATION (0x1 << 5) +#define MVSW_G1_VTU_OP_SPID_MASK (0xf << 0) + +/* ATU Control */ +#define MVSW_G1_ATU_CTRL 0x0a +#define MVSW_G1_ATU_CTRL_LEARN2ALL (0x1 << 3) + +/* ATU Operation */ +#define MVSW_G1_ATU_OP 0x0a +#define MVSW_G1_ATU_OP_BUSY (0x1 << 15) +#define MVSW_G1_ATU_OP_MASK (0x7 << 12) +#define MVSW_G1_ATU_OP_NOOP (0x0 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_ALL (0x1 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_NON_STATIC (0x2 << 12) +#define MVSW_G1_ATU_OP_LOAD_DB (0x3 << 12) +#define MVSW_G1_ATU_OP_GET_NEXT_DB (0x4 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_ALL_DB (0x5 << 12) +#define MVSW_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB (0x6 << 12) +#define MVSW_G1_ATU_OP_GET_CLR_VIOLATION (0x7 << 12) +#define MVSW_G1_ATU_OP_AGE_OUT_VIOLATION (0x1 << 7) +#define MVSW_G1_ATU_OP_MEMBER_VIOLATION (0x1 << 6) +#define MVSW_G1_ATU_OP_MISS_VIOLATION (0x1 << 5) +#define MVSW_G1_ATU_OP_FULL_VIOLATION (0x1 << 4) + +/* ATU Data */ +#define MVSW_G1_ATU_DATA 0x0c +#define MVSW_G1_ATU_DATA_TRUNK (0x1 << 15) +#define MVSW_G1_ATU_DATA_TRUNK_ID_MASK (0xf << 4) +#define MVSW_G1_ATU_DATA_PORT_VECTOR_MASK (0x3ff << 4) +#define MVSW_G1_ATU_DATA_STATE_MASK (0xf << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_UNUSED (0x0 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_1_OLDEST (0x1 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_2 (0x2 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_3 (0x3 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_4 (0x4 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_5 (0x5 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_6 (0x6 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_AGE_7_NEWEST (0x7 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_POLICY (0x8 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_POLICY_PO (0x9 << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL (0xa << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_AVB_NRL_PO (0xb << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT (0xc << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_DA_MGMT_PO (0xd << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC (0xe << 0) +#define MVSW_G1_ATU_DATA_STATE_UC_STATIC_PO (0xf << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_UNUSED (0x0 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_POLICY (0x4 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL (0x5 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT (0x6 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC (0x7 << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_POLICY_PO (0xc << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_AVB_NRL_PO (0xd << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_DA_MGMT_PO (0xe << 0) +#define MVSW_G1_ATU_DATA_STATE_MC_STATIC_PO (0xf << 0) + +#define MVSW_G1_ATU_MAC_BASE 0x0d +#define MVSW_G1_ATU_MAC_01 (MVSW_G1_ATU_MAC_BASE + 0) +#define MVSW_G1_ATU_MAC_23 (MVSW_G1_ATU_MAC_BASE + 1) +#define MVSW_G1_ATU_MAC_45 (MVSW_G1_ATU_MAC_BASE + 2) + +/* Monitor & MGMT Control */ +#define MVSW_G1_MONITOR_MGMT_CTL 0x1a +#define MVSW_G1_MONITOR_MGMT_CTL_UPDATE (0x1 << 15) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_MASK (0x3f << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200000XLO (0x00 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200000XHI (0x01 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200002XLO (0x02 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_0180C200002XHI (0x03 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST (0x20 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST (0x21 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST (0x30 << 8) +#define MVSW_G1_MONITOR_MGMT_CTL_DATA_SHIFT 0 +#define MVSW_G1_MONITOR_MGMT_CTL_DATA_MASK 0xff +#define MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI 0xe0 + +/* Control2 */ +#define MVSW_G1_CTRL2 0x1c +#define MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT 0 +#define MVSW_G1_CTRL2_DEVICE_NUMBER_MASK 0x1f +#define MVSW_G1_CTRL2_RMU_MODE_MASK (0x7 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_PORT_0 (0x0 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_PORT_1 (0x1 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_ALL_DSA (0x6 << 8) +#define MVSW_G1_CTRL2_RMU_MODE_DISABLED (0x7 << 8) + +/* + * Global2 registers + */ + +/* Trunk Mask Table */ +#define MVSW_G2_TRUNK_MASK 0x07 +#define MVSW_G2_TRUNK_MASK_UPDATE (0x1 << 15) +#define MVSW_G2_TRUNK_MASK_SHIFT 12 +#define MVSW_G2_TRUNK_MASK_COUNT 8 /* 0x0 to 0x7 */ +#define MVSW_G2_TRUNK_MASK_HASH (0x1 << 11) +/* low bits are a bitmap of ports in the trunk i think */ + +/* Trunk Mapping Table */ +#define MVSW_G2_TRUNK_MAPPING 0x08 +#define MVSW_G2_TRUNK_MAPPING_UPDATE (0x1 << 15) +#define MVSW_G2_TRUNK_MAPPING_ID_SHIFT 11 +#define MVSW_G2_TRUNK_MAPPING_ID_COUNT 16 /* 0x0 to 0xf */ +/* low bits are a bitmap of ports in the trunk i think */ + +/* Ingress Rate Command */ +#define MVSW_G2_IRL_CMD 0x09 +#define MVSW_G2_IRL_CMD_BUSY (0x1 << 15) +#define MVSW_G2_IRL_CMD_OP_MASK (0x7 << 12) +#define MVSW_G2_IRL_CMD_OP_NOOP (0x0 << 12) +#define MVSW_G2_IRL_CMD_OP_INIT_ALL (0x1 << 12) +#define MVSW_G2_IRL_CMD_OP_INIT_RES (0x2 << 12) +#define MVSW_G2_IRL_CMD_OP_WRITE_REG (0x3 << 12) +#define MVSW_G2_IRL_CMD_OP_READ_REG (0x4 << 12) +#define MVSW_G2_IRL_CMD_PORT_SHIFT 8 +#define MVSW_G2_IRL_CMD_PORT_MASK 0xf +#define MVSW_G2_IRL_CMD_RES_MASK (0x7 << 5) +#define MVSW_G2_IRL_CMD_REG_MASK (0xf << 0) + +/* Ingress Rate Data */ +#define MVSW_G2_IRL_DATA 0x0a + #define MVSW_G2_SMI_PHY_CMD 0x18 #define MVSW_G2_SMI_PHY_DATA 0x19 +/* Misc */ +#define MVSW_G2_MISC 0x1d +#define MVSW_G2_MISC_5BIT_PORT (0x1 << 14) + /* SERDES registers */ #define MVSW_SERDES(x) (0x10 + (x)) #define MVSW_SERDES_BMCR (0x2000 + MII_BMCR) +struct mvsw_tag { + uint16_t tag0; +#define MVSW_TAG_MODE_SHIFT 14 +#define MVSW_TAG_MODE_MASK (0x3 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_TO_CPU (0x0 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_FROM_CPU (0x1 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_TO_SNIFFER (0x2 << MVSW_TAG_MODE_SHIFT) +#define MVSW_TAG_MODE_TAG (0x3 << MVSW_TAG_MODE_SHIFT) + +#define MVSW_TAG_IEEE (1 << 13) + +#define MVSW_TAG_SWITCH_SHIFT 8 +#define MVSW_TAG_SWITCH_MASK 0x1f + +#define MVSW_TAG_PORT_SHIFT 3 +#define MVSW_TAG_PORT_MASK 0x1f + + uint16_t tag1; +}; + +struct mvsw_etag { + uint16_t reserved; + uint16_t tag0; + uint16_t tag1; +}; + /* XXX #include */ #define MDIO_MMD_PHYXS 4 +/* + * The driver. + */ + +struct mvsw_port { + int p_port; + struct mvsw_softc *p_softc; + struct ifnet *p_ifp0; + + int (*p_ioctl)(struct ifnet *, u_long, caddr_t); + void (*p_input)(struct ifnet *, struct mbuf *); + int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *, + struct rtentry *); + + TAILQ_ENTRY(mvsw_port) p_entry; +}; +TAILQ_HEAD(mvsw_ports, mvsw_port); + +struct mvsport_softc; + struct mvsw_softc { - struct device sc_dev; + struct device sc_dev; + + int sc_node; + struct mii_bus *sc_mdio; + int sc_reg; - struct mii_bus *sc_mdio; - int sc_reg; + unsigned int sc_nports; + struct mvsport_softc *sc_ports[MVSW_MAX_PORTS]; + struct mvsw_ports sc_cpus; + + caddr_t sc_bpf; }; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + int mvsw_match(struct device *, void *, void *); void mvsw_attach(struct device *, struct device *, void *); @@ -88,6 +390,26 @@ struct cfdriver mvsw_cd = { NULL, "mvsw", DV_DULL }; +struct mvsw_defer { + struct task d_task; + struct mvsw_softc *d_sc; +}; + +static void mvsw_attach_deferred(void *); + +static void mvsw_attach_cpu(struct mvsw_softc *, int, uint32_t); +static void mvsw_config_cpu(struct mvsw_softc *, struct mvsw_port *); + +static int mvsw_p_ioctl(struct ifnet *, u_long, caddr_t); +static void mvsw_p_input(struct ifnet *, struct mbuf *); +static int mvsw_p_output(struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *); + +static int mvsw_print(void *, const char *); + +static struct mbuf * + mvsport_input(struct mvsport_softc *, struct mbuf *); + int mvsw_smi_read(struct mvsw_softc *, int, int); void mvsw_smi_write(struct mvsw_softc *, int, int, int); int mvsw_phy_read(struct mvsw_softc *, int, int); @@ -95,7 +417,25 @@ void mvsw_phy_write(struct mvsw_softc *, int mvsw_serdes_read(struct mvsw_softc *, int, int, int); void mvsw_serdes_write(struct mvsw_softc *, int, int, int, int); -void mvsw_port_enable(struct mvsw_softc *, int); +static int mvsw_wait(struct mvsw_softc *, int, int, uint16_t, uint16_t, + const char *); + +#define mvsw_vtu_wait(_sc) \ + mvsw_wait((_sc), MVSW_G1, MVSW_G1_VTU_OP, \ + MVSW_G1_VTU_OP_BUSY, 0, "mvswvtu") + +#define mvsw_atu_wait(_sc) \ + mvsw_wait((_sc), MVSW_G1, MVSW_G1_ATU_OP, \ + MVSW_G1_ATU_OP_BUSY, 0, "mvswatu") + +#define mvsw_irl_wait(_sc) \ + mvsw_wait((_sc), MVSW_G2, MVSW_G2_IRL_CMD, \ + MVSW_G2_IRL_CMD_BUSY, 0, "mvswirl") + +static int mvsw_vtu_op(struct mvsw_softc *, uint16_t); +static int mvsw_atu_op(struct mvsw_softc *, uint16_t, uint16_t, uint16_t); +static int mvsw_irl_op(struct mvsw_softc *, uint16_t); + void mvsw_phy_enable(struct mvsw_softc *, int); void mvsw_serdes_enable(struct mvsw_softc *, int); @@ -112,15 +452,18 @@ mvsw_attach(struct device *parent, struc { struct mvsw_softc *sc = (struct mvsw_softc *)self; struct fdt_attach_args *faa = aux; - int ports, port, node; - uint32_t phy; - uint16_t swid; + uint16_t r; + struct mvsw_defer *d; + + TAILQ_INIT(&sc->sc_cpus); + sc->sc_nports = nitems(sc->sc_ports); if (faa->fa_nreg < 1) { printf(": no registers\n"); return; } + sc->sc_node = faa->fa_node; sc->sc_reg = faa->fa_reg[0].addr; printf(" phy %d", sc->sc_reg); @@ -130,24 +473,136 @@ mvsw_attach(struct device *parent, struc return; } - swid = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID); - switch (swid & MVSW_PORT_SWITCHID_PROD_MASK) { + r = mvsw_smi_read(sc, MVSW_PORT(0), MVSW_PORT_SWITCHID); + switch (r & MVSW_PORT_SWITCHID_PROD_MASK) { case MVSW_PORT_SWITCHID_PROD_88E6141: + sc->sc_nports = 6; printf(": 88E6141"); break; case MVSW_PORT_SWITCHID_PROD_88E6341: + sc->sc_nports = 6; printf(": 88E6341"); break; default: printf(": unknown product 0x%04x\n", - swid & MVSW_PORT_SWITCHID_PROD_MASK); + r & MVSW_PORT_SWITCHID_PROD_MASK); return; } - printf(" rev %d\n", swid & MVSW_PORT_SWITCHID_REV_MASK); + printf(" rev %d\n", r & MVSW_PORT_SWITCHID_REV_MASK); - ports = OF_getnodebyname(faa->fa_node, "ports"); - if (ports == 0) + if (sc->sc_dev.dv_unit & ~MVSW_G1_CTRL2_DEVICE_NUMBER_MASK) { + printf("%s: too many switches\n", DEVNAME(sc)); return; + } + + /* + * wait until the cpu port is (probably) attached to wire things up. + */ + + d = malloc(sizeof(*d), M_TEMP, M_WAITOK); + task_set(&d->d_task, mvsw_attach_deferred, d); + d->d_sc = sc; + + config_pending_incr(); + task_add(systq, &d->d_task); +} + +static void +mvsw_attach_deferred(void *arg) +{ + struct mvsw_defer *d = arg; + struct mvsw_softc *sc = d->d_sc; + int ports, port, node, i; + uint32_t phy, phandle; + uint16_t r; + struct mvsw_port *p; + + free(d, M_TEMP, sizeof(*d)); + + for (port = 0; port < sc->sc_nports; port++) { + /* start with all ports disabled */ + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL0); + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); + SET(r, MVSW_PORT_CTRL0_STATE_DISABLED); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL0, r); + + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL1); + CLR(r, MVSW_PORT_CTRL1_MESSAGE_PORT); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL1, r); + + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_DEFAULT_VLAN, 0); + + /* reset ingress rate limiting (IRL) */ + if (mvsw_irl_op(sc, MVSW_G2_IRL_CMD_OP_INIT_ALL | + (port << MVSW_G2_IRL_CMD_PORT_SHIFT)) == -1) { + printf("%s: unable to reset ingress rate limiting " + "on port %u\n", DEVNAME(sc), port); + /* we can carry on */ + } + } + + /* flush the vlan translation unit */ + if (mvsw_vtu_wait(sc) == -1) { + printf("%s: VLAN Translation Unit busy\n", DEVNAME(sc)); + goto done; + } + + if (mvsw_vtu_op(sc, MVSW_G1_VTU_OP_FLUSH_ALL) == -1) { + printf("%s: VLAN Translation Unit flush timeout\n", + DEVNAME(sc)); + goto done; + } + + /* clear 5 bit port use in port vlan table (PVT) */ + r = mvsw_smi_read(sc, MVSW_G2, MVSW_G2_MISC); + CLR(r, MVSW_G2_MISC_5BIT_PORT); + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_MISC, r); + + /* XXX PVT clear/reset/setup? */ + + /* flush the address translation unit */ + if (mvsw_atu_wait(sc) == -1) { + printf("%s: Address Translation Unit busy\n", DEVNAME(sc)); + goto done; + } + + if (mvsw_atu_op(sc, 0, MVSW_G1_ATU_OP_FLUSH_MOVE_ALL, 0) == -1) { + printf("%s: Address Translation Unit flush timeout\n", + DEVNAME(sc)); + goto done; + } + + /* XXX clear priority overrite table */ + + r = mvsw_smi_read(sc, MVSW_G1, MVSW_G1_CTRL2); + /* set device number */ + CLR(r, MVSW_G1_CTRL2_DEVICE_NUMBER_MASK << + MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT); + SET(r, sc->sc_dev.dv_unit << + MVSW_G1_CTRL2_DEVICE_NUMBER_SHIFT); + + /* disable remote management */ + CLR(r, MVSW_G1_CTRL2_RMU_MODE_MASK); + SET(r, MVSW_G1_CTRL2_RMU_MODE_DISABLED); + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_CTRL2, r); + + /* clear trunk setup */ + for (i = 0; i < MVSW_G2_TRUNK_MASK_COUNT; i++) { +// mvsw_smi_write(sc, MVSW_G2, MVSW_G2_TRUNK_MASK, +// MVSW_G2_TRUNK_MASK_UPDATE | +// (i << MVSW_G2_TRUNK_MASK_SHIFT) | /* clear bitmap */ 0); + } + for (i = 0; i < MVSW_G2_TRUNK_MAPPING_ID_COUNT; i++) { +// mvsw_smi_write(sc, MVSW_G2, MVSW_G2_TRUNK_MAPPING, +// MVSW_G2_TRUNK_MAPPING_UPDATE | +// (i << MVSW_G2_TRUNK_MAPPING_ID_SHIFT) | +// /* clear bitmap */ 0); + } + + ports = OF_getnodebyname(sc->sc_node, "ports"); + if (ports == 0) + goto done; + for (port = OF_child(ports); port; port = OF_peer(port)) { phy = OF_getpropint(port, "phy-handle", 0); node = OF_getnodebyphandle(phy); @@ -156,8 +611,274 @@ mvsw_attach(struct device *parent, struc else mvsw_serdes_enable(sc, port); - mvsw_port_enable(sc, port); + phandle = OF_getpropint(port, "ethernet", 0); + if (phandle != 0) + mvsw_attach_cpu(sc, port, phandle); + else { + uint32_t reg = OF_getpropint(port, "reg", + MVSW_MAX_PORTS); + struct device *child; + + if (reg < sc->sc_nports) { + child = config_found(&sc->sc_dev, &port, + mvsw_print); + sc->sc_ports[reg] = + (struct mvsport_softc *)child; + } + } + } + + p = TAILQ_FIRST(&sc->sc_cpus); + if (p == NULL) { + printf("%s: no CPU ports found\n", DEVNAME(sc)); + goto done; + } + + mvsw_config_cpu(sc, p); + + r = 0x1 << p->p_port; + for (port = 0; port < sc->sc_nports; port++) { + if (sc->sc_ports[port] == NULL) + continue; + + mvsw_smi_write(sc, MVSW_PORT(port), + MVSW_PORT_BASED_VLAN, r); } + +done: + config_pending_decr(); +} + +static void +mvsw_attach_cpu(struct mvsw_softc *sc, int node, uint32_t phandle) +{ + struct ifnet *ifp0; + struct arpcom *ac0; + struct mvsw_port *p; + int port; + uint16_t r; + + port = OF_getpropint(node, "reg", -1); + if (port == -1) { + printf("%s: can't find cpu interface port number\n", + DEVNAME(sc)); + return; + } + + ifp0 = if_byphandle(phandle); + if (ifp0 == NULL) { + printf("%s: unable to find cpu interface on port %u\n", + DEVNAME(sc), port); + return; + } + + if (ifp0->if_type != IFT_ETHER) { + printf("%s: unsupported type of cpu interface on port %u\n", + DEVNAME(sc), port); + return; + } + + printf("%s: %s at port %u\n", DEVNAME(sc), ifp0->if_xname, port); + + NET_LOCK(); + ac0 = (struct arpcom *)ifp0; + if (ac0->ac_trunkport != NULL) { + printf("%s: cpu interface %s is busy\n", + DEVNAME(sc), ifp0->if_xname); + NET_UNLOCK(); + return; + } + + p = malloc(sizeof(*p), M_DEVBUF, M_WAITOK); + + p->p_softc = sc; + p->p_ifp0 = ifp0; + p->p_port = port; + p->p_ioctl = ifp0->if_ioctl; + p->p_input = ifp0->if_input; + p->p_output = ifp0->if_output; + + TAILQ_INSERT_TAIL(&sc->sc_cpus, p, p_entry); + + if (ifpromisc(ifp0, 1) != 0) + printf("%s: %s promisc error\n", DEVNAME(sc), ifp0->if_xname); + + ac0->ac_trunkport = p; + /* membar_producer()? */ + ifp0->if_ioctl = mvsw_p_ioctl; + ifp0->if_input = mvsw_p_input; + ifp0->if_output = mvsw_p_output; + NET_UNLOCK(); + + /* Enable port. */ + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL0); + CLR(r, MVSW_PORT_CTRL0_STATE_MASK); + SET(r, MVSW_PORT_CTRL0_STATE_FORWARD); + CLR(r, MVSW_PORT_CTRL0_FRAME_MODE_MASK); + SET(r, MVSW_PORT_CTRL0_FRAME_MODE_ETAG); + CLR(r, MVSW_PORT_CTRL0_EGRESS_MODE_MASK); + SET(r, MVSW_PORT_CTRL0_EGRESS_MODE_ETAG); + SET(r, MVSW_PORT_CTRL0_EGRESS_FLOOD_UCAST | + MVSW_PORT_CTRL0_EGRESS_FLOOD_MCAST); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL0, r); + + r = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL2); + CLR(r, MVSW_PORT_CTRL2_MAP_DA); + CLR(r, MVSW_PORT_CTRL2_JUMBO_MODE_MASK); + SET(r, MVSW_PORT_CTRL2_JUMBO_MODE_10240); + CLR(r, MVSW_PORT_CTRL2_8021Q_MODE_MASK); + SET(r, MVSW_PORT_CTRL2_8021Q_MODE_DISABLED); + CLR(r, MVSW_PORT_CTRL2_DISCARD_TAGGED); + CLR(r, MVSW_PORT_CTRL2_DISCARD_UNTAGGED); + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL2, r); + + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_ASSOC_VECTOR, + 0x1 << port); + + mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_ETH_TYPE, + ETHERTYPE_MVSW_ETAG); +} + +static void +mvsw_config_cpu(struct mvsw_softc *sc, struct mvsw_port *p) +{ + int port = p->p_port; + uint16_t r; + + /* tell the switch this is the cpu port */ + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | + MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST | + (port | MVSW_G1_MONITOR_MGMT_CTL_PTR_CPU_DEST_MGMTPRI); + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); + + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | + MVSW_G1_MONITOR_MGMT_CTL_PTR_INGRESS_DEST | + port; + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); + + r = MVSW_G1_MONITOR_MGMT_CTL_UPDATE | + MVSW_G1_MONITOR_MGMT_CTL_PTR_EGRESS_DEST | + port; + mvsw_smi_write(sc, MVSW_G2, MVSW_G1_MONITOR_MGMT_CTL, r); +} + +static int +mvsw_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mvsw_port *p = ac0->ac_trunkport; + int error = 0; + + switch (cmd) { + case SIOCGTRUNKPORT: { + struct trunk_reqport *rp = (struct trunk_reqport *)data; + struct mvsw_softc *sc = p->p_softc; + + if (strncmp(rp->rp_ifname, rp->rp_portname, + sizeof(rp->rp_ifname)) != 0) + return (EINVAL); + + (void)strlcpy(rp->rp_ifname, DEVNAME(sc), + sizeof(rp->rp_ifname)); + break; + } + + case SIOCSIFLLADDR: + error = EBUSY; + break; + + default: + error = (*p->p_ioctl)(ifp0, cmd, data); + break; + } + + return (error); +} + +static int +mvsw_p_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst, + struct rtentry *rt) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mvsw_port *p = ac0->ac_trunkport; + +#if 0 + /* restrict transmission to bpf only */ + if (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL) { + m_freem(m); + return (EBUSY); + } +#endif + + return ((*p->p_output)(ifp0, m, dst, rt)); +} + +static void +mvsw_p_input(struct ifnet *ifp0, struct mbuf *m) +{ + struct arpcom *ac0 = (struct arpcom *)ifp0; + struct mvsw_port *p = ac0->ac_trunkport; + struct mvsw_softc *sc = p->p_softc; + struct ether_header *eh; + struct mvsw_etag *etag; + int hlen = sizeof(*eh) + sizeof(*etag); + int diff = hlen - offsetof(struct ether_header, ether_type); + uint16_t tag0; + struct mvsport_softc *psc; + + eh = mtod(m, struct ether_header *); + if (eh->ether_type != htons(ETHERTYPE_MVSW_ETAG)) + goto drop; + + if (m->m_len < hlen) { + m = m_pullup(m, hlen); + if (m == NULL) { + /* drop++ */ + return; + } + + eh = mtod(m, struct ether_header *); + } + + etag = (struct mvsw_etag *)(eh + 1); + tag0 = bemtoh16(&etag->tag0); + + int port = (tag0 >> MVSW_TAG_PORT_SHIFT) & MVSW_TAG_PORT_MASK; + if (port >= sc->sc_nports) + goto drop; + + psc = sc->sc_ports[port]; + if (psc == NULL) + goto drop; + + memmove(mtod(m, caddr_t) + diff, mtod(m, caddr_t), + offsetof(struct ether_header, ether_type)); + m_adj(m, diff); + + m = mvsport_input(psc, m); + if (m == NULL) + return; + + ether_input(ifp0, m); + return; + +drop: + m_freem(m); +} + +static int +mvsw_print(void *aux, const char *pnp) +{ + int node = *(int *)aux; + int port; + + if (pnp != NULL) + printf("\"port\" at %s", pnp); + + port = OF_getpropint(node, "reg", 0); + printf(" port %d", port); + + return (UNCONF); } static inline int @@ -219,6 +940,53 @@ mvsw_smi_write(struct mvsw_softc *sc, in mvsw_smi_wait(sc); } +static int +mvsw_wait(struct mvsw_softc *sc, int phy, int reg, uint16_t mask, uint16_t v, + const char *wmesg) +{ + unsigned int i; + uint16_t r; + + for (i = 0; i < 16; i++) { + r = mvsw_smi_read(sc, phy, reg); + if ((r & mask) == v) + return (0); + + tsleep_nsec(&sc->sc_mdio, PPAUSE, wmesg, 1500000); + } + + return (-1); +} + +static int +mvsw_vtu_op(struct mvsw_softc *sc, uint16_t op) +{ + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_VTU_OP, + MVSW_G1_VTU_OP_BUSY | op); + + return (mvsw_vtu_wait(sc)); +} + +static int +mvsw_atu_op(struct mvsw_softc *sc, uint16_t fid, uint16_t op, uint16_t data) +{ + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_ATU_DATA, data); + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_ATU_FID, fid); + mvsw_smi_write(sc, MVSW_G1, MVSW_G1_VTU_OP, + MVSW_G1_VTU_OP_BUSY | op); + + return (mvsw_atu_wait(sc)); +} + +static int +mvsw_irl_op(struct mvsw_softc *sc, uint16_t op) +{ + mvsw_smi_write(sc, MVSW_G2, MVSW_G2_IRL_CMD, + MVSW_G2_IRL_CMD_BUSY | op); + + return (mvsw_irl_wait(sc)); +} + int mvsw_phy_wait(struct mvsw_softc *sc) { @@ -308,23 +1076,6 @@ mvsw_serdes_write(struct mvsw_softc *sc, } void -mvsw_port_enable(struct mvsw_softc *sc, int node) -{ - uint16_t val; - int port; - - port = OF_getpropint(node, "reg", -1); - if (port == -1) - return; - - /* Enable port. */ - val = mvsw_smi_read(sc, MVSW_PORT(port), MVSW_PORT_CTRL); - val &= ~MVSW_PORT_CTRL_STATE_MASK; - val |= MVSW_PORT_CTRL_STATE_FORWARD; - mvsw_smi_write(sc, MVSW_PORT(port), MVSW_PORT_CTRL, val); -} - -void mvsw_phy_enable(struct mvsw_softc *sc, int node) { uint16_t val; @@ -357,4 +1108,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: dev/fdt/rkpciephy.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/rkpciephy.c,v retrieving revision 1.1 diff -u -p -r1.1 rkpciephy.c --- dev/fdt/rkpciephy.c 19 Mar 2023 11:17:16 -0000 1.1 +++ dev/fdt/rkpciephy.c 29 Mar 2023 06:41:50 -0000 @@ -48,6 +48,7 @@ struct rkpciephy_softc { bus_space_handle_t sc_ioh; struct phy_device sc_pd; + unsigned int sc_pd_enabled; }; int rkpciephy_match(struct device *, void *, void *); @@ -95,6 +96,11 @@ rkpciephy_enable(void *cookie, uint32_t uint32_t grf, stat; int timo; + if (sc->sc_pd_enabled) { + /* turn this into a refcnt when we can disable phys */ + return (0); + } + grf = OF_getpropint(node, "rockchip,phy-grf", 0); rm = regmap_byphandle(grf); if (rm == NULL) @@ -133,5 +139,6 @@ rkpciephy_enable(void *cookie, uint32_t return ETIMEDOUT; } + sc->sc_pd_enabled = 1; return 0; } Index: dev/fdt/rkpmic.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/rkpmic.c,v retrieving revision 1.12 diff -u -p -r1.12 rkpmic.c --- dev/fdt/rkpmic.c 12 Oct 2022 13:39:50 -0000 1.12 +++ dev/fdt/rkpmic.c 29 Mar 2023 06:41:50 -0000 @@ -332,13 +332,19 @@ rkpmic_attach(struct device *parent, str sc->sc_rtc_status_reg = RK809_RTC_STATUS; sc->sc_regdata = rk817_regdata; } - printf(": %s\n", chip); + printf(": %s v %u\n", chip, rkpmic_reg_read(sc, 0xee)); node = OF_getnodebyname(node, "regulators"); if (node == 0) return; for (node = OF_child(node); node; node = OF_peer(node)) rkpmic_attach_regulator(sc, node); + + printf("%s: en %02x %02x %02x %02x\n", sc->sc_dev.dv_xname, + rkpmic_reg_read(sc, 0xb1), + rkpmic_reg_read(sc, 0xb2), + rkpmic_reg_read(sc, 0xb3), + rkpmic_reg_read(sc, 0xb4)); } struct rkpmic_regulator { @@ -393,7 +399,8 @@ rkpmic_get_voltage(void *cookie) uint32_t ret = 0; vsel = rkpmic_reg_read(rr->rr_sc, rr->rr_reg) & rr->rr_mask; - +printf("%s: node %u vsel %u\n", __func__, rr->rr_rd.rd_node, vsel); + while (vsel_range->base) { ret = vsel_range->base; if (vsel >= vsel_range->vsel_min && Index: dev/fdt/rkpmu.c =================================================================== RCS file: dev/fdt/rkpmu.c diff -N dev/fdt/rkpmu.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/fdt/rkpmu.c 29 Mar 2023 06:41:50 -0000 @@ -0,0 +1,140 @@ +/* $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. + */ + +/* + * Rockchip Power Management Unit + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +struct rkpmu_chip { + unsigned int pmu_pwr_gate; +}; + +static const struct rkpmu_chip rkpmu_rk3568 = { + .pmu_pwr_gate = 0xa0, +}; + +struct rkpmu_softc { + struct device sc_dev; + int sc_node; + const struct rkpmu_chip *sc_chip; + + struct regmap *sc_grf; +}; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + +static int rkpmu_match(struct device *, void *, void *); +static void rkpmu_attach(struct device *, struct device *, void *); + +static inline uint32_t rkpmu_rd(struct rkpmu_softc *, bus_size_t); +static inline void rkpmu_wr(struct rkpmu_softc *, bus_size_t, + uint32_t, uint32_t); + +const struct cfattach rkpmu_ca = { + sizeof (struct rkpmu_softc), rkpmu_match, rkpmu_attach +}; + +struct cfdriver rkpmu_cd = { + NULL, "rkpmu", DV_DULL +}; + +struct rkpmu_id { + const char *id_name; + const struct rkpmu_chip *id_chip; +}; + +static const struct rkpmu_id rkpmu_ids[] = { + { "rockchip,rk3568-power-controller", &rkpmu_rk3568 }, +}; + +static const struct rkpmu_chip * +rkpmu_lookup(struct fdt_attach_args *faa) +{ + size_t i; + + for (i = 0; i < nitems(rkpmu_ids); i++) { + const struct rkpmu_id *id = &rkpmu_ids[i]; + if (OF_is_compatible(faa->fa_node, id->id_name)) + return (id->id_chip); + } + + return (NULL); +} + +static int +rkpmu_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return (rkpmu_lookup(faa) != NULL ? 2 : 0); +} + +static void +rkpmu_attach(struct device *parent, struct device *self, void *aux) +{ + struct rkpmu_softc *sc = (struct rkpmu_softc *)self; + struct fdt_attach_args *faa = aux; + int pnode; + + sc->sc_node = faa->fa_node; + sc->sc_chip = rkpmu_lookup(faa); + + pnode = OF_parent(sc->sc_node); + sc->sc_grf = regmap_bynode(pnode); + if (sc->sc_grf == NULL) { + printf(": no registers\n"); + return; + } + + printf(": pwr %04x status %04x req %04x idle %04x ack %04x", + rkpmu_rd(sc, 0xa0), + rkpmu_rd(sc, 0x98), + rkpmu_rd(sc, 0x50), + rkpmu_rd(sc, 0x68), + rkpmu_rd(sc, 0x60)); + + // https://github.com/jaredmcneill/quartz64_uefi/blob/main/edk2-rockchip/Platform/Firefly/ROC-RK3566-PC/Drivers/BoardInitDxe/BoardInitDxe.c#L440 + /* Enable automatic clock gating */ + rkpmu_wr(sc, 0x70, 0xffff, 0xffff); + rkpmu_wr(sc, 0x74, 0x000f, 0x000f); + + printf("\n"); +} + +static inline uint32_t +rkpmu_rd(struct rkpmu_softc *sc, bus_size_t r) +{ + return (regmap_read_4(sc->sc_grf, r) & 0xffff); +} + +static inline void +rkpmu_wr(struct rkpmu_softc *sc, bus_size_t r, uint32_t m, uint32_t v) +{ + regmap_write_4(sc->sc_grf, r, (m << 16) | v); +} Index: dev/fdt/rkusbphy.c =================================================================== RCS file: dev/fdt/rkusbphy.c diff -N dev/fdt/rkusbphy.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/fdt/rkusbphy.c 29 Mar 2023 06:41:50 -0000 @@ -0,0 +1,241 @@ +/* $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. + */ + +/* + * Rockchip USB2PHY with Innosilicon IP + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * chip stuff + */ + +struct rkusbphy_reg { + bus_size_t r_offs; +}; + +struct rkusbphy_port_regs { + struct rkusbphy_reg pr_phy_sus; + struct rkusbphy_reg pr_bvalid_det_en; +}; + +struct rkusbphy_chip { + bus_addr_t c_base_addr; + struct rkusbphy_port_regs c_regs; +}; + +static const struct rkusbphy_chip rkusbphy_rk3568[] = { + { + .c_base_addr = 0xfe8a0000, + }, + { + .c_base_addr = 0xfe8b0000, + }, +}; + +/* + * driver stuff + */ + +struct rkusbphy_softc { + struct device sc_dev; + const struct rkusbphy_chip *sc_chip; + struct regmap *sc_grf; + int sc_node; + + struct phy_device sc_otg_phy; + struct phy_device sc_host_phy; +}; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + +static int rkusbphy_match(struct device *, void *, void *); +static void rkusbphy_attach(struct device *, struct device *, void *); + +static int rkusbphy_otg_phy_enable(void *, uint32_t *); +static int rkusbphy_host_phy_enable(void *, uint32_t *); + +struct rkusbphy_port_config { + const char *pc_name; + int (*pc_enable)(void *, uint32_t *); +}; + +static void rkusbphy_register(struct rkusbphy_softc *, + struct phy_device *, const struct rkusbphy_port_config *); + +static const struct rkusbphy_port_config rkusbphy_otg_config = { + .pc_name = "otg-port", + .pc_enable = rkusbphy_otg_phy_enable, +}; + +static const struct rkusbphy_port_config rkusbphy_host_config = { + .pc_name = "host-port", + .pc_enable = rkusbphy_host_phy_enable, +}; + +const struct cfattach rkusbphy_ca = { + sizeof (struct rkusbphy_softc), rkusbphy_match, rkusbphy_attach +}; + +struct cfdriver rkusbphy_cd = { + NULL, "rkusbphy", DV_DULL +}; + +struct rkusbphy_id { + const char *id_name; + const struct rkusbphy_chip *id_chips; + size_t id_nchips; +}; + +#define RKUSBPHY_ID(_n, _c) { _n, _c, nitems(_c) } + +static const struct rkusbphy_id rkusbphy_ids[] = { + RKUSBPHY_ID("rockchip,rk3568-usb2phy", rkusbphy_rk3568), +}; + +static const struct rkusbphy_id * +rkusbphy_lookup(struct fdt_attach_args *faa) +{ + size_t i; + + for (i = 0; i < nitems(rkusbphy_ids); i++) { + const struct rkusbphy_id *id = &rkusbphy_ids[i]; + if (OF_is_compatible(faa->fa_node, id->id_name)) + return (id); + } + + return (NULL); +} + +static int +rkusbphy_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return (rkusbphy_lookup(faa) != NULL ? 1 : 0); +} + +static void +rkusbphy_attach(struct device *parent, struct device *self, void *aux) +{ + struct rkusbphy_softc *sc = (struct rkusbphy_softc *)self; + struct fdt_attach_args *faa = aux; + const struct rkusbphy_id *id = rkusbphy_lookup(faa); + size_t i; + uint32_t grfph; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + for (i = 0; i < id->id_nchips; i++) { + const struct rkusbphy_chip *c = &id->id_chips[i]; + if (faa->fa_reg[0].addr == c->c_base_addr) { + printf(": phy %zu\n", i); + sc->sc_chip = c; + break; + } + } + if (sc->sc_chip == NULL) { + printf(": unknown base address 0x%llu\n", faa->fa_reg[0].addr); + return; + } + + sc->sc_node = faa->fa_node; + + grfph = OF_getpropint(sc->sc_node, "rockchip,usbgrf", 0); + sc->sc_grf = regmap_byphandle(grfph); + if (sc->sc_grf == NULL) { + printf("%s: rockchip,usbgrf 0x%x not found\n", DEVNAME(sc), + grfph); + return; + } + + reset_assert_all(sc->sc_node); + + rkusbphy_register(sc, &sc->sc_otg_phy, &rkusbphy_otg_config); + rkusbphy_register(sc, &sc->sc_host_phy, &rkusbphy_host_config); +} + +static void +rkusbphy_register(struct rkusbphy_softc *sc, struct phy_device *pd, + const struct rkusbphy_port_config *pc) +{ + char status[32]; + int node; + + node = OF_getnodebyname(sc->sc_node, pc->pc_name); + if (node == 0) { + printf("%s: cannot find %s\n", DEVNAME(sc), pc->pc_name); + return; + } + + if (OF_getprop(node, "status", status, sizeof(status)) > 0 && + strcmp(status, "disabled") == 0) + return; + + pd->pd_node = node; + pd->pd_cookie = sc; + pd->pd_enable = pc->pc_enable; + phy_register(pd); +} + +static void +rkusbphy_phy_supply(struct rkusbphy_softc *sc, int node) +{ + int phandle; + + phandle = OF_getpropint(node, "phy-supply", 0); + if (phandle == 0) + return; + + regulator_enable(phandle); +} + +static int +rkusbphy_otg_phy_enable(void *cookie, uint32_t *cells) +{ + struct rkusbphy_softc *sc = cookie; + + rkusbphy_phy_supply(sc, sc->sc_otg_phy.pd_node); + + return (EINVAL); +} + +static int +rkusbphy_host_phy_enable(void *cookie, uint32_t *cells) +{ + struct rkusbphy_softc *sc = cookie; + + rkusbphy_phy_supply(sc, sc->sc_host_phy.pd_node); + + return (EINVAL); +} Index: dev/fdt/rkvdom.c =================================================================== RCS file: dev/fdt/rkvdom.c diff -N dev/fdt/rkvdom.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/fdt/rkvdom.c 29 Mar 2023 06:41:50 -0000 @@ -0,0 +1,204 @@ +/* $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. + */ + +/* + * Rockchip Voltage Domain management + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +struct rkvdom_chip { + const char *name; +}; + +#define RK3568_PMU_GRF_IO_VSEL0 0x140 +#define RK3568_POC_VCCIO2_CTRL_MASK (1 << 0) +#define RK3568_POC_VCCIO2_CTRL_GPIO_0A7 (0 << 0) +#define RK3568_POC_VCCIO2_CTRL_GRF (1 << 0) +#define RK3568_POC_VCCIO_SEL18(_i) (1 << ((_i) + 0)) /* 1 to 7 */ +#define RK3568_PMU_GRF_IO_VSEL1 0x144 +#define RK3568_POC_VCCIO_SEL33(_i) (1 << ((_i) + 0)) /* 1 to 7 */ +#define RK3568_PMU_GRF_IO_VSEL2 0x148 +#define RK3568_POC_PMUIO2_SEL18 (1 << 1) +#define RK3568_POC_PMUIO2_SEL25 (1 << 3) +#define RK3568_POC_PMUIO2_SEL33 (1 << 5) +#define RK3568_POC_PMUIO2_SEL_MASK ( RK3568_POC_PMUIO2_SEL18 | \ + RK3568_POC_PMUIO2_SEL25 | \ + RK3568_POC_PMUIO2_SEL33) + +static const struct rkvdom_chip rkvdom_rk3568_pmu = { + .name = "PMU", +}; + +struct rkvdom_softc { + struct device sc_dev; + int sc_node; + const struct rkvdom_chip *sc_chip; + + struct regmap *sc_grf; +}; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + +static int rkvdom_match(struct device *, void *, void *); +static void rkvdom_attach(struct device *, struct device *, void *); + +static inline void rkvdom_wr(struct rkvdom_softc *, bus_size_t, + uint32_t, uint32_t); + +const struct cfattach rkvdom_ca = { + sizeof (struct rkvdom_softc), rkvdom_match, rkvdom_attach +}; + +struct cfdriver rkvdom_cd = { + NULL, "rkvdom", DV_DULL +}; + +struct rkvdom_id { + const char *id_name; + const struct rkvdom_chip *id_chip; +}; + +static const struct rkvdom_id rkvdom_ids[] = { + { "rockchip,rk3568-pmu-io-voltage-domain", &rkvdom_rk3568_pmu }, +}; + +static const struct rkvdom_chip * +rkvdom_lookup(struct fdt_attach_args *faa) +{ + size_t i; + + for (i = 0; i < nitems(rkvdom_ids); i++) { + const struct rkvdom_id *id = &rkvdom_ids[i]; + if (OF_is_compatible(faa->fa_node, id->id_name)) + return (id->id_chip); + } + + return (NULL); +} + +static int +rkvdom_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return (rkvdom_lookup(faa) != NULL ? 2 : 0); +} + +static void +rkvdom_attach(struct device *parent, struct device *self, void *aux) +{ + struct rkvdom_softc *sc = (struct rkvdom_softc *)self; + struct fdt_attach_args *faa = aux; + int grf, phandle; + uint32_t uv, sel; + unsigned int i; + + sc->sc_node = faa->fa_node; + sc->sc_chip = rkvdom_lookup(faa); + + if (sc->sc_chip->name != NULL) + printf(": %s", sc->sc_chip->name); + + printf("\n"); + + grf = OF_parent(sc->sc_node); + sc->sc_grf = regmap_bynode(grf); + if (sc->sc_grf == NULL) { + /* fall back? */ + grf = OF_getpropint(sc->sc_node, "rockchip,grf", 0); + sc->sc_grf = regmap_byphandle(grf); + if (sc->sc_grf == NULL) { + printf("%s: can't find grf\n", DEVNAME(sc)); + return; + } + } + + phandle = OF_getpropint(sc->sc_node, "pmuio2-supply", 0); + if (phandle) { + uv = regulator_get_voltage(phandle); + printf("%s: %s %u\n", DEVNAME(sc), "pmuio2-supply", uv); +#if 0 + sel = (uv > 1980000) ? + RK3568_POC_PMUIO2_SEL33 : RK3568_POC_PMUIO2_SEL18; +#else + sel = RK3568_POC_PMUIO2_SEL33; +#endif + rkvdom_wr(sc, RK3568_PMU_GRF_IO_VSEL2, + RK3568_POC_VCCIO2_CTRL_MASK, sel); + } + + phandle = OF_getpropint(sc->sc_node, "vccio2-supply", 0); + if (phandle) { + rkvdom_wr(sc, RK3568_PMU_GRF_IO_VSEL0, + RK3568_POC_PMUIO2_SEL_MASK, RK3568_POC_VCCIO2_CTRL_GRF); + } + + uint32_t uvs[] = { + [1] = 3300000, + [3] = 1800000, + [4] = 1800000, + [5] = 3300000, + [6] = 1800000, + [7] = 3300000, + }; + + for (i = 1; i <= 7; i++) { + char supply[32]; + + snprintf(supply, sizeof(supply), "vccio%u-supply", i); + phandle = OF_getpropint(sc->sc_node, supply, 0); + if (!phandle) + continue; + + uv = regulator_get_voltage(phandle); + printf("%s: %s %u (%u)\n", DEVNAME(sc), supply, uv, uvs[i]); + uv = uvs[i]; + if (uv == 0) + continue; + if (uv > 1980000) { + rkvdom_wr(sc, RK3568_PMU_GRF_IO_VSEL0, + RK3568_POC_VCCIO_SEL18(i), 0); + rkvdom_wr(sc, RK3568_PMU_GRF_IO_VSEL1, + RK3568_POC_VCCIO_SEL33(i), + RK3568_POC_VCCIO_SEL33(i)); + } else { + rkvdom_wr(sc, RK3568_PMU_GRF_IO_VSEL1, + RK3568_POC_VCCIO_SEL33(i), 0); + rkvdom_wr(sc, RK3568_PMU_GRF_IO_VSEL0, + RK3568_POC_VCCIO_SEL18(i), + RK3568_POC_VCCIO_SEL18(i)); + } + } +} + +static inline void +rkvdom_wr(struct rkvdom_softc *sc, bus_size_t r, uint32_t m, uint32_t v) +{ + regmap_write_4(sc->sc_grf, r, (m << 16) | v); +} Index: dev/fdt/vreg.c =================================================================== RCS file: dev/fdt/vreg.c diff -N dev/fdt/vreg.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ dev/fdt/vreg.c 29 Mar 2023 06:41:50 -0000 @@ -0,0 +1,74 @@ +/* $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. + */ + +/* + * fixed voltage regulator + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +struct vreg_softc { + struct device sc_dev; + struct regulator_device sc_rd; + char sc_name[32]; +}; +#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) + +static int vreg_match(struct device *, void *, void *); +static void vreg_attach(struct device *, struct device *, void *); + +const struct cfattach vreg_ca = { + sizeof (struct vreg_softc), vreg_match, vreg_attach +}; + +struct cfdriver vreg_cd = { + NULL, "vreg", DV_DULL +}; + +static int +vreg_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return (OF_is_compatible(faa->fa_node, "regulator-fixed")); +} + +static void +vreg_attach(struct device *parent, struct device *self, void *aux) +{ + struct vreg_softc *sc = (struct vreg_softc *)self; + struct fdt_attach_args *faa = aux; + char name[64]; + int len; + + len = OF_getprop(faa->fa_node, "name", name, sizeof(name)); + printf(": \"%s\"\n", name); + + sc->sc_rd.rd_node = faa->fa_node; + regulator_register(&sc->sc_rd); +} Index: dev/fdt/xhci_fdt.c =================================================================== RCS file: /cvs/src/sys/dev/fdt/xhci_fdt.c,v retrieving revision 1.22 diff -u -p -r1.22 xhci_fdt.c --- dev/fdt/xhci_fdt.c 10 Mar 2023 10:22:55 -0000 1.22 +++ dev/fdt/xhci_fdt.c 29 Mar 2023 06:41:50 -0000 @@ -362,32 +362,16 @@ xhci_init_phy(struct xhci_fdt_softc *sc, } void -xhci_init_phys(struct xhci_fdt_softc *sc) +xhci_phy_enable(struct xhci_fdt_softc *sc, char *name) { uint32_t *phys; uint32_t *phy; - uint32_t usb_phy; - int len, idx; - - /* - * Legacy binding; assume there only is a single USB PHY. - */ - usb_phy = OF_getpropint(sc->sc_node, "usb-phy", 0); - if (usb_phy) { - xhci_init_phy(sc, &usb_phy); - return; - } + int idx, len; - /* - * Generic PHY binding; only initialize USB 3 PHY for now. - */ - idx = OF_getindex(sc->sc_node, "usb3-phy", "phy-names"); + idx = OF_getindex(sc->sc_node, name, "phy-names"); if (idx < 0) return; - if (phy_enable_idx(sc->sc_node, idx) != ENXIO) - return; - len = OF_getproplen(sc->sc_node, "phys"); if (len <= 0) return; @@ -407,6 +391,26 @@ xhci_init_phys(struct xhci_fdt_softc *sc idx--; } free(phys, M_TEMP, len); +} + +void +xhci_init_phys(struct xhci_fdt_softc *sc) +{ + int rv; + + rv = phy_enable_prop_idx(sc->sc_node, "usb-phy", 0); + if (rv != 0) { + rv = phy_enable(sc->sc_node, "usb2-phy"); + if (rv != 0) + xhci_phy_enable(sc, "usb2-phy"); + } + + rv = phy_enable_prop_idx(sc->sc_node, "usb-phy", 1); + if (rv != 0) { + rv = phy_enable(sc->sc_node, "usb3-phy"); + if (rv != 0) + xhci_phy_enable(sc, "usb3-phy"); + } } /* Index: dev/ic/dwqe.c =================================================================== RCS file: /cvs/src/sys/dev/ic/dwqe.c,v retrieving revision 1.3 diff -u -p -r1.3 dwqe.c --- dev/ic/dwqe.c 22 Mar 2023 21:41:28 -0000 1.3 +++ dev/ic/dwqe.c 29 Mar 2023 06:41:50 -0000 @@ -21,6 +21,7 @@ */ #include "bpfilter.h" +#include "kstat.h" #include #include @@ -54,6 +55,10 @@ #include #endif +#if NKSTAT > 0 +#include +#endif + #include #include @@ -101,6 +106,10 @@ void dwqe_dmamem_free(struct dwqe_softc struct mbuf *dwqe_alloc_mbuf(struct dwqe_softc *, bus_dmamap_t); void dwqe_fill_rx_ring(struct dwqe_softc *); +#if NKSTAT > 0 +void dwqe_kstat_create(struct dwqe_softc *); +#endif + int dwqe_attach(struct dwqe_softc *sc) { @@ -224,6 +233,10 @@ dwqe_attach(struct dwqe_softc *sc) if_attach(ifp); ether_ifattach(ifp); +#if NKSTAT > 0 + dwqe_kstat_create(sc); +#endif + /* Disable interrupts. */ dwqe_write(sc, GMAC_INT_EN, 0); dwqe_write(sc, GMAC_CHAN_INTR_ENA(0), 0); @@ -1151,3 +1164,94 @@ dwqe_fill_rx_ring(struct dwqe_softc *sc) if (if_rxr_inuse(&sc->sc_rx_ring) == 0) timeout_add(&sc->sc_rxto, 1); } + +#if NKSTAT > 0 +struct dwqe_mmc_stat { + const char *mmc_name; + unsigned int mmc_off; + enum kstat_kv_unit mmc_unit; +}; + +static const struct dwqe_mmc_stat dwqe_mmc_stats[] = { + { "Tx", 0x14, KSTAT_KV_U_BYTES }, + { "Tx", 0x18, KSTAT_KV_U_PACKETS }, + { "TxBroadcastGood", 0x1c, KSTAT_KV_U_PACKETS }, + { "TxMulticastGood", 0x20, KSTAT_KV_U_PACKETS }, + { "Tx64B", 0x24, KSTAT_KV_U_PACKETS }, + { "Tx65-127B", 0x28, KSTAT_KV_U_PACKETS }, + { "Tx128-255B", 0x2c, KSTAT_KV_U_PACKETS }, + { "Tx256-511B", 0x30, KSTAT_KV_U_PACKETS }, + { "Tx512-1023B", 0x34, KSTAT_KV_U_PACKETS }, + { "Tx1024-MaxB", 0x38, KSTAT_KV_U_PACKETS }, + { "TxUnicast", 0x3c, KSTAT_KV_U_PACKETS }, + { "TxMulticast", 0x40, KSTAT_KV_U_PACKETS }, + { "TxBroadcast", 0x44, KSTAT_KV_U_PACKETS }, + { "TxUnderflowErrs", 0x48, KSTAT_KV_U_PACKETS }, + { "TxCarrierErrs", 0x60, KSTAT_KV_U_PACKETS }, + { "TxGood", 0x64, KSTAT_KV_U_BYTES }, + { "TxGood", 0x68, KSTAT_KV_U_PACKETS }, + { "Rx", 0x80, KSTAT_KV_U_PACKETS }, + { "Rx", 0x84, KSTAT_KV_U_BYTES }, + { "RxGood", 0x88, KSTAT_KV_U_BYTES }, + { "RxMulticast", 0x90, KSTAT_KV_U_PACKETS }, + { "RxCRCErrs", 0x94, KSTAT_KV_U_PACKETS }, + { "RxLenErrs", 0xc8, KSTAT_KV_U_PACKETS }, + { "RxFifoOverflow", 0xd4, KSTAT_KV_U_PACKETS }, +}; + +int dwqe_kstat_read(struct kstat *ks); + +void +dwqe_kstat_create(struct dwqe_softc *sc) +{ + struct kstat *ks; + struct kstat_kv *kvs; + size_t i; + + ks = kstat_create(sc->sc_dev.dv_xname, 0, "dwqe-stats", 0, + KSTAT_T_KV, 0); + if (ks == NULL) + return; + + kvs = mallocarray(nitems(dwqe_mmc_stats), sizeof(*kvs), + M_DEVBUF, M_WAITOK|M_ZERO); + for (i = 0; i < nitems(dwqe_mmc_stats); i++) { + const struct dwqe_mmc_stat *mmc = &dwqe_mmc_stats[i]; + + kstat_kv_unit_init(&kvs[i], mmc->mmc_name, + KSTAT_KV_T_COUNTER32, mmc->mmc_unit); + } + + rw_init(&sc->sc_kstat_lock, "dwqekstat"); + + ks->ks_softc = sc; + kstat_set_wlock(ks, &sc->sc_kstat_lock); + ks->ks_data = kvs; + ks->ks_datalen = nitems(dwqe_mmc_stats) * sizeof(*kvs); + ks->ks_read = dwqe_kstat_read; + + sc->sc_kstat = ks; + + kstat_install(ks); +} + +int +dwqe_kstat_read(struct kstat *ks) +{ + struct dwqe_softc *sc = ks->ks_softc; + struct kstat_kv *kvs = ks->ks_data; + size_t i; + + for (i = 0; i < nitems(dwqe_mmc_stats); i++) { + const struct dwqe_mmc_stat *mmc = &dwqe_mmc_stats[i]; + + kstat_kv_u32(&kvs[i]) = + dwqe_read(sc, GMAC_MMC_BASE + mmc->mmc_off); + } + + nanouptime(&ks->ks_updated); + + return (0); +} + +#endif /* NKSTAT > 0 */ Index: dev/ic/dwqereg.h =================================================================== RCS file: /cvs/src/sys/dev/ic/dwqereg.h,v retrieving revision 1.2 diff -u -p -r1.2 dwqereg.h --- dev/ic/dwqereg.h 16 Feb 2023 14:43:53 -0000 1.2 +++ dev/ic/dwqereg.h 29 Mar 2023 06:41:50 -0000 @@ -84,6 +84,16 @@ #define GMAC_MAC_ADDR0_HI 0x0300 #define GMAC_MAC_ADDR0_LO 0x0304 +#define GMAC_MMC_BASE 0x0700 +#define GMAC_MMC_CONTROL 0x0000 +#define GMAC_MMC_RX_INTERRUPT 0x0004 +#define GMAC_MMC_TX_INTERRUPT 0x0008 +#define GMAC_MMC_RX_INTERRUPT_MASK \ + 0x000c +#define GMAC_MMC_TX_INTERRUPT_MASK \ + 0x0010 + + #define GMAC_MTL_OPERATION_MODE 0x0c00 #define GMAC_MTL_FRPE (1 << 15) #define GMAC_MTL_OPERATION_SCHALG_MASK (0x3 << 5) Index: dev/ic/dwqevar.h =================================================================== RCS file: /cvs/src/sys/dev/ic/dwqevar.h,v retrieving revision 1.2 diff -u -p -r1.2 dwqevar.h --- dev/ic/dwqevar.h 19 Mar 2023 09:46:40 -0000 1.2 +++ dev/ic/dwqevar.h 29 Mar 2023 06:41:50 -0000 @@ -94,6 +94,9 @@ struct dwqe_softc { int sc_rd_osr_lmt; uint32_t sc_blen[7]; + + struct kstat *sc_kstat; + struct rwlock sc_kstat_lock; }; #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) Index: dev/ofw/ofw_misc.c =================================================================== RCS file: /cvs/src/sys/dev/ofw/ofw_misc.c,v retrieving revision 1.38 diff -u -p -r1.38 ofw_misc.c --- dev/ofw/ofw_misc.c 17 Dec 2022 11:54:32 -0000 1.38 +++ dev/ofw/ofw_misc.c 29 Mar 2023 06:41:50 -0000 @@ -119,6 +119,46 @@ regmap_read_4(struct regmap *rm, bus_siz return bus_space_read_4(rm->rm_tag, rm->rm_handle, offset); } +/* + * Network interface support. + */ + +LIST_HEAD(, if_device) if_devices = + LIST_HEAD_INITIALIZER(if_devices); + +void +if_register(struct if_device *ifd) +{ + ifd->if_phandle = OF_getpropint(ifd->if_node, "phandle", 0); + + LIST_INSERT_HEAD(&if_devices, ifd, if_list); +} + +struct ifnet * +if_bynode(int node) +{ + struct if_device *ifd; + + LIST_FOREACH(ifd, &if_devices, if_list) { + if (ifd->if_node == node) + return (ifd->if_ifp); + } + + return (NULL); +} + +struct ifnet * +if_byphandle(uint32_t phandle) +{ + struct if_device *ifd; + + LIST_FOREACH(ifd, &if_devices, if_list) { + if (ifd->if_phandle == phandle) + return (ifd->if_ifp); + } + + return (NULL); +} /* * PHY support. @@ -207,7 +247,7 @@ phy_next_phy(uint32_t *cells) } int -phy_enable_idx(int node, int idx) +phy_enable_prop_idx(int node, char *prop, int idx) { uint32_t *phys; uint32_t *phy; @@ -219,7 +259,7 @@ phy_enable_idx(int node, int idx) return -1; phys = malloc(len, M_TEMP, M_WAITOK); - OF_getpropintarray(node, "phys", phys, len); + OF_getpropintarray(node, prop, phys, len); phy = phys; while (phy && phy < phys + (len / sizeof(uint32_t))) { @@ -233,6 +273,12 @@ phy_enable_idx(int node, int idx) free(phys, M_TEMP, len); return rv; +} + +int +phy_enable_idx(int node, int idx) +{ + return (phy_enable_prop_idx(node, "phys", idx)); } int Index: dev/ofw/ofw_misc.h =================================================================== RCS file: /cvs/src/sys/dev/ofw/ofw_misc.h,v retrieving revision 1.27 diff -u -p -r1.27 ofw_misc.h --- dev/ofw/ofw_misc.h 12 Dec 2022 19:18:25 -0000 1.27 +++ dev/ofw/ofw_misc.h 29 Mar 2023 06:41:50 -0000 @@ -30,6 +30,23 @@ struct regmap *regmap_byphandle(uint32_t uint32_t regmap_read_4(struct regmap *, bus_size_t); void regmap_write_4(struct regmap *, bus_size_t, uint32_t); +/* Interface support */ + +struct ifnet; + +struct if_device { + int if_node; + struct ifnet *if_ifp; + + LIST_ENTRY(if_device) if_list; + uint32_t if_phandle; +}; + +void if_register(struct if_device *); + +struct ifnet *if_bynode(int); +struct ifnet *if_byphandle(uint32_t); + /* PHY support */ #define PHY_NONE 0 @@ -51,6 +68,7 @@ struct phy_device { void phy_register(struct phy_device *); +int phy_enable_prop_idx(int, char *, int); int phy_enable_idx(int, int); int phy_enable(int, const char *); Index: dev/ofw/ofw_regulator.c =================================================================== RCS file: /cvs/src/sys/dev/ofw/ofw_regulator.c,v retrieving revision 1.16 diff -u -p -r1.16 ofw_regulator.c --- dev/ofw/ofw_regulator.c 14 Feb 2022 12:54:43 -0000 1.16 +++ dev/ofw/ofw_regulator.c 29 Mar 2023 06:41:50 -0000 @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "kstat.h" + #include #include #include @@ -24,6 +26,10 @@ #include #include +#if NKSTAT > 0 +#include +#endif + #define REGULATOR_VOLTAGE 0 #define REGULATOR_CURRENT 1 @@ -34,6 +40,10 @@ int regulator_type(int); uint32_t regulator_gpio_get(int); int regulator_gpio_set(int, uint32_t); +#if NKSTAT > 0 +void regulator_kstat_create(struct regulator_device *); +#endif + void regulator_register(struct regulator_device *rd) { @@ -68,10 +78,17 @@ regulator_register(struct regulator_devi rd->rd_set_current(rd->rd_cookie, rd->rd_amp_max); } +#if NKSTAT > 0 + regulator_kstat_create(rd); +#endif + rd->rd_phandle = OF_getpropint(rd->rd_node, "phandle", 0); if (rd->rd_phandle == 0) return; + if (OF_getproplen(rd->rd_node, "regulator-boot-on") == 0) + regulator_enable(rd->rd_phandle); + LIST_INSERT_HEAD(®ulator_devices, rd, rd_list); } @@ -435,3 +452,98 @@ regulator_gpio_set(int node, uint32_t va return 0; } + +#if NKSTAT > 0 +#include +#include + +struct rwlock regulator_kstat_lock = RWLOCK_INITIALIZER("regkslk"); + +struct regulator_kstats { + struct kstat_kv rks_v_cur; + struct kstat_kv rks_v_min; + struct kstat_kv rks_v_max; + + struct kstat_kv rks_name; +}; + +int regulator_kstat_read(struct kstat *); + +void +regulator_kstat_create(struct regulator_device *rd) +{ + struct kstat *ks; + struct regulator_kstats *rks; + int len; + size_t mlen = sizeof(*rks); + + len = OF_getproplen(rd->rd_node, "name"); + len = roundup(len, KSTAT_KV_ALIGN); + + mlen = sizeof(*rks) + roundup(len, KSTAT_KV_ALIGN); + rks = malloc(mlen, M_DEVBUF, M_WAITOK|M_ZERO); + + kstat_kv_init(&rks->rks_v_cur, "uV", KSTAT_KV_T_UINT32); + kstat_kv_init(&rks->rks_v_min, "uV min", KSTAT_KV_T_UINT32); + kstat_kv_u32(&rks->rks_v_min) = rd->rd_volt_min; + kstat_kv_init(&rks->rks_v_max, "uV max", KSTAT_KV_T_UINT32); + kstat_kv_u32(&rks->rks_v_max) = rd->rd_volt_max; + + kstat_kv_init(&rks->rks_name, "name", KSTAT_KV_T_STR); + kstat_kv_len(&rks->rks_name) = len; + OF_getprop(rd->rd_node, "name", rks + 1, len); + + ks = kstat_create("regulator", 0, "regulator", rd->rd_node, + KSTAT_T_KV, 0); + if (ks == NULL) { + free(rks, M_DEVBUF, mlen); + return; + } + + kstat_set_rlock(ks, ®ulator_kstat_lock); + ks->ks_softc = rd; + ks->ks_data = rks; + ks->ks_datalen = mlen; + ks->ks_read = regulator_kstat_read; + + kstat_install(ks); +} + +struct regulator_kstat_task { + struct kstat *ks; + struct cond c; +}; + +void +regulator_kstat_task(void *arg) +{ + struct regulator_kstat_task *s = arg; + struct kstat *ks = s->ks; + struct regulator_device *rd = ks->ks_softc; + struct regulator_kstats *rks = ks->ks_data; + + if (rd->rd_phandle) { + kstat_kv_u32(&rks->rks_v_cur) = + regulator_get_voltage(rd->rd_phandle); + } else if (rd->rd_get_voltage) { + kstat_kv_u32(&rks->rks_v_cur) = + rd->rd_get_voltage(rd->rd_cookie); + } + + nanouptime(&ks->ks_updated); + + cond_signal(&s->c); +} + +int +regulator_kstat_read(struct kstat *ks) +{ + struct regulator_kstat_task s = { ks, COND_INITIALIZER() }; + struct task t = TASK_INITIALIZER(regulator_kstat_task, &s); + + task_add(systq, &t); + cond_wait(&s.c, "regrd"); + + return (0); +} +#endif