From: Kirill A. Korinsky Subject: sys/cnmac: support CN71xx 1000BASE-X ports To: OpenBSD tech , visa@openbsd.org Date: Mon, 20 Apr 2026 22:07:40 +0200 tech@, visa@, Some CN71xx boards describe active GMX ports only in the PIP device tree, and mark the CPU facing link as cavium,sgmii-mac-1000x-mode with cavium,disable-autonegotiation, but without a PHY handle. OpenBSD otherwise trusts GMX0_INF_MODE for port discovery and insists on a PHY attach in cn30xxgmx_attach(), so such ports never reach cnmac with a usable media setup. Enumerate CN71xx SGMII ports from pip/interface@N when that description is present, carry the 1000x and disable-autonegotiation flags into the per port state, and let cnmac seed fixed 1000baseSX full duplex media for that case. Ports that still use a normal SGMII PHY path continue to go through cn30xxsmi_get_phy() and mii_attach() unchanged. Tested on two CN71xx Octeon systems: Juniper SRX300, which uses 1000BASE-X DT ports, and Ubiquiti EdgeRouter 4, which does not. This diff brings only 0/0 port on SRX300 and requires to use that command in uboot: usb reset; dhcp; fatload usb 0 ${loadaddr} boot; bootoctlinux rootdev=sd0 numcores=2 but it boots and I have network. Ok? Index: sys/arch/octeon/dev/cn30xxgmx.c =================================================================== RCS file: /home/cvs/src/sys/arch/octeon/dev/cn30xxgmx.c,v diff -u -p -r1.55 cn30xxgmx.c --- sys/arch/octeon/dev/cn30xxgmx.c 8 Jul 2024 08:07:45 -0000 1.55 +++ sys/arch/octeon/dev/cn30xxgmx.c 20 Apr 2026 17:56:27 -0000 @@ -169,21 +169,63 @@ cn30xxgmx_match(struct device *parent, v } int -cn30xxgmx_get_phy_phandle(int interface, int port) +cn30xxgmx_get_phy_phandle(int interface, int port, int *port_1000x, + int *disable_an) { char name[64]; int node; int phandle = 0; + if (port_1000x != NULL) + *port_1000x = 0; + if (disable_an != NULL) + *disable_an = 0; + snprintf(name, sizeof(name), "/soc/pip@11800a0000000/interface@%x/ethernet@%x", interface, port); node = OF_finddevice(name); - if (node != - 1) + if (node != -1) { phandle = OF_getpropint(node, "phy-handle", 0); + if (port_1000x != NULL) { + *port_1000x = OF_getproplen(node, + "cavium,sgmii-mac-1000x-mode") >= 0; + } + if (disable_an != NULL) { + *disable_an = OF_getproplen(node, + "cavium,disable-autonegotiation") >= 0; + } + } return phandle; } +int +cn30xxgmx_init_cn71xx(struct cn30xxgmx_softc *sc) +{ + char name[64]; + int child, node, reg; + + snprintf(name, sizeof(name), + "/soc/pip@11800a0000000/interface@%x", sc->sc_unitno); + node = OF_finddevice(name); + if (node == -1) + return 1; + + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + if (!OF_is_compatible(child, "cavium,octeon-3860-pip-port")) + continue; + reg = OF_getpropint(child, "reg", -1); + if (reg < 0 || reg >= nitems(sc->sc_port_types)) + continue; + + sc->sc_port_types[reg] = GMX_SGMII_PORT; + if (sc->sc_nports < reg + 1) + sc->sc_nports = reg + 1; + } + + return sc->sc_nports == 0 ? 1 : 0; +} + void cn30xxgmx_attach(struct device *parent, struct device *self, void *aux) { @@ -193,6 +235,7 @@ cn30xxgmx_attach(struct device *parent, struct cn30xxgmx_softc *sc = (void *)self; struct cn30xxsmi_softc *smi; int i; + int phandle; int phy_addr; int port; int status; @@ -224,15 +267,20 @@ cn30xxgmx_attach(struct device *parent, printf("\n"); for (i = 0; i < sc->sc_nports; i++) { + port_sc = &sc->sc_ports[i]; if (sc->sc_port_types[i] == GMX_AGL_PORT) port = 24; else port = GMX_PORT_NUM(sc->sc_unitno, i); - if (cn30xxsmi_get_phy(cn30xxgmx_get_phy_phandle(sc->sc_unitno, - i), port, &smi, &phy_addr)) + phandle = cn30xxgmx_get_phy_phandle(sc->sc_unitno, i, + &port_sc->sc_port_1000x, + &port_sc->sc_port_disable_an); + smi = NULL; + phy_addr = -1; + if (!(port_sc->sc_port_1000x && phandle == 0) && + cn30xxsmi_get_phy(phandle, port, &smi, &phy_addr)) continue; - port_sc = &sc->sc_ports[i]; port_sc->sc_port_gmx = sc; port_sc->sc_port_no = port; port_sc->sc_port_type = sc->sc_port_types[i]; @@ -421,6 +469,9 @@ cn30xxgmx_init(struct cn30xxgmx_softc *s break; } + if (cn30xxgmx_init_cn71xx(sc) == 0) + break; + inf_mode = bus_space_read_8(sc->sc_regt, sc->sc_regh, GMX0_INF_MODE); if ((inf_mode & INF_MODE_EN) == 0) @@ -1341,6 +1392,17 @@ cn30xxgmx_sgmii_enable(struct cn30xxgmx_ return 1; } + if (sc->sc_port_disable_an) { + CLR(ctl_reg, PCS_MR_CONTROL_AN_EN); + CLR(ctl_reg, PCS_MR_CONTROL_RST_AN); + CLR(ctl_reg, PCS_MR_CONTROL_PWR_DN); + CLR(ctl_reg, PCS_MR_CONTROL_SPDLSB); + SET(ctl_reg, PCS_MR_CONTROL_SPDMSB); + SET(ctl_reg, PCS_MR_CONTROL_DUPLEX); + PCS_WRITE_8(sc, PCS_MR_CONTROL, ctl_reg); + return 0; + } + /* Start a new SGMII autonegotiation. */ SET(ctl_reg, PCS_MR_CONTROL_AN_EN); SET(ctl_reg, PCS_MR_CONTROL_RST_AN); @@ -1382,6 +1444,8 @@ cn30xxgmx_sgmii_speed(struct cn30xxgmx_p misc_ctl = PCS_READ_8(sc, PCS_MISC_CTL); CLR(misc_ctl, PCS_MISC_CTL_SAMP_PT); + if (sc->sc_port_disable_an) + SET(misc_ctl, PCS_MISC_CTL_AN_OVRD); /* Disable the GMX port if the link is down. */ if (cn30xxgmx_link_status(sc)) Index: sys/arch/octeon/dev/cn30xxgmxvar.h =================================================================== RCS file: /home/cvs/src/sys/arch/octeon/dev/cn30xxgmxvar.h,v diff -u -p -r1.14 cn30xxgmxvar.h --- sys/arch/octeon/dev/cn30xxgmxvar.h 20 May 2024 23:13:33 -0000 1.14 +++ sys/arch/octeon/dev/cn30xxgmxvar.h 20 Apr 2026 17:28:22 -0000 @@ -61,6 +61,8 @@ struct cn30xxgmx_port_softc { bus_space_handle_t sc_port_regh; int sc_port_no; /* GMX0:0, GMX0:1, ... */ int sc_port_type; + int sc_port_1000x; + int sc_port_disable_an; uint64_t sc_link; struct mii_data *sc_port_mii; struct arpcom *sc_port_ac; Index: sys/arch/octeon/dev/if_cnmac.c =================================================================== RCS file: /home/cvs/src/sys/arch/octeon/dev/if_cnmac.c,v diff -u -p -r1.86 if_cnmac.c --- sys/arch/octeon/dev/if_cnmac.c 20 May 2024 23:13:33 -0000 1.86 +++ sys/arch/octeon/dev/if_cnmac.c 20 Apr 2026 17:28:22 -0000 @@ -471,6 +471,19 @@ cnmac_mediainit(struct cnmac_softc *sc) ifmedia_init(&sc->sc_mii.mii_media, 0, cnmac_mediachange, cnmac_mediastatus); + if (sc->sc_gmx_port->sc_port_1000x) { + ifmedia_add(&sc->sc_mii.mii_media, + IFM_ETHER | IFM_1000_SX | IFM_FDX, 0, NULL); + ifmedia_set(&sc->sc_mii.mii_media, + IFM_ETHER | IFM_1000_SX | IFM_FDX); + sc->sc_mii.mii_media_status = IFM_AVALID | IFM_ACTIVE; + sc->sc_mii.mii_media_active = + IFM_ETHER | IFM_1000_SX | IFM_FDX; + ifp->if_baudrate = IF_Gbps(1); + ifp->if_link_state = LINK_STATE_FULL_DUPLEX; + return 0; + } + mii_attach(&sc->sc_dev, &sc->sc_mii, 0xffffffff, sc->sc_phy_addr, MII_OFFSET_ANY, MIIF_DOPAUSE); @@ -492,6 +505,14 @@ cnmac_mediastatus(struct ifnet *ifp, str { struct cnmac_softc *sc = ifp->if_softc; + if (sc->sc_gmx_port->sc_port_1000x) { + ifmr->ifm_status = sc->sc_mii.mii_media_status; + ifmr->ifm_active = (sc->sc_mii.mii_media_active & + ~IFM_ETH_FMASK) | + sc->sc_gmx_port->sc_port_flowflags; + return; + } + mii_pollstat(&sc->sc_mii); ifmr->ifm_status = sc->sc_mii.mii_media_status; ifmr->ifm_active = sc->sc_mii.mii_media_active; @@ -503,6 +524,9 @@ int cnmac_mediachange(struct ifnet *ifp) { struct cnmac_softc *sc = ifp->if_softc; + + if (sc->sc_gmx_port->sc_port_1000x) + return 0; if ((ifp->if_flags & IFF_UP) == 0) return 0; -- wbr, Kirill