1*c574fa6dSskrll /* $NetBSD: mcommphy.c,v 1.3 2024/10/23 05:55:45 skrll Exp $ */ 217d21f01Sjmcneill 317d21f01Sjmcneill /* 417d21f01Sjmcneill * Copyright (c) 2022 Jared McNeill <jmcneill@invisible.ca> 517d21f01Sjmcneill * All rights reserved. 617d21f01Sjmcneill * 717d21f01Sjmcneill * Redistribution and use in source and binary forms, with or without 817d21f01Sjmcneill * modification, are permitted provided that the following conditions 917d21f01Sjmcneill * are met: 1017d21f01Sjmcneill * 1. Redistributions of source code must retain the above copyright 1117d21f01Sjmcneill * notice, this list of conditions and the following disclaimer. 1217d21f01Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 1317d21f01Sjmcneill * notice, this list of conditions and the following disclaimer in the 1417d21f01Sjmcneill * documentation and/or other materials provided with the distribution. 1517d21f01Sjmcneill * 1617d21f01Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 1717d21f01Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1817d21f01Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 1917d21f01Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2017d21f01Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2117d21f01Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2217d21f01Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2317d21f01Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2417d21f01Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2517d21f01Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2617d21f01Sjmcneill * POSSIBILITY OF SUCH DAMAGE. 2717d21f01Sjmcneill */ 2817d21f01Sjmcneill 2917d21f01Sjmcneill /* 3017d21f01Sjmcneill * Motorcomm YT8511C / YT8511H Integrated 10/100/1000 Gigabit Ethernet 3117d21f01Sjmcneill * Transceiver. 3217d21f01Sjmcneill */ 3317d21f01Sjmcneill 3417d21f01Sjmcneill #include <sys/cdefs.h> 35*c574fa6dSskrll __KERNEL_RCSID(0, "$NetBSD: mcommphy.c,v 1.3 2024/10/23 05:55:45 skrll Exp $"); 3617d21f01Sjmcneill 3717d21f01Sjmcneill #include <sys/param.h> 3817d21f01Sjmcneill #include <sys/systm.h> 3917d21f01Sjmcneill #include <sys/kernel.h> 4017d21f01Sjmcneill #include <sys/device.h> 4117d21f01Sjmcneill #include <sys/socket.h> 4217d21f01Sjmcneill #include <sys/errno.h> 4317d21f01Sjmcneill 4417d21f01Sjmcneill #include <net/if.h> 4517d21f01Sjmcneill #include <net/if_media.h> 4617d21f01Sjmcneill 4717d21f01Sjmcneill #include <dev/mii/mii.h> 4817d21f01Sjmcneill #include <dev/mii/miivar.h> 4917d21f01Sjmcneill #include <dev/mii/miidevs.h> 5017d21f01Sjmcneill 51812342bbSskrll #define YT85X1_MCOMMPHY_OUI 0x000000 52812342bbSskrll #define YT8511_MCOMMPHY_MODEL 0x10 53*c574fa6dSskrll #define YT8521_MCOMMPHY_MODEL 0x11 54812342bbSskrll #define YT85X1_MCOMMPHY_REV 0x0a 5517d21f01Sjmcneill 56812342bbSskrll #define YTPHY_EXT_REG_ADDR 0x1e 57812342bbSskrll #define YTPHY_EXT_REG_DATA 0x1f 5817d21f01Sjmcneill 5917d21f01Sjmcneill /* Extended registers */ 60812342bbSskrll #define YT8511_CLOCK_GATING_REG 0x0c 61812342bbSskrll #define YT8511_TX_CLK_DELAY_SEL __BITS(7,4) 62812342bbSskrll #define YT8511_CLK_25M_SEL __BITS(2,1) 63812342bbSskrll #define YT8511_CLK_25M_SEL_125M 3 64812342bbSskrll #define YT8511_RX_CLK_DELAY_EN __BIT(0) 65812342bbSskrll 66*c574fa6dSskrll #define YT8511_DELAY_DRIVE_REG 0x0d 67*c574fa6dSskrll #define YT8511_FE_TX_CLK_DELAY_SEL __BITS(15,12) 68*c574fa6dSskrll 69*c574fa6dSskrll #define YT8521_CLOCK_GATING_REG 0x0c 70*c574fa6dSskrll #define YT8521_RX_CLK_EN __BIT(12) 71*c574fa6dSskrll 72812342bbSskrll #define YT8511_SLEEP_CONTROL1_REG 0x27 73812342bbSskrll #define YT8511_PLLON_IN_SLP __BIT(14) 7417d21f01Sjmcneill 75*c574fa6dSskrll #define YT8521_SLEEP_CONTROL1_REG 0x27 76*c574fa6dSskrll #define YT8521_PLLON_IN_SLP __BIT(14) 77*c574fa6dSskrll 78*c574fa6dSskrll #define YT8521_EXT_CHIP_CONFIG 0xa001 79*c574fa6dSskrll #define YT8521_RXC_DLY_EN __BIT(8) 80*c574fa6dSskrll #define YT8521_CFG_LDO_MASK __BITS(5, 4) 81*c574fa6dSskrll #define YT8521_CFG_LDO_3V3 0 82*c574fa6dSskrll #define YT8521_CFG_LDO_2V5 1 83*c574fa6dSskrll #define YT8521_CFG_LDO_1V8 2 /* or 3 */ 84*c574fa6dSskrll 85*c574fa6dSskrll #define YT8521_EXT_SDS_CONFIG 0xa002 86*c574fa6dSskrll 87*c574fa6dSskrll #define YT8521_EXT_RGMII_CONFIG1 0xa003 88*c574fa6dSskrll #define YT8521_TX_CLK_SEL_INV __BIT(14) 89*c574fa6dSskrll #define YT8521_RX_DELAY_SEL_MASK __BITS(13, 10) 90*c574fa6dSskrll #define YT8521_FE_TX_DELAY_SEL_MASK __BITS(7, 4) 91*c574fa6dSskrll #define YT8521_GE_TX_DELAY_SEL_MASK __BITS(3, 0) 92*c574fa6dSskrll #define YT8521_DELAY_PS(ps) ((ps) / 150) 93*c574fa6dSskrll #define YT8521_DELAY_DEFAULT 1950 94*c574fa6dSskrll 95*c574fa6dSskrll #define YT8521_EXT_RGMII_CONFIG2 0xa004 96*c574fa6dSskrll 97*c574fa6dSskrll #define YT8521_EXT_PAD_DRIVE_STRENGTH 0xa010 98*c574fa6dSskrll #define YT8531_RGMII_RXC_DS_MASK __BITS(15, 13) 99*c574fa6dSskrll #define YT8531_RGMII_RXD_DS_HIMASK __BIT(12) 100*c574fa6dSskrll #define YT8531_RGMII_RXD_DS_LOMASK __BITS(5, 4) 101*c574fa6dSskrll #define YT8531_RGMII_RXD_DS_MASK \ 102*c574fa6dSskrll (YT8531_RGMII_RXD_DS_HIMASK | YT8531_RGMII_RXD_DS_LOMASK) 103*c574fa6dSskrll 104*c574fa6dSskrll #define YT8531_RGMII_RXD_DS_HIBITS __BIT(2) 105*c574fa6dSskrll #define YT8531_RGMII_RXD_DS_LOBITS __BITS(1, 0) 106*c574fa6dSskrll 107*c574fa6dSskrll CTASSERT(__SHIFTOUT_MASK(YT8531_RGMII_RXD_DS_HIMASK) == 108*c574fa6dSskrll __SHIFTOUT_MASK(YT8531_RGMII_RXD_DS_HIBITS)); 109*c574fa6dSskrll CTASSERT(__SHIFTOUT_MASK(YT8531_RGMII_RXD_DS_LOMASK) == 110*c574fa6dSskrll __SHIFTOUT_MASK(YT8531_RGMII_RXD_DS_LOBITS)); 111*c574fa6dSskrll 112*c574fa6dSskrll #define YT8531_DEFAULT_DS 3 113*c574fa6dSskrll 114*c574fa6dSskrll #define YT8521_EXT_SYNCE_CFG 0xa012 115*c574fa6dSskrll #define YT8531_EN_SYNC_E __BIT(6) 116*c574fa6dSskrll #define YT8531_CLK_FRE_SEL_125M __BIT(4) 117*c574fa6dSskrll #define YT8531_CLK_SRC_SEL_MASK __BITS(3, 1) 118*c574fa6dSskrll #define YT8531_CLK_SRC_SEL_PLL_125M 0 119*c574fa6dSskrll #define YT8531_CLK_SRC_SEL_REF_25M 4 120*c574fa6dSskrll 121*c574fa6dSskrll 122*c574fa6dSskrll typedef enum mcommtype { 123*c574fa6dSskrll YTUNK, 124*c574fa6dSskrll YT8511, 125*c574fa6dSskrll YT8521, 126*c574fa6dSskrll YT8531, 127*c574fa6dSskrll } mcommtype_t; 128*c574fa6dSskrll 129*c574fa6dSskrll struct mcomm_softc { 130*c574fa6dSskrll struct mii_softc sc_miisc; 131*c574fa6dSskrll 132*c574fa6dSskrll mcommtype_t sc_type; 133*c574fa6dSskrll 134*c574fa6dSskrll bool sc_tx_clk_adj_enabled; 135*c574fa6dSskrll bool sc_tx_clk_10_inverted; 136*c574fa6dSskrll bool sc_tx_clk_100_inverted; 137*c574fa6dSskrll bool sc_tx_clk_1000_inverted; 138*c574fa6dSskrll u_int sc_clk_out_frequency_hz; 139*c574fa6dSskrll u_int sc_rx_clk_drv_microamp; 140*c574fa6dSskrll u_int sc_rx_data_drv_microamp; 141*c574fa6dSskrll u_int sc_rx_internal_delay_ps; 142*c574fa6dSskrll u_int sc_tx_internal_delay_ps; 143*c574fa6dSskrll }; 144*c574fa6dSskrll 14517d21f01Sjmcneill static int mcommphymatch(device_t, cfdata_t, void *); 14617d21f01Sjmcneill static void mcommphyattach(device_t, device_t, void *); 14717d21f01Sjmcneill 148*c574fa6dSskrll CFATTACH_DECL_NEW(mcommphy, sizeof(struct mcomm_softc), 14917d21f01Sjmcneill mcommphymatch, mcommphyattach, mii_phy_detach, mii_phy_activate); 15017d21f01Sjmcneill 15117d21f01Sjmcneill static int mcommphy_service(struct mii_softc *, struct mii_data *, int); 152*c574fa6dSskrll static void mcommphy_reset(struct mii_softc *); 15317d21f01Sjmcneill 15417d21f01Sjmcneill static const struct mii_phy_funcs mcommphy_funcs = { 155*c574fa6dSskrll .pf_service = mcommphy_service, 156*c574fa6dSskrll .pf_status = ukphy_status, 157*c574fa6dSskrll .pf_reset = mcommphy_reset, 15817d21f01Sjmcneill }; 15917d21f01Sjmcneill 160*c574fa6dSskrll static const struct mii_phydesc mcommphys[] = { 161*c574fa6dSskrll MII_PHY_DESC(MOTORCOMM, YT8531), 162*c574fa6dSskrll MII_PHY_END, 163*c574fa6dSskrll }; 164*c574fa6dSskrll 165*c574fa6dSskrll static void 166*c574fa6dSskrll mcomm_yt8531properties(struct mcomm_softc *msc, prop_dictionary_t dict) 16717d21f01Sjmcneill { 168*c574fa6dSskrll struct mii_softc * const sc = &msc->sc_miisc; 169*c574fa6dSskrll 170*c574fa6dSskrll if (!prop_dictionary_get_bool(dict, "motorcomm,tx-clk-adj-enabled", 171*c574fa6dSskrll &msc->sc_tx_clk_adj_enabled)) { 172*c574fa6dSskrll msc->sc_tx_clk_adj_enabled = false; 173*c574fa6dSskrll } else { 174*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 175*c574fa6dSskrll "motorcomm,tx-clk-adj-enabled is %s\n", 176*c574fa6dSskrll msc->sc_tx_clk_adj_enabled ? "true" : "false"); 177*c574fa6dSskrll } 178*c574fa6dSskrll 179*c574fa6dSskrll if (!prop_dictionary_get_bool(dict, "motorcomm,tx-clk-10-inverted", 180*c574fa6dSskrll &msc->sc_tx_clk_10_inverted)) { 181*c574fa6dSskrll msc->sc_tx_clk_10_inverted = false; 182*c574fa6dSskrll } else { 183*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 184*c574fa6dSskrll "motorcomm,tx_clk_10_inverted is %s\n", 185*c574fa6dSskrll msc->sc_tx_clk_10_inverted ? "true" : "false"); 186*c574fa6dSskrll } 187*c574fa6dSskrll 188*c574fa6dSskrll if (!prop_dictionary_get_bool(dict, "motorcomm,tx-clk-100-inverted", 189*c574fa6dSskrll &msc->sc_tx_clk_100_inverted)) { 190*c574fa6dSskrll msc->sc_tx_clk_100_inverted = false; 191*c574fa6dSskrll } else { 192*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 193*c574fa6dSskrll "motorcomm,tx-clk-100-inverted is %s\n", 194*c574fa6dSskrll msc->sc_tx_clk_100_inverted ? "true" : "false"); 195*c574fa6dSskrll } 196*c574fa6dSskrll 197*c574fa6dSskrll if (!prop_dictionary_get_bool(dict, "motorcomm,tx-clk-1000-inverted", 198*c574fa6dSskrll &msc->sc_tx_clk_1000_inverted)) { 199*c574fa6dSskrll msc->sc_tx_clk_1000_inverted = false; 200*c574fa6dSskrll } else { 201*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 202*c574fa6dSskrll "motorcomm,tx-clk-1000-inverted is %s\n", 203*c574fa6dSskrll msc->sc_tx_clk_1000_inverted ? "true" : "false"); 204*c574fa6dSskrll } 205*c574fa6dSskrll 206*c574fa6dSskrll if (!prop_dictionary_get_uint32(dict, "motorcomm,clk-out-frequency-hz", 207*c574fa6dSskrll &msc->sc_clk_out_frequency_hz)) { 208*c574fa6dSskrll msc->sc_clk_out_frequency_hz = 0; 209*c574fa6dSskrll } else { 210*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 211*c574fa6dSskrll "motorcomm,clk-out-frequency-hz is %u\n", 212*c574fa6dSskrll msc->sc_clk_out_frequency_hz); 213*c574fa6dSskrll } 214*c574fa6dSskrll 215*c574fa6dSskrll if (!prop_dictionary_get_uint32(dict, "motorcomm,rx-clk-drv-microamp", 216*c574fa6dSskrll &msc->sc_rx_clk_drv_microamp)) { 217*c574fa6dSskrll aprint_debug_dev(sc->mii_dev, 218*c574fa6dSskrll "motorcomm,rx-clk-drv-microamp not found\n"); 219*c574fa6dSskrll } else { 220*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 221*c574fa6dSskrll "rx-clk-drv-microamp is %u\n", 222*c574fa6dSskrll msc->sc_rx_clk_drv_microamp); 223*c574fa6dSskrll } 224*c574fa6dSskrll 225*c574fa6dSskrll if (!prop_dictionary_get_uint32(dict, "motorcomm,rx-data-drv-microamp", 226*c574fa6dSskrll &msc->sc_rx_data_drv_microamp)) { 227*c574fa6dSskrll aprint_debug_dev(sc->mii_dev, 228*c574fa6dSskrll "motorcomm,rx-data-drv-microamp not found\n"); 229*c574fa6dSskrll } else { 230*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 231*c574fa6dSskrll "motorcomm,rx-data-drv-microamp is %u\n", 232*c574fa6dSskrll msc->sc_rx_data_drv_microamp); 233*c574fa6dSskrll } 234*c574fa6dSskrll 235*c574fa6dSskrll if (!prop_dictionary_get_uint32(dict, "rx-internal-delay-ps", 236*c574fa6dSskrll &msc->sc_rx_internal_delay_ps)) { 237*c574fa6dSskrll aprint_debug_dev(sc->mii_dev, 238*c574fa6dSskrll "rx-internal-delay-ps not found\n"); 239*c574fa6dSskrll } else { 240*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 241*c574fa6dSskrll "rx-internal-delay-ps is %u\n", 242*c574fa6dSskrll msc->sc_rx_internal_delay_ps); 243*c574fa6dSskrll } 244*c574fa6dSskrll 245*c574fa6dSskrll if (!prop_dictionary_get_uint32(dict, "tx-internal-delay-ps", 246*c574fa6dSskrll &msc->sc_tx_internal_delay_ps)) { 247*c574fa6dSskrll aprint_debug_dev(sc->mii_dev, 248*c574fa6dSskrll "tx-internal-delay-ps not found\n"); 249*c574fa6dSskrll } else { 250*c574fa6dSskrll aprint_verbose_dev(sc->mii_dev, 251*c574fa6dSskrll "tx-internal-delay-ps is %u\n", 252*c574fa6dSskrll msc->sc_tx_internal_delay_ps); 253*c574fa6dSskrll } 254*c574fa6dSskrll } 255*c574fa6dSskrll 256*c574fa6dSskrll static bool 257*c574fa6dSskrll mcommphy_isyt8511(struct mii_attach_args *ma) 258*c574fa6dSskrll { 25917d21f01Sjmcneill 26017d21f01Sjmcneill /* 26117d21f01Sjmcneill * The YT8511C reports an OUI of 0. Best we can do here is to match 26217d21f01Sjmcneill * exactly the contents of the PHY identification registers. 26317d21f01Sjmcneill */ 264812342bbSskrll if (MII_OUI(ma->mii_id1, ma->mii_id2) == YT85X1_MCOMMPHY_OUI && 265812342bbSskrll MII_MODEL(ma->mii_id2) == YT8511_MCOMMPHY_MODEL && 266812342bbSskrll MII_REV(ma->mii_id2) == YT85X1_MCOMMPHY_REV) { 267*c574fa6dSskrll return true; 26817d21f01Sjmcneill } 269*c574fa6dSskrll return false; 270*c574fa6dSskrll } 271*c574fa6dSskrll 272*c574fa6dSskrll 273*c574fa6dSskrll static int 274*c574fa6dSskrll mcommphymatch(device_t parent, cfdata_t match, void *aux) 275*c574fa6dSskrll { 276*c574fa6dSskrll struct mii_attach_args *ma = aux; 277*c574fa6dSskrll 278*c574fa6dSskrll if (mii_phy_match(ma, mcommphys) != NULL) 279*c574fa6dSskrll return 10; 280*c574fa6dSskrll 281*c574fa6dSskrll if (mcommphy_isyt8511(ma)) 282*c574fa6dSskrll return 10; 28317d21f01Sjmcneill 28417d21f01Sjmcneill return 0; 28517d21f01Sjmcneill } 28617d21f01Sjmcneill 28717d21f01Sjmcneill static void 288*c574fa6dSskrll mcomm_yt8511_init(struct mcomm_softc *msc) 28917d21f01Sjmcneill { 290*c574fa6dSskrll struct mii_softc *sc = &msc->sc_miisc; 29117d21f01Sjmcneill uint16_t oldaddr, data; 29217d21f01Sjmcneill 293812342bbSskrll PHY_READ(sc, YTPHY_EXT_REG_ADDR, &oldaddr); 29417d21f01Sjmcneill 295812342bbSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8511_CLOCK_GATING_REG); 296*c574fa6dSskrll 297812342bbSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 298*c574fa6dSskrll 299812342bbSskrll data &= ~YT8511_CLK_25M_SEL; 300812342bbSskrll data |= __SHIFTIN(YT8511_CLK_25M_SEL_125M, YT8511_CLK_25M_SEL); 30117d21f01Sjmcneill if (ISSET(sc->mii_flags, MIIF_RXID)) { 302812342bbSskrll data |= YT8511_RX_CLK_DELAY_EN; 30317d21f01Sjmcneill } else { 304812342bbSskrll data &= ~YT8511_RX_CLK_DELAY_EN; 30517d21f01Sjmcneill } 306812342bbSskrll data &= ~YT8511_TX_CLK_DELAY_SEL; 30717d21f01Sjmcneill if (ISSET(sc->mii_flags, MIIF_TXID)) { 308812342bbSskrll data |= __SHIFTIN(0xf, YT8511_TX_CLK_DELAY_SEL); 30917d21f01Sjmcneill } else { 310812342bbSskrll data |= __SHIFTIN(0x2, YT8511_TX_CLK_DELAY_SEL); 31117d21f01Sjmcneill } 312812342bbSskrll PHY_WRITE(sc, YTPHY_EXT_REG_DATA, data); 31317d21f01Sjmcneill 314812342bbSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8511_SLEEP_CONTROL1_REG); 315812342bbSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 316*c574fa6dSskrll 317812342bbSskrll data |= YT8511_PLLON_IN_SLP; 318*c574fa6dSskrll 319812342bbSskrll PHY_WRITE(sc, YTPHY_EXT_REG_DATA, data); 32017d21f01Sjmcneill 321812342bbSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, oldaddr); 322*c574fa6dSskrll } 323*c574fa6dSskrll 324*c574fa6dSskrll 325*c574fa6dSskrll 326*c574fa6dSskrll static void 327*c574fa6dSskrll mcomm_yt8521_init(struct mcomm_softc *msc) 328*c574fa6dSskrll { 329*c574fa6dSskrll struct mii_softc *sc = &msc->sc_miisc; 330*c574fa6dSskrll bool rx_delay_en = false; 331*c574fa6dSskrll u_int rx_delay_val; 332*c574fa6dSskrll u_int tx_delay_val; 333*c574fa6dSskrll uint16_t oldaddr, data; 334*c574fa6dSskrll 335*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_ADDR, &oldaddr); 336*c574fa6dSskrll 337*c574fa6dSskrll u_int rx_delay = msc->sc_rx_internal_delay_ps; 338*c574fa6dSskrll if (rx_delay <= 15 * 150 && rx_delay % 150 == 0) { 339*c574fa6dSskrll rx_delay_val = YT8521_DELAY_PS(rx_delay); 340*c574fa6dSskrll } else { 341*c574fa6dSskrll /* It's OK if this underflows */ 342*c574fa6dSskrll rx_delay -= 1900; 343*c574fa6dSskrll if (rx_delay <= 15 * 150 && rx_delay % 150 == 0) { 344*c574fa6dSskrll rx_delay_en = true; 345*c574fa6dSskrll rx_delay_val = YT8521_DELAY_PS(rx_delay); 346*c574fa6dSskrll } else { 347*c574fa6dSskrll rx_delay_val = YT8521_DELAY_PS(YT8521_DELAY_DEFAULT); 348*c574fa6dSskrll } 349*c574fa6dSskrll } 350*c574fa6dSskrll 351*c574fa6dSskrll u_int tx_delay = msc->sc_tx_internal_delay_ps; 352*c574fa6dSskrll if (tx_delay <= 15 * 150 && tx_delay % 150 == 0) { 353*c574fa6dSskrll tx_delay_val = YT8521_DELAY_PS(tx_delay); 354*c574fa6dSskrll } else { 355*c574fa6dSskrll tx_delay_val = YT8521_DELAY_PS(YT8521_DELAY_DEFAULT); 356*c574fa6dSskrll } 357*c574fa6dSskrll 358*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8521_EXT_CHIP_CONFIG); 359*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 360*c574fa6dSskrll 361*c574fa6dSskrll if (rx_delay_en) 362*c574fa6dSskrll data |= YT8521_RXC_DLY_EN; 363*c574fa6dSskrll else 364*c574fa6dSskrll data &= ~YT8521_RXC_DLY_EN; 365*c574fa6dSskrll 366*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_DATA, data); 367*c574fa6dSskrll 368*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8521_EXT_RGMII_CONFIG1); 369*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 370*c574fa6dSskrll 371*c574fa6dSskrll data &= ~(YT8521_RX_DELAY_SEL_MASK | YT8521_GE_TX_DELAY_SEL_MASK); 372*c574fa6dSskrll // XXX FE? 373*c574fa6dSskrll data |= 374*c574fa6dSskrll __SHIFTIN(rx_delay_val, YT8521_RX_DELAY_SEL_MASK) | 375*c574fa6dSskrll __SHIFTIN(tx_delay_val, YT8521_GE_TX_DELAY_SEL_MASK); 376*c574fa6dSskrll 377*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_DATA, data); 378*c574fa6dSskrll 379*c574fa6dSskrll /* Restore address register. */ 380*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, oldaddr); 381*c574fa6dSskrll } 382*c574fa6dSskrll 383*c574fa6dSskrll struct mcomm_ytphy_ds_map { 384*c574fa6dSskrll u_int microamps; 385*c574fa6dSskrll u_int ds; 386*c574fa6dSskrll }; 387*c574fa6dSskrll 388*c574fa6dSskrll struct mcomm_ytphy_ds_map mcomm_1d8v_ds_map[] = { 389*c574fa6dSskrll { .microamps = 1200, .ds = 0 }, 390*c574fa6dSskrll { .microamps = 2100, .ds = 1 }, 391*c574fa6dSskrll { .microamps = 2700, .ds = 2 }, 392*c574fa6dSskrll { .microamps = 2910, .ds = 3 }, 393*c574fa6dSskrll { .microamps = 3110, .ds = 4 }, 394*c574fa6dSskrll { .microamps = 3600, .ds = 5 }, 395*c574fa6dSskrll { .microamps = 3970, .ds = 6 }, 396*c574fa6dSskrll { .microamps = 4350, .ds = 7 }, 397*c574fa6dSskrll }; 398*c574fa6dSskrll struct mcomm_ytphy_ds_map mcomm_3d3v_ds_map[] = { 399*c574fa6dSskrll { .microamps = 3070, .ds = 0 }, 400*c574fa6dSskrll { .microamps = 4080, .ds = 1 }, 401*c574fa6dSskrll { .microamps = 4370, .ds = 2 }, 402*c574fa6dSskrll { .microamps = 4680, .ds = 3 }, 403*c574fa6dSskrll { .microamps = 5020, .ds = 4 }, 404*c574fa6dSskrll { .microamps = 5450, .ds = 5 }, 405*c574fa6dSskrll { .microamps = 5740, .ds = 6 }, 406*c574fa6dSskrll { .microamps = 6140, .ds = 7 }, 407*c574fa6dSskrll }; 408*c574fa6dSskrll 409*c574fa6dSskrll static u_int 410*c574fa6dSskrll mcomm_yt8531_ds(struct mcomm_softc *msc, 411*c574fa6dSskrll struct mcomm_ytphy_ds_map *ds_map, size_t n, 412*c574fa6dSskrll u_int microamps, u_int millivolts) 413*c574fa6dSskrll { 414*c574fa6dSskrll for (size_t i = 0; i < n; i++) { 415*c574fa6dSskrll if (ds_map[i].microamps == microamps) 416*c574fa6dSskrll return ds_map[i].ds; 417*c574fa6dSskrll } 418*c574fa6dSskrll if (microamps) { 419*c574fa6dSskrll struct mii_softc *sc = &msc->sc_miisc; 420*c574fa6dSskrll 421*c574fa6dSskrll aprint_error_dev(sc->mii_dev, "unknown drive strength " 422*c574fa6dSskrll "(%u uA at %u mV)", microamps, millivolts); 423*c574fa6dSskrll } 424*c574fa6dSskrll return YT8531_DEFAULT_DS; 425*c574fa6dSskrll } 426*c574fa6dSskrll 427*c574fa6dSskrll static void 428*c574fa6dSskrll mcomm_yt8531_init(struct mcomm_softc *msc) 429*c574fa6dSskrll { 430*c574fa6dSskrll struct mii_softc *sc = &msc->sc_miisc; 431*c574fa6dSskrll uint16_t oldaddr, data; 432*c574fa6dSskrll 433*c574fa6dSskrll mcomm_yt8521_init(msc); 434*c574fa6dSskrll 435*c574fa6dSskrll /* Save address register. */ 436*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_ADDR, &oldaddr); 437*c574fa6dSskrll 438*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8521_EXT_CHIP_CONFIG); 439*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 440*c574fa6dSskrll 441*c574fa6dSskrll struct mcomm_ytphy_ds_map *ds_map = NULL; 442*c574fa6dSskrll size_t ds_mapsize; 443*c574fa6dSskrll u_int millivolts = 1800; 444*c574fa6dSskrll switch (__SHIFTOUT(data, YT8521_CFG_LDO_MASK)) { 445*c574fa6dSskrll case YT8521_CFG_LDO_3V3: 446*c574fa6dSskrll ds_map = mcomm_3d3v_ds_map; 447*c574fa6dSskrll ds_mapsize = __arraycount(mcomm_3d3v_ds_map); 448*c574fa6dSskrll millivolts = 3300; 449*c574fa6dSskrll break; 450*c574fa6dSskrll case YT8521_CFG_LDO_2V5: 451*c574fa6dSskrll millivolts = 2500; 452*c574fa6dSskrll break; 453*c574fa6dSskrll default: 454*c574fa6dSskrll ds_map = mcomm_1d8v_ds_map; 455*c574fa6dSskrll ds_mapsize = __arraycount(mcomm_1d8v_ds_map); 456*c574fa6dSskrll break; 457*c574fa6dSskrll } 458*c574fa6dSskrll 459*c574fa6dSskrll if (ds_map) { 460*c574fa6dSskrll u_int rx_clk_ds = mcomm_yt8531_ds(msc, ds_map, ds_mapsize, 461*c574fa6dSskrll msc->sc_rx_clk_drv_microamp, millivolts); 462*c574fa6dSskrll u_int rx_data_ds = mcomm_yt8531_ds(msc, ds_map, ds_mapsize, 463*c574fa6dSskrll msc->sc_rx_data_drv_microamp, millivolts); 464*c574fa6dSskrll 465*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8521_EXT_PAD_DRIVE_STRENGTH); 466*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 467*c574fa6dSskrll 468*c574fa6dSskrll data &= ~(YT8531_RGMII_RXC_DS_MASK | YT8531_RGMII_RXD_DS_MASK); 469*c574fa6dSskrll 470*c574fa6dSskrll u_int rx_data_ds_hi = 471*c574fa6dSskrll __SHIFTOUT(rx_data_ds, YT8531_RGMII_RXD_DS_HIBITS); 472*c574fa6dSskrll u_int rx_data_ds_lo = 473*c574fa6dSskrll __SHIFTOUT(rx_data_ds, YT8531_RGMII_RXD_DS_LOBITS); 474*c574fa6dSskrll data |= 475*c574fa6dSskrll __SHIFTIN(rx_clk_ds, YT8531_RGMII_RXC_DS_MASK) | 476*c574fa6dSskrll __SHIFTIN(rx_data_ds_hi, YT8531_RGMII_RXD_DS_HIMASK) | 477*c574fa6dSskrll __SHIFTIN(rx_data_ds_lo, YT8531_RGMII_RXD_DS_LOMASK); 478*c574fa6dSskrll 479*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_DATA, data); 480*c574fa6dSskrll } 481*c574fa6dSskrll 482*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8521_EXT_SYNCE_CFG); 483*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 484*c574fa6dSskrll 485*c574fa6dSskrll data &= ~(YT8531_EN_SYNC_E | YT8531_CLK_SRC_SEL_MASK); 486*c574fa6dSskrll 487*c574fa6dSskrll switch (msc->sc_clk_out_frequency_hz) { 488*c574fa6dSskrll case 125000000: 489*c574fa6dSskrll data |= YT8531_EN_SYNC_E; 490*c574fa6dSskrll data |= YT8531_CLK_FRE_SEL_125M; 491*c574fa6dSskrll data |= __SHIFTIN(YT8531_CLK_SRC_SEL_PLL_125M, YT8531_CLK_SRC_SEL_MASK); 492*c574fa6dSskrll break; 493*c574fa6dSskrll case 25000000: 494*c574fa6dSskrll data |= YT8531_EN_SYNC_E; 495*c574fa6dSskrll data |= __SHIFTIN(YT8531_CLK_SRC_SEL_REF_25M, YT8531_CLK_SRC_SEL_MASK); 496*c574fa6dSskrll break; 497*c574fa6dSskrll default: 498*c574fa6dSskrll break; 499*c574fa6dSskrll } 500*c574fa6dSskrll 501*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_DATA, data); 502*c574fa6dSskrll 503*c574fa6dSskrll /* Restore address register. */ 504*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, oldaddr); 505*c574fa6dSskrll } 506*c574fa6dSskrll 507*c574fa6dSskrll 508*c574fa6dSskrll static void 509*c574fa6dSskrll mcommphy_reset(struct mii_softc *sc) 510*c574fa6dSskrll { 511*c574fa6dSskrll struct mcomm_softc * const msc = 512*c574fa6dSskrll container_of(sc, struct mcomm_softc, sc_miisc); 513*c574fa6dSskrll 514*c574fa6dSskrll KASSERT(mii_locked(sc->mii_pdata)); 515*c574fa6dSskrll 516*c574fa6dSskrll mii_phy_reset(sc); 517*c574fa6dSskrll 518*c574fa6dSskrll switch (msc->sc_type) { 519*c574fa6dSskrll case YT8511: 520*c574fa6dSskrll mcomm_yt8511_init(msc); 521*c574fa6dSskrll break; 522*c574fa6dSskrll case YT8531: 523*c574fa6dSskrll mcomm_yt8531_init(msc); 524*c574fa6dSskrll break; 525*c574fa6dSskrll default: 526*c574fa6dSskrll return; 527*c574fa6dSskrll } 528*c574fa6dSskrll } 529*c574fa6dSskrll 530*c574fa6dSskrll 531*c574fa6dSskrll static void 532*c574fa6dSskrll mcommphyattach(device_t parent, device_t self, void *aux) 533*c574fa6dSskrll { 534*c574fa6dSskrll struct mcomm_softc *msc = device_private(self); 535*c574fa6dSskrll struct mii_softc *sc = &msc->sc_miisc; 536*c574fa6dSskrll struct mii_attach_args *ma = aux; 537*c574fa6dSskrll struct mii_data *mii = ma->mii_data; 538*c574fa6dSskrll 539*c574fa6dSskrll msc->sc_type = YTUNK; 540*c574fa6dSskrll if (mcommphy_isyt8511(ma)) { 541*c574fa6dSskrll msc->sc_type = YT8511; 542*c574fa6dSskrll aprint_normal(": Motorcomm YT8511 GbE PHY\n"); 543*c574fa6dSskrll } else { 544*c574fa6dSskrll KASSERT(mii_phy_match(ma, mcommphys) != NULL); 545*c574fa6dSskrll msc->sc_type = YT8531; 546*c574fa6dSskrll aprint_normal(": Motorcomm YT8531 GbE PHY\n"); 547*c574fa6dSskrll } 548*c574fa6dSskrll aprint_naive(": Media interface\n"); 549*c574fa6dSskrll 550*c574fa6dSskrll sc->mii_dev = self; 551*c574fa6dSskrll sc->mii_inst = mii->mii_instance; 552*c574fa6dSskrll sc->mii_phy = ma->mii_phyno; 553*c574fa6dSskrll sc->mii_mpd_oui = MII_OUI(ma->mii_id1, ma->mii_id2); 554*c574fa6dSskrll sc->mii_mpd_model = MII_MODEL(ma->mii_id2); 555*c574fa6dSskrll sc->mii_mpd_rev = MII_REV(ma->mii_id2); 556*c574fa6dSskrll sc->mii_funcs = &mcommphy_funcs; 557*c574fa6dSskrll sc->mii_pdata = mii; 558*c574fa6dSskrll sc->mii_flags = ma->mii_flags; 559*c574fa6dSskrll 560*c574fa6dSskrll prop_dictionary_t dict = device_properties(parent); 561*c574fa6dSskrll switch (msc->sc_type) { 562*c574fa6dSskrll case YT8521: 563*c574fa6dSskrll case YT8531: 564*c574fa6dSskrll /* Default values */ 565*c574fa6dSskrll msc->sc_rx_internal_delay_ps = YT8521_DELAY_DEFAULT; 566*c574fa6dSskrll msc->sc_tx_internal_delay_ps = YT8521_DELAY_DEFAULT; 567*c574fa6dSskrll 568*c574fa6dSskrll mcomm_yt8531properties(msc, dict); 569*c574fa6dSskrll break; 570*c574fa6dSskrll default: 571*c574fa6dSskrll break; 572*c574fa6dSskrll } 573*c574fa6dSskrll 574*c574fa6dSskrll mii_lock(mii); 575*c574fa6dSskrll 576*c574fa6dSskrll PHY_RESET(sc); 57717d21f01Sjmcneill 57817d21f01Sjmcneill PHY_READ(sc, MII_BMSR, &sc->mii_capabilities); 57917d21f01Sjmcneill sc->mii_capabilities &= ma->mii_capmask; 58017d21f01Sjmcneill if (sc->mii_capabilities & BMSR_EXTSTAT) 58117d21f01Sjmcneill PHY_READ(sc, MII_EXTSR, &sc->mii_extcapabilities); 58217d21f01Sjmcneill 58317d21f01Sjmcneill mii_unlock(mii); 58417d21f01Sjmcneill 58517d21f01Sjmcneill mii_phy_add_media(sc); 58617d21f01Sjmcneill } 58717d21f01Sjmcneill 588*c574fa6dSskrll static void 589*c574fa6dSskrll ytphy_yt8521_update(struct mcomm_softc *msc) 590*c574fa6dSskrll { 591*c574fa6dSskrll struct mii_softc *sc = &msc->sc_miisc; 592*c574fa6dSskrll struct mii_data *mii = sc->mii_pdata; 593*c574fa6dSskrll bool tx_clk_inv = false; 594*c574fa6dSskrll uint16_t oldaddr, data; 595*c574fa6dSskrll 596*c574fa6dSskrll if (sc->mii_media_active == mii->mii_media_active) 597*c574fa6dSskrll return; 598*c574fa6dSskrll 599*c574fa6dSskrll if (!msc->sc_tx_clk_adj_enabled) 600*c574fa6dSskrll return; 601*c574fa6dSskrll 602*c574fa6dSskrll switch (IFM_SUBTYPE(mii->mii_media_active)) { 603*c574fa6dSskrll case IFM_1000_T: 604*c574fa6dSskrll tx_clk_inv = msc->sc_tx_clk_1000_inverted; 605*c574fa6dSskrll break; 606*c574fa6dSskrll case IFM_100_TX: 607*c574fa6dSskrll tx_clk_inv = msc->sc_tx_clk_100_inverted; 608*c574fa6dSskrll break; 609*c574fa6dSskrll case IFM_10_T: 610*c574fa6dSskrll tx_clk_inv = msc->sc_tx_clk_10_inverted; 611*c574fa6dSskrll break; 612*c574fa6dSskrll } 613*c574fa6dSskrll 614*c574fa6dSskrll /* Save address register. */ 615*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_ADDR, &oldaddr); 616*c574fa6dSskrll 617*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, YT8521_EXT_RGMII_CONFIG1); 618*c574fa6dSskrll PHY_READ(sc, YTPHY_EXT_REG_DATA, &data); 619*c574fa6dSskrll 620*c574fa6dSskrll if (tx_clk_inv) 621*c574fa6dSskrll data |= YT8521_TX_CLK_SEL_INV; 622*c574fa6dSskrll else 623*c574fa6dSskrll data &= ~YT8521_TX_CLK_SEL_INV; 624*c574fa6dSskrll 625*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_DATA, data); 626*c574fa6dSskrll 627*c574fa6dSskrll /* Restore address register. */ 628*c574fa6dSskrll PHY_WRITE(sc, YTPHY_EXT_REG_ADDR, oldaddr); 629*c574fa6dSskrll } 630*c574fa6dSskrll 63117d21f01Sjmcneill static int 63217d21f01Sjmcneill mcommphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 63317d21f01Sjmcneill { 63417d21f01Sjmcneill struct ifmedia_entry *ife = mii->mii_media.ifm_cur; 635*c574fa6dSskrll struct mcomm_softc * const msc = 636*c574fa6dSskrll container_of(sc, struct mcomm_softc, sc_miisc); 63717d21f01Sjmcneill 63817d21f01Sjmcneill KASSERT(mii_locked(mii)); 63917d21f01Sjmcneill 64017d21f01Sjmcneill switch (cmd) { 64117d21f01Sjmcneill case MII_POLLSTAT: 64217d21f01Sjmcneill /* If we're not polling our PHY instance, just return. */ 64317d21f01Sjmcneill if (IFM_INST(ife->ifm_media) != sc->mii_inst) 64417d21f01Sjmcneill return 0; 64517d21f01Sjmcneill break; 64617d21f01Sjmcneill 64717d21f01Sjmcneill case MII_MEDIACHG: 64817d21f01Sjmcneill /* If the interface is not up, don't do anything. */ 64917d21f01Sjmcneill if ((mii->mii_ifp->if_flags & IFF_UP) == 0) 65017d21f01Sjmcneill break; 65117d21f01Sjmcneill 65217d21f01Sjmcneill mii_phy_setmedia(sc); 65317d21f01Sjmcneill break; 65417d21f01Sjmcneill 65517d21f01Sjmcneill case MII_TICK: 65617d21f01Sjmcneill /* If we're not currently selected, just return. */ 65717d21f01Sjmcneill if (IFM_INST(ife->ifm_media) != sc->mii_inst) 65817d21f01Sjmcneill return 0; 65917d21f01Sjmcneill 66017d21f01Sjmcneill if (mii_phy_tick(sc) == EJUSTRETURN) 66117d21f01Sjmcneill return 0; 66217d21f01Sjmcneill break; 66317d21f01Sjmcneill 66417d21f01Sjmcneill case MII_DOWN: 66517d21f01Sjmcneill mii_phy_down(sc); 66617d21f01Sjmcneill return 0; 66717d21f01Sjmcneill } 66817d21f01Sjmcneill 66917d21f01Sjmcneill /* Update the media status. */ 67017d21f01Sjmcneill mii_phy_status(sc); 67117d21f01Sjmcneill 67217d21f01Sjmcneill /* Callback if something changed. */ 673*c574fa6dSskrll if (msc->sc_type != YT8511) 674*c574fa6dSskrll ytphy_yt8521_update(msc); 675*c574fa6dSskrll 67617d21f01Sjmcneill mii_phy_update(sc, cmd); 67717d21f01Sjmcneill return 0; 67817d21f01Sjmcneill } 679