diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2018-07-09 16:30:14 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2018-07-09 16:30:14 +0000 |
commit | b268a49f3e05445ca51e9eb8c564dee867b64f1f (patch) | |
tree | bf96bcb749bd2f7ba4550d2b6c7e600242d30fc5 /sys | |
parent | 4eccaa601667cfeb9badc608f3b78413e112acc0 (diff) |
Basic support for SFP modules in mvneta(4). SFP modules are basically
hotpluggable PHYs whose status can either be read using an I2C-connected
PHY, or using in-band status management implemented in the controller
itself over SGMII. With this, 802.3z SFPs work on mvneta(4).
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/fdt/if_mvneta.c | 198 |
1 files changed, 148 insertions, 50 deletions
diff --git a/sys/dev/fdt/if_mvneta.c b/sys/dev/fdt/if_mvneta.c index c8d563bedfa..4abea483923 100644 --- a/sys/dev/fdt/if_mvneta.c +++ b/sys/dev/fdt/if_mvneta.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_mvneta.c,v 1.4 2018/02/09 00:08:17 jmatthew Exp $ */ +/* $OpenBSD: if_mvneta.c,v 1.5 2018/07/09 16:30:13 patrick Exp $ */ /* $NetBSD: if_mvneta.c,v 1.41 2015/04/15 10:15:40 hsuenaga Exp $ */ /* * Copyright (c) 2007, 2008, 2013 KIYOHARA Takashi @@ -161,6 +161,7 @@ struct mvneta_softc { PHY_MODE_RGMII_ID, } sc_phy_mode; int sc_fixed_link; + int sc_inband_status; int sc_phy; int sc_link; }; @@ -182,6 +183,8 @@ int mvneta_intr(void *); void mvneta_start(struct ifnet *); int mvneta_ioctl(struct ifnet *, u_long, caddr_t); +void mvneta_inband_statchg(struct mvneta_softc *); +void mvneta_port_change(struct mvneta_softc *); void mvneta_port_up(struct mvneta_softc *); int mvneta_up(struct mvneta_softc *); void mvneta_down(struct mvneta_softc *); @@ -255,22 +258,30 @@ mvneta_miibus_statchg(struct device *self) MVNETA_WRITE(sc, MVNETA_PANC, panc); } - if (!!(sc->sc_mii.mii_media_status & IFM_ACTIVE) != sc->sc_link) { - sc->sc_link = !sc->sc_link; + mvneta_port_change(sc); +} - if (sc->sc_link) { - uint32_t panc = MVNETA_READ(sc, MVNETA_PANC); - panc &= ~MVNETA_PANC_FORCELINKFAIL; - panc |= MVNETA_PANC_FORCELINKPASS; - MVNETA_WRITE(sc, MVNETA_PANC, panc); - mvneta_port_up(sc); - } else { - uint32_t panc = MVNETA_READ(sc, MVNETA_PANC); - panc &= ~MVNETA_PANC_FORCELINKPASS; - panc |= MVNETA_PANC_FORCELINKFAIL; - MVNETA_WRITE(sc, MVNETA_PANC, panc); - } - } +void +mvneta_inband_statchg(struct mvneta_softc *sc) +{ + uint32_t reg; + + sc->sc_mii.mii_media_status = IFM_AVALID; + sc->sc_mii.mii_media_active = IFM_ETHER; + + reg = MVNETA_READ(sc, MVNETA_PS0); + if (reg & MVNETA_PS0_LINKUP) + sc->sc_mii.mii_media_status |= IFM_ACTIVE; + if (reg & MVNETA_PS0_GMIISPEED) + sc->sc_mii.mii_media_active |= IFM_1000_T; + else if (reg & MVNETA_PS0_MIISPEED) + sc->sc_mii.mii_media_active |= IFM_100_TX; + else + sc->sc_mii.mii_media_active |= IFM_10_T; + if (reg & MVNETA_PS0_FULLDX) + sc->sc_mii.mii_media_active |= IFM_FDX; + + mvneta_port_change(sc); } void @@ -337,9 +348,11 @@ mvneta_attach(struct device *parent, struct device *self, void *aux) { struct mvneta_softc *sc = (struct mvneta_softc *) self; struct fdt_attach_args *faa = aux; + uint32_t ctl0, ctl2, panc; struct ifnet *ifp; int i, len, node; char *phy_mode; + char *managed; printf("\n"); @@ -384,6 +397,17 @@ mvneta_attach(struct device *parent, struct device *self, void *aux) OF_child(faa->fa_node)) sc->sc_fixed_link = 1; + if ((len = OF_getproplen(faa->fa_node, "managed")) >= 0) { + managed = malloc(len, M_TEMP, M_WAITOK); + OF_getprop(faa->fa_node, "managed", managed, len); + if (!strncmp(managed, "in-band-status", + strlen("in-band-status"))) { + sc->sc_fixed_link = 1; + sc->sc_inband_status = 1; + } + free(managed, M_TEMP, len); + } + if (!sc->sc_fixed_link) { node = OF_getnodebyphandle(OF_getpropint(faa->fa_node, "phy", 0)); @@ -434,9 +458,10 @@ mvneta_attach(struct device *parent, struct device *self, void *aux) MVNETA_WRITE(sc, MVNETA_PMIC, 0); /* mask all interrupts */ - MVNETA_WRITE(sc, MVNETA_PRXTXTIM, 0); + MVNETA_WRITE(sc, MVNETA_PRXTXTIM, MVNETA_PRXTXTI_PMISCICSUMMARY); MVNETA_WRITE(sc, MVNETA_PRXTXIM, 0); - MVNETA_WRITE(sc, MVNETA_PMIM, 0); + MVNETA_WRITE(sc, MVNETA_PMIM, MVNETA_PMI_PHYSTATUSCHNG | + MVNETA_PMI_LINKCHANGE | MVNETA_PMI_PSCSYNCCHNG); MVNETA_WRITE(sc, MVNETA_PIE, 0); /* enable MBUS Retry bit16 */ @@ -478,15 +503,6 @@ mvneta_attach(struct device *parent, struct device *self, void *aux) MVNETA_WRITE(sc, MVNETA_EUC, MVNETA_READ(sc, MVNETA_EUC) & ~MVNETA_EUC_POLLING); - /* Disable Auto-Negotiation */ - MVNETA_WRITE(sc, MVNETA_PANC, - MVNETA_READ(sc, MVNETA_PANC) & ~(MVNETA_PANC_INBANDANEN | - MVNETA_PANC_ANSPEEDEN | MVNETA_PANC_ANDUPLEXEN)); - MVNETA_WRITE(sc, MVNETA_OMSCD, - MVNETA_READ(sc, MVNETA_OMSCD) & ~MVNETA_OMSCD_1MS_CLOCK_ENABLE); - MVNETA_WRITE(sc, MVNETA_PMACC2, - MVNETA_READ(sc, MVNETA_PMACC2) & ~MVNETA_PMACC2_INBANDAN); - /* clear uni-/multicast tables */ uint32_t dfut[MVNETA_NDFUT], dfsmt[MVNETA_NDFSMT], dfomt[MVNETA_NDFOMT]; memset(dfut, 0, sizeof(dfut)); @@ -502,27 +518,69 @@ mvneta_attach(struct device *parent, struct device *self, void *aux) MVNETA_WRITE(sc, MVNETA_EUIC, 0); /* Setup phy. */ - uint32_t ctrl = MVNETA_READ(sc, MVNETA_PMACC2); + ctl0 = MVNETA_READ(sc, MVNETA_PMACC0); + ctl2 = MVNETA_READ(sc, MVNETA_PMACC2); + panc = MVNETA_READ(sc, MVNETA_PANC); + + /* Force link down to change in-band settings. */ + panc &= ~MVNETA_PANC_FORCELINKPASS; + panc |= MVNETA_PANC_FORCELINKFAIL; + MVNETA_WRITE(sc, MVNETA_PANC, panc); + + ctl0 &= ~MVNETA_PMACC0_PORTTYPE; + ctl2 &= ~(MVNETA_PMACC2_PORTMACRESET | MVNETA_PMACC2_INBANDAN); + panc &= ~(MVNETA_PANC_INBANDANEN | MVNETA_PANC_INBANDRESTARTAN | + MVNETA_PANC_SETMIISPEED | MVNETA_PANC_SETGMIISPEED | + MVNETA_PANC_ANSPEEDEN | MVNETA_PANC_SETFCEN | + MVNETA_PANC_PAUSEADV | MVNETA_PANC_ANFCEN | + MVNETA_PANC_SETFULLDX | MVNETA_PANC_ANDUPLEXEN); + + ctl2 |= MVNETA_PMACC2_RGMIIEN; switch (sc->sc_phy_mode) { case PHY_MODE_QSGMII: MVNETA_WRITE(sc, MVNETA_SERDESCFG, MVNETA_SERDESCFG_QSGMII_PROTO); - ctrl |= MVNETA_PMACC2_PCSEN | MVNETA_PMACC2_RGMIIEN; + ctl2 |= MVNETA_PMACC2_PCSEN; break; case PHY_MODE_SGMII: MVNETA_WRITE(sc, MVNETA_SERDESCFG, MVNETA_SERDESCFG_SGMII_PROTO); - ctrl |= MVNETA_PMACC2_PCSEN | MVNETA_PMACC2_RGMIIEN; + ctl2 |= MVNETA_PMACC2_PCSEN; break; - case PHY_MODE_RGMII: - case PHY_MODE_RGMII_ID: - ctrl |= MVNETA_PMACC2_RGMIIEN; + default: break; } - ctrl &= ~MVNETA_PMACC2_PORTMACRESET; - MVNETA_WRITE(sc, MVNETA_PMACC2, ctrl); + /* Use Auto-Negotiation for Inband Status only */ + if (sc->sc_inband_status) { + panc &= ~(MVNETA_PANC_FORCELINKFAIL | + MVNETA_PANC_FORCELINKPASS); + /* TODO: read mode from SFP */ + if (1) { + /* 802.3z */ + ctl0 |= MVNETA_PMACC0_PORTTYPE; + panc |= (MVNETA_PANC_INBANDANEN | + MVNETA_PANC_SETGMIISPEED | + MVNETA_PANC_SETFULLDX); + } else { + /* SGMII */ + ctl2 |= MVNETA_PMACC2_INBANDAN; + panc |= (MVNETA_PANC_INBANDANEN | + MVNETA_PANC_ANSPEEDEN | + MVNETA_PANC_ANDUPLEXEN); + } + MVNETA_WRITE(sc, MVNETA_OMSCD, + MVNETA_READ(sc, MVNETA_OMSCD) | MVNETA_OMSCD_1MS_CLOCK_ENABLE); + } else { + MVNETA_WRITE(sc, MVNETA_OMSCD, + MVNETA_READ(sc, MVNETA_OMSCD) & ~MVNETA_OMSCD_1MS_CLOCK_ENABLE); + } + + MVNETA_WRITE(sc, MVNETA_PMACC0, ctl0); + MVNETA_WRITE(sc, MVNETA_PMACC2, ctl2); + MVNETA_WRITE(sc, MVNETA_PANC, panc); + /* Port reset */ while (MVNETA_READ(sc, MVNETA_PMACC2) & MVNETA_PMACC2_PORTMACRESET) ; @@ -589,9 +647,13 @@ mvneta_attach(struct device *parent, struct device *self, void *aux) ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL); - sc->sc_mii.mii_media_status = IFM_AVALID|IFM_ACTIVE; - sc->sc_mii.mii_media_active = IFM_ETHER|IFM_1000_T|IFM_FDX; - mvneta_miibus_statchg(self); + if (sc->sc_inband_status) { + mvneta_inband_statchg(sc); + } else { + sc->sc_mii.mii_media_status = IFM_AVALID|IFM_ACTIVE; + sc->sc_mii.mii_media_active = IFM_ETHER|IFM_1000_T|IFM_FDX; + mvneta_miibus_statchg(self); + } ifp->if_baudrate = ifmedia_baudrate(sc->sc_mii.mii_media_active); ifp->if_link_state = LINK_STATE_FULL_DUPLEX; @@ -657,13 +719,24 @@ mvneta_intr(void *arg) { struct mvneta_softc *sc = arg; struct ifnet *ifp = &sc->sc_ac.ac_if; - uint32_t ic; + uint32_t ic, misc; + + ic = MVNETA_READ(sc, MVNETA_PRXTXTIC); + + if (ic & MVNETA_PRXTXTI_PMISCICSUMMARY) { + misc = MVNETA_READ(sc, MVNETA_PMIC); + MVNETA_WRITE(sc, MVNETA_PMIC, 0); + if (sc->sc_inband_status && (misc & + (MVNETA_PMI_PHYSTATUSCHNG | + MVNETA_PMI_LINKCHANGE | + MVNETA_PMI_PSCSYNCCHNG))) { + mvneta_inband_statchg(sc); + } + } if (!(ifp->if_flags & IFF_RUNNING)) return 1; - ic = MVNETA_READ(sc, MVNETA_PRXTXTIC); - if (ic & MVNETA_PRXTXTI_TBTCQ(0)) mvneta_tx_proc(sc); @@ -791,6 +864,31 @@ mvneta_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr) } void +mvneta_port_change(struct mvneta_softc *sc) +{ + if (!!(sc->sc_mii.mii_media_status & IFM_ACTIVE) != sc->sc_link) { + sc->sc_link = !sc->sc_link; + + if (sc->sc_link) { + if (!sc->sc_inband_status) { + uint32_t panc = MVNETA_READ(sc, MVNETA_PANC); + panc &= ~MVNETA_PANC_FORCELINKFAIL; + panc |= MVNETA_PANC_FORCELINKPASS; + MVNETA_WRITE(sc, MVNETA_PANC, panc); + } + mvneta_port_up(sc); + } else { + if (!sc->sc_inband_status) { + uint32_t panc = MVNETA_READ(sc, MVNETA_PANC); + panc &= ~MVNETA_PANC_FORCELINKPASS; + panc |= MVNETA_PANC_FORCELINKFAIL; + MVNETA_WRITE(sc, MVNETA_PANC, panc); + } + } + } +} + +void mvneta_port_up(struct mvneta_softc *sc) { /* Enable port RX/TX. */ @@ -863,6 +961,7 @@ mvneta_up(struct mvneta_softc *sc) /* TODO: correct frame size */ MVNETA_WRITE(sc, MVNETA_PMACC0, + (MVNETA_READ(sc, MVNETA_PMACC0) & MVNETA_PMACC0_PORTTYPE) | MVNETA_PMACC0_FRAMESIZELIMIT(MCLBYTES - MVNETA_HWHEADER_SIZE)); /* set max MTU */ @@ -886,9 +985,10 @@ mvneta_up(struct mvneta_softc *sc) mvneta_port_up(sc); /* Enable interrupt masks */ - MVNETA_WRITE(sc, MVNETA_PRXTXTIM, - MVNETA_PRXTXTI_RBICTAPQ(0) | - MVNETA_PRXTXTI_TBTCQ(0)); + MVNETA_WRITE(sc, MVNETA_PRXTXTIM, MVNETA_PRXTXTI_RBICTAPQ(0) | + MVNETA_PRXTXTI_TBTCQ(0) | MVNETA_PRXTXTI_PMISCICSUMMARY); + MVNETA_WRITE(sc, MVNETA_PMIM, MVNETA_PMI_PHYSTATUSCHNG | + MVNETA_PMI_LINKCHANGE | MVNETA_PMI_PSCSYNCCHNG); timeout_add_sec(&sc->sc_tick_ch, 1); @@ -976,15 +1076,13 @@ mvneta_down(struct mvneta_softc *sc) MVNETA_READ(sc, MVNETA_PMACC0) & ~MVNETA_PMACC0_PORTEN); delay(200); + /* mask all interrupts */ + MVNETA_WRITE(sc, MVNETA_PRXTXTIM, MVNETA_PRXTXTI_PMISCICSUMMARY); + MVNETA_WRITE(sc, MVNETA_PRXTXIM, 0); + /* clear all cause registers */ MVNETA_WRITE(sc, MVNETA_PRXTXTIC, 0); MVNETA_WRITE(sc, MVNETA_PRXTXIC, 0); - MVNETA_WRITE(sc, MVNETA_PMIC, 0); - - /* mask all interrupts */ - MVNETA_WRITE(sc, MVNETA_PRXTXTIM, 0); - MVNETA_WRITE(sc, MVNETA_PRXTXIM, 0); - MVNETA_WRITE(sc, MVNETA_PMIM, 0); /* Free RX and TX mbufs still in the queues. */ for (i = 0; i < MVNETA_TX_RING_CNT; i++) { |