Module Name: src Committed By: msaitoh Date: Wed Nov 6 16:02:53 UTC 2019
Modified Files: src/share/man/man4: micphy.4 src/sys/dev/mii: micphy.c Log Message: Add support for KSZ80[0456]1, KSZ8721, KSZ9[01]31, KS8731 and KSZ9477. It also supports LAN7430's internal PHY. First wrote by nisimura@ and added some device support by me. Not tested well. To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/share/man/man4/micphy.4 cvs rdiff -u -r1.8 -r1.9 src/sys/dev/mii/micphy.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man4/micphy.4 diff -u src/share/man/man4/micphy.4:1.2 src/share/man/man4/micphy.4:1.3 --- src/share/man/man4/micphy.4:1.2 Mon Mar 3 09:10:46 2014 +++ src/share/man/man4/micphy.4 Wed Nov 6 16:02:53 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: micphy.4,v 1.2 2014/03/03 09:10:46 wiz Exp $ +.\" $NetBSD: micphy.4,v 1.3 2019/11/06 16:02:53 msaitoh Exp $ .\" .\" Copyright (c) 2014 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -15,18 +15,20 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd March 3, 2014 +.Dd November 6, 2019 .Dt MICPHY 4 .Os .Sh NAME .Nm micphy -.Nd Micrel KSZ9021 10/100/1000 PHY driver +.Nd Micrel KSZ8xxx 10/100 and KSZ9xxx 10/100/1000 PHY driver .Sh SYNOPSIS .Cd "micphy* at mii? phy ?" .Sh DESCRIPTION The .Nm -driver currently supports only Micrel KSZ9021RN PHY. +driver currently supports KSZ80[2345689]1, KSZ87[23]x, KSZ90[23]1, KSZ9131 and +KSZ9477. +It also supports LAN7430's internal PHY. The driver has a fixup for .Xr evbarm/cpsw 4 which requires Gig-E PHYs to adjust RGMII signal timing. Index: src/sys/dev/mii/micphy.c diff -u src/sys/dev/mii/micphy.c:1.8 src/sys/dev/mii/micphy.c:1.9 --- src/sys/dev/mii/micphy.c:1.8 Mon Mar 25 07:34:13 2019 +++ src/sys/dev/mii/micphy.c Wed Nov 6 16:02:53 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: micphy.c,v 1.8 2019/03/25 07:34:13 msaitoh Exp $ */ +/* $NetBSD: micphy.c,v 1.9 2019/11/06 16:02:53 msaitoh Exp $ */ /*- * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. @@ -55,11 +55,11 @@ */ /* - * Driver for Micrel KSZ9021RN PHYs + * Driver for Micrel KSZ8xxx 10/100 and KSZ9xxx 10/100/1000 PHY. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: micphy.c,v 1.8 2019/03/25 07:34:13 msaitoh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: micphy.c,v 1.9 2019/11/06 16:02:53 msaitoh Exp $"); #include "opt_mii.h" @@ -87,19 +87,98 @@ CFATTACH_DECL3_NEW(micphy, sizeof(struct DVF_DETACH_SHUTDOWN); static int micphy_service(struct mii_softc *, struct mii_data *, int); +static void micphy_status(struct mii_softc *); static void micphy_fixup(struct mii_softc *, int, int, device_t); static const struct mii_phy_funcs micphy_funcs = { - micphy_service, ukphy_status, micphy_reset, + micphy_service, micphy_status, micphy_reset, +}; + +struct micphy_softc { + struct mii_softc sc_mii; + uint32_t sc_lstype; /* Type of link status register */ }; static const struct mii_phydesc micphys[] = { - MII_PHY_DESC(MICREL, KSZ8081), - MII_PHY_DESC(MICREL, KSZ9021RNI), + MII_PHY_DESC(MICREL, KSZ8041), + MII_PHY_DESC(MICREL, KSZ8051), /* +8021,8031 */ + MII_PHY_DESC(MICREL, KSZ8061), + MII_PHY_DESC(MICREL, KSZ8081), /* +8051,8091 */ + MII_PHY_DESC(MICREL, KS8737), + MII_PHY_DESC(MICREL, KSZ9021_8001_8721), + MII_PHY_DESC(MICREL, KSZ9031), + MII_PHY_DESC(MICREL, KSZ9131), + MII_PHY_DESC(MICREL, KSZ9477), /* +LAN7430internal */ MII_PHY_END, }; -#define MII_KSZ8081_PHYCTL2 0x1f +/* + * Model Rev. Media LSTYPE Devices + * + * 0x11 100 1F_42 KSZ8041 + * 0x13 100 1F_42? KSZ8041RNLI + * 0x15 ? 100 1E_20 KSZ8051 + * 0x5 100 1E_20 KSZ8021 + * 0x6 100 1E_20 KSZ8031 + * 0x16 ? 100 1E_20 KSZ8081 + * ? 100 1E_20 KSZ8091 + * 0x17 100 1E_20 KSZ8061 + * 0x21 0x0 giga GIGA KSZ9021 + * 0x1 giga GIGA KSZ9021RLRN + * 0x9 100 1F_42 KSZ8721BL/SL + * 0x9 100 none? KSZ8721CL + * 0xa 100 1F_42 KSZ8001 + * 0x22 giga GIGA KSZ9031 + * 0x23 1? gigasw GIGA KSZ9477 (No master/slave bit) + * 5? giga GIGA LAN7430internal + * 0x24 giga GIGA KSZ9131 + * 0x32 100 1F_42 KS8737 + */ + +/* Type of link status register */ +#define MICPHYF_LSTYPE_DEFAULT 0 +#define MICPHYF_LSTYPE_1F_42 1 +#define MICPHYF_LSTYPE_1E_20 2 +#define MICPHYF_LSTYPE_GIGA 3 + +/* Return if the device is Gigabit (KSZ9021) */ +#define KSZ_MODEL21H_GIGA(rev) \ + ((((rev) & 0x0e) == 0) ? true : false) + +#define KSZ_XREG_CONTROL 0x0b +#define KSZ_XREG_WRITE 0x0c +#define KSZ_XREG_READ 0x0d +#define KSZ_XREG_CTL_SEL_READ 0x0000 +#define KSZ_XREG_CTL_SEL_WRITE 0x8000 + +#define REG_RGMII_CLOCK_AND_CONTROL 0x104 +#define REG_RGMII_RX_DATA 0x105 + +/* PHY control 1 register for 10/100 PHYs (KSZ80[235689]1) */ +#define KSZ8051_PHYCTL1 0x1e +#define KSZ8051_PHY_LINK 0x0100 +#define KSZ8051_PHY_MDIX 0x0020 +#define KSZ8051_PHY_FDX 0x0004 +#define KSZ8051_PHY_SPD_MASK 0x0003 +#define KSZ8051_PHY_SPD_10T 0x0001 +#define KSZ8051_PHY_SPD_100TX 0x0002 + +/* PHY control 2 register for 10/100 PHYs (KSZ8041, KSZ8721 and KSZ8001) */ +#define KSZ8041_PHYCTL2 0x1f +#define KSZ8041_PHY_ACOMP 0x0080 +#define KSZ8041_PHY_SPD_MASK 0x001c +#define KSZ8041_PHY_SPD_10T 0x0004 +#define KSZ8041_PHY_SPD_100TX 0x0008 +#define KSZ8041_PHY_FDX 0x0010 +#define KSZ8051_PHYCTL2 0x1f + +/* PHY control register for Gigabit PHYs */ +#define KSZ_GPHYCTL 0x1f +#define KSZ_GPHY_SPD_1000T 0x0040 +#define KSZ_GPHY_SPD_100TX 0x0020 +#define KSZ_GPHY_SPD_10T 0x0010 +#define KSZ_GPHY_FDX 0x0008 +#define KSZ_GPHY_1000T_MS 0x0004 static int micphymatch(device_t parent, cfdata_t match, void *aux) @@ -115,7 +194,8 @@ micphymatch(device_t parent, cfdata_t ma static void micphyattach(device_t parent, device_t self, void *aux) { - struct mii_softc *sc = device_private(self); + struct micphy_softc *msc = device_private(self); + struct mii_softc *sc = &msc->sc_mii; struct mii_attach_args *ma = aux; struct mii_data *mii = ma->mii_data; int model = MII_MODEL(ma->mii_id2); @@ -134,6 +214,25 @@ micphyattach(device_t parent, device_t s sc->mii_flags = ma->mii_flags; sc->mii_anegticks = MII_ANEGTICKS; + if ((sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8041) + || (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8041RNLI) + || (sc->mii_mpd_model == MII_MODEL_MICREL_KS8737) + || ((sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9021_8001_8721) + && !KSZ_MODEL21H_GIGA(sc->mii_mpd_rev))) { + msc->sc_lstype = MICPHYF_LSTYPE_1F_42; + } else if ((sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8051) + || (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8081) + || (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8061)) { + msc->sc_lstype = MICPHYF_LSTYPE_1E_20; + } else if (((sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9021_8001_8721) + && KSZ_MODEL21H_GIGA(sc->mii_mpd_rev)) + || (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9031) + || (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9477) + || (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ9131)) { + msc->sc_lstype = MICPHYF_LSTYPE_GIGA; + } else + msc->sc_lstype = MICPHYF_LSTYPE_DEFAULT; + PHY_RESET(sc); micphy_fixup(sc, model, rev, parent); @@ -164,10 +263,10 @@ micphy_reset(struct mii_softc *sc) * behavior). */ if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8081) - PHY_READ(sc, MII_KSZ8081_PHYCTL2, ®); + PHY_READ(sc, KSZ8051_PHYCTL2, ®); mii_phy_reset(sc); if (sc->mii_mpd_model == MII_MODEL_MICREL_KSZ8081) - PHY_WRITE(sc, MII_KSZ8081_PHYCTL2, reg); + PHY_WRITE(sc, KSZ8051_PHYCTL2, reg); } static int @@ -223,24 +322,15 @@ micphy_service(struct mii_softc *sc, str return 0; } -#define XREG_CONTROL 0x0b -#define XREG_WRITE 0x0c -#define XREG_READ 0x0d -#define XREG_CTL_SEL_READ 0x0000 -#define XREG_CTL_SEL_WRITE 0x8000 - -#define REG_RGMII_CLOCK_AND_CONTROL 0x104 -#define REG_RGMII_RX_DATA 0x105 - static void micphy_writexreg(struct mii_softc *sc, uint32_t reg, uint32_t wval) { uint16_t rval __debugused; - PHY_WRITE(sc, XREG_CONTROL, XREG_CTL_SEL_WRITE | reg); - PHY_WRITE(sc, XREG_WRITE, wval); - PHY_WRITE(sc, XREG_CONTROL, XREG_CTL_SEL_READ | reg); - PHY_READ(sc, XREG_READ, &rval); + PHY_WRITE(sc, KSZ_XREG_CONTROL, KSZ_XREG_CTL_SEL_WRITE | reg); + PHY_WRITE(sc, KSZ_XREG_WRITE, wval); + PHY_WRITE(sc, KSZ_XREG_CONTROL, KSZ_XREG_CTL_SEL_READ | reg); + PHY_READ(sc, KSZ_XREG_READ, &rval); KDASSERT(wval == rval); } @@ -248,7 +338,7 @@ static void micphy_fixup(struct mii_softc *sc, int model, int rev, device_t parent) { switch (model) { - case MII_MODEL_MICREL_KSZ9021RNI: + case MII_MODEL_MICREL_KSZ9021_8001_8721: if (!device_is_a(parent, "cpsw")) break; @@ -268,3 +358,77 @@ micphy_fixup(struct mii_softc *sc, int m return; } + +static void +micphy_status(struct mii_softc *sc) +{ + struct micphy_softc *msc = device_private(sc->mii_dev); + struct mii_data *mii = sc->mii_pdata; + uint16_t bmsr, bmcr, sr; + + /* For unknown devices */ + if (msc->sc_lstype == MICPHYF_LSTYPE_DEFAULT) { + ukphy_status(sc); + return; + } + + mii->mii_media_status = IFM_AVALID; + mii->mii_media_active = IFM_ETHER; + + PHY_READ(sc, MII_BMCR, &bmcr); + + PHY_READ(sc, MII_BMSR, &bmsr); + PHY_READ(sc, MII_BMSR, &bmsr); + if (bmsr & BMSR_LINK) + mii->mii_media_status |= IFM_ACTIVE; + + if (bmcr & BMCR_AUTOEN) { + if ((bmsr & BMSR_ACOMP) == 0) { + mii->mii_media_active |= IFM_NONE; + return; + } + } + + if (msc->sc_lstype == MICPHYF_LSTYPE_1F_42) { + PHY_READ(sc, KSZ8041_PHYCTL2, &sr); + if ((sr & KSZ8041_PHY_SPD_MASK) == 0) + mii->mii_media_active |= IFM_NONE; + else if (sr & KSZ8041_PHY_SPD_100TX) + mii->mii_media_active |= IFM_100_TX; + else if (sr & KSZ8041_PHY_SPD_10T) + mii->mii_media_active |= IFM_10_T; + if (sr & KSZ8041_PHY_FDX) + mii->mii_media_active |= IFM_FDX + | mii_phy_flowstatus(sc); + } else if (msc->sc_lstype == MICPHYF_LSTYPE_1E_20) { + PHY_READ(sc, KSZ8051_PHYCTL1, &sr); + if ((sr & KSZ8051_PHY_SPD_MASK) == 0) + mii->mii_media_active |= IFM_NONE; + else if (sr & KSZ8051_PHY_SPD_100TX) + mii->mii_media_active |= IFM_100_TX; + else if (sr & KSZ8051_PHY_SPD_10T) + mii->mii_media_active |= IFM_10_T; + if (sr & KSZ8051_PHY_FDX) + mii->mii_media_active |= IFM_FDX + | mii_phy_flowstatus(sc); + } else if (msc->sc_lstype == MICPHYF_LSTYPE_GIGA) { + /* 9021/9031/7430/9131 gphy */ + PHY_READ(sc, KSZ_GPHYCTL, &sr); + if (sr & KSZ_GPHY_SPD_1000T) + mii->mii_media_active |= IFM_1000_T; + else if (sr & KSZ_GPHY_SPD_100TX) + mii->mii_media_active |= IFM_100_TX; + else if (sr & KSZ_GPHY_SPD_10T) + mii->mii_media_active |= IFM_10_T; + else + mii->mii_media_active |= IFM_NONE; + if ((mii->mii_media_active & IFM_1000_T) + && (sr & KSZ_GPHY_1000T_MS)) + mii->mii_media_active |= IFM_ETH_MASTER; + if (sr & KSZ_GPHY_FDX) + mii->mii_media_active |= IFM_FDX + | mii_phy_flowstatus(sc); + else + mii->mii_media_active |= IFM_HDX; + } +}