1 /* 2 * Copyright (c) 2022 Jared McNeill <jmcneill@invisible.ca> 3 * Copyright (c) 2022 Soren Schmidt <sos@deepcore.dk> 4 * Copyright (c) 2024 Jari Sihvola <jsihv@gmx.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Motorcomm YT8511C/YT8511H/YT8531 31 * Integrated 10/100/1000 Gigabit Ethernet phy 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/socket.h> 38 #include <sys/errno.h> 39 #include <sys/module.h> 40 #include <sys/bus.h> 41 42 #include <net/if.h> 43 #include <net/if_media.h> 44 45 #include <dev/mii/mii.h> 46 #include <dev/mii/miivar.h> 47 #include <dev/mii/mii_fdt.h> 48 49 #include "miidevs.h" 50 #include "miibus_if.h" 51 52 #define MCOMMPHY_YT8511_OUI 0x000000 53 #define MCOMMPHY_YT8511_MODEL 0x10 54 #define MCOMMPHY_YT8511_REV 0x0a 55 56 #define MCOMMPHY_YT8531_MODEL 0x11 57 58 #define EXT_REG_ADDR 0x1e 59 #define EXT_REG_DATA 0x1f 60 61 /* Extended registers */ 62 #define PHY_CLOCK_GATING_REG 0x0c 63 #define RX_CLK_DELAY_EN 0x0001 64 #define CLK_25M_SEL 0x0006 65 #define CLK_25M_SEL_125M 3 66 #define TX_CLK_DELAY_SEL 0x00f0 67 #define PHY_SLEEP_CONTROL1_REG 0x27 68 #define PLLON_IN_SLP 0x4000 69 70 /* Registers and values for YT8531 */ 71 #define YT8531_CHIP_CONFIG 0xa001 72 #define RXC_DLY_EN (1 << 8) 73 74 #define YT8531_PAD_DRSTR_CFG 0xa010 75 #define PAD_RXC_MASK 0x7 76 #define PAD_RXC_SHIFT 13 77 #define JH7110_RGMII_RXC_STRENGTH 6 78 79 #define YT8531_RGMII_CONFIG1 0xa003 80 #define RX_DELAY_SEL_SHIFT 10 81 #define RX_DELAY_SEL_MASK 0xf 82 #define RXC_DLY_THRESH 2250 83 #define RXC_DLY_ADDON 1900 84 #define TX_DELAY_SEL_FE_MASK 0xf 85 #define TX_DELAY_SEL_FE_SHIFT 4 86 #define TX_DELAY_SEL_MASK 0xf 87 #define TX_DELAY_SEL_SHIFT 0 88 #define TX_CLK_SEL (1 << 14) 89 #define INTERNAL_DLY_DIV 150 90 91 #define YT8531_SYNCE_CFG 0xa012 92 #define EN_SYNC_E (1 << 6) 93 94 #define LOWEST_SET_BIT(mask) ((((mask) - 1) & (mask)) ^ (mask)) 95 #define SHIFTIN(x, mask) ((x) * LOWEST_SET_BIT(mask)) 96 97 static const struct mii_phydesc mcommphys[] = { 98 MII_PHY_DESC(MOTORCOMM, YT8511), 99 MII_PHY_DESC(MOTORCOMM2, YT8531), 100 MII_PHY_END 101 }; 102 103 struct mcommphy_softc { 104 mii_softc_t mii_sc; 105 device_t dev; 106 bool tx_10_inv; 107 bool tx_100_inv; 108 bool tx_1000_inv; 109 }; 110 111 static void mcommphy_yt8531_speed_adjustment(struct mii_softc *sc); 112 113 static int 114 mcommphy_service(struct mii_softc *sc, struct mii_data *mii, int cmd) 115 { 116 switch (cmd) { 117 case MII_POLLSTAT: 118 break; 119 120 case MII_MEDIACHG: 121 mii_phy_setmedia(sc); 122 break; 123 124 case MII_TICK: 125 if (mii_phy_tick(sc) == EJUSTRETURN) 126 return (0); 127 break; 128 } 129 130 /* Update the media status. */ 131 PHY_STATUS(sc); 132 133 /* 134 * For the needs of JH7110 which has two Ethernet devices with 135 * different TX inverted configuration depending on speed used 136 */ 137 if (sc->mii_mpd_model == MCOMMPHY_YT8531_MODEL && 138 (sc->mii_media_active != mii->mii_media_active || 139 sc->mii_media_status != mii->mii_media_status)) { 140 mcommphy_yt8531_speed_adjustment(sc); 141 } 142 143 /* Callback if something changed. */ 144 mii_phy_update(sc, cmd); 145 146 return (0); 147 } 148 149 static const struct mii_phy_funcs mcommphy_funcs = { 150 mcommphy_service, 151 ukphy_status, 152 mii_phy_reset 153 }; 154 155 static int 156 mcommphy_probe(device_t dev) 157 { 158 struct mii_attach_args *ma = device_get_ivars(dev); 159 160 /* 161 * The YT8511C reports an OUI of 0. Best we can do here is to match 162 * exactly the contents of the PHY identification registers. 163 */ 164 if (MII_OUI(ma->mii_id1, ma->mii_id2) == MCOMMPHY_YT8511_OUI && 165 MII_MODEL(ma->mii_id2) == MCOMMPHY_YT8511_MODEL && 166 MII_REV(ma->mii_id2) == MCOMMPHY_YT8511_REV) { 167 device_set_desc(dev, "Motorcomm YT8511 media interface"); 168 return (BUS_PROBE_DEFAULT); 169 } 170 171 /* YT8531 follows a conventional procedure */ 172 return (mii_phy_dev_probe(dev, mcommphys, BUS_PROBE_DEFAULT)); 173 } 174 175 static void 176 mcommphy_yt8511_setup(struct mii_softc *sc) 177 { 178 uint16_t oldaddr, data; 179 180 oldaddr = PHY_READ(sc, EXT_REG_ADDR); 181 182 PHY_WRITE(sc, EXT_REG_ADDR, PHY_CLOCK_GATING_REG); 183 data = PHY_READ(sc, EXT_REG_DATA); 184 data &= ~CLK_25M_SEL; 185 data |= SHIFTIN(CLK_25M_SEL_125M, CLK_25M_SEL); 186 if (sc->mii_flags & MIIF_RX_DELAY) { 187 data |= RX_CLK_DELAY_EN; 188 } else { 189 data &= ~RX_CLK_DELAY_EN; 190 } 191 data &= ~TX_CLK_DELAY_SEL; 192 if (sc->mii_flags & MIIF_TX_DELAY) { 193 data |= SHIFTIN(0xf, TX_CLK_DELAY_SEL); 194 } else { 195 data |= SHIFTIN(0x2, TX_CLK_DELAY_SEL); 196 } 197 PHY_WRITE(sc, EXT_REG_DATA, data); 198 199 PHY_WRITE(sc, EXT_REG_ADDR, PHY_SLEEP_CONTROL1_REG); 200 data = PHY_READ(sc, EXT_REG_DATA); 201 data |= PLLON_IN_SLP; 202 PHY_WRITE(sc, EXT_REG_DATA, data); 203 204 PHY_WRITE(sc, EXT_REG_ADDR, oldaddr); 205 } 206 207 static void 208 mcommphy_yt8531_speed_adjustment(struct mii_softc *sc) 209 { 210 struct mcommphy_softc *mcomm_sc = (struct mcommphy_softc *)sc; 211 struct mii_data *mii = sc->mii_pdata; 212 bool tx_clk_inv = false; 213 uint16_t reg, oldaddr; 214 215 switch (IFM_SUBTYPE(mii->mii_media_active)) { 216 case IFM_1000_T: 217 tx_clk_inv = mcomm_sc->tx_1000_inv; 218 break; 219 case IFM_100_T: 220 tx_clk_inv = mcomm_sc->tx_100_inv; 221 break; 222 case IFM_10_T: 223 tx_clk_inv = mcomm_sc->tx_10_inv; 224 break; 225 } 226 227 oldaddr = PHY_READ(sc, EXT_REG_ADDR); 228 229 PHY_WRITE(sc, EXT_REG_ADDR, YT8531_RGMII_CONFIG1); 230 reg = PHY_READ(sc, EXT_REG_DATA); 231 if (tx_clk_inv) 232 reg |= TX_CLK_SEL; 233 else 234 reg &= ~TX_CLK_SEL; 235 PHY_WRITE(sc, EXT_REG_DATA, reg); 236 237 PHY_WRITE(sc, EXT_REG_ADDR, oldaddr); 238 239 return; 240 } 241 242 static int 243 mcommphy_yt8531_setup_delay(struct mii_softc *sc) 244 { 245 struct mcommphy_softc *mcomm_sc = (struct mcommphy_softc *)sc; 246 mii_fdt_phy_config_t *cfg = mii_fdt_get_config(mcomm_sc->dev); 247 pcell_t val; 248 uint16_t reg, oldaddr; 249 int rx_delay, tx_delay = 0; 250 bool rxc_dly_en_off = false; 251 252 if (OF_getencprop(cfg->phynode, "rx-internal-delay-ps", &val, 253 sizeof(val)) > 0) { 254 if (val <= RXC_DLY_THRESH && val % INTERNAL_DLY_DIV == 0) { 255 rx_delay = val / INTERNAL_DLY_DIV; 256 rxc_dly_en_off = true; 257 } else { 258 rx_delay = (val - RXC_DLY_ADDON) / INTERNAL_DLY_DIV; 259 if ((val - RXC_DLY_ADDON) % INTERNAL_DLY_DIV != 0) 260 return (ENXIO); 261 } 262 } 263 264 if (OF_getencprop(cfg->phynode, "tx-internal-delay-ps", &val, 265 sizeof(val)) > 0) { 266 tx_delay = val / INTERNAL_DLY_DIV; 267 if (val % INTERNAL_DLY_DIV != 0) 268 return (ENXIO); 269 } 270 271 mii_fdt_free_config(cfg); 272 273 oldaddr = PHY_READ(sc, EXT_REG_ADDR); 274 275 /* Modifying Chip Config register */ 276 PHY_WRITE(sc, EXT_REG_ADDR, YT8531_CHIP_CONFIG); 277 reg = PHY_READ(sc, EXT_REG_DATA); 278 if (rxc_dly_en_off) 279 reg &= ~(RXC_DLY_EN); 280 PHY_WRITE(sc, EXT_REG_DATA, reg); 281 282 /* Modifying RGMII Config1 register */ 283 PHY_WRITE(sc, EXT_REG_ADDR, YT8531_RGMII_CONFIG1); 284 reg = PHY_READ(sc, EXT_REG_DATA); 285 reg &= ~(RX_DELAY_SEL_MASK << RX_DELAY_SEL_SHIFT); 286 reg |= rx_delay << RX_DELAY_SEL_SHIFT; 287 reg &= ~(TX_DELAY_SEL_MASK << TX_DELAY_SEL_SHIFT); 288 reg |= tx_delay << TX_DELAY_SEL_SHIFT; 289 PHY_WRITE(sc, EXT_REG_DATA, reg); 290 291 PHY_WRITE(sc, EXT_REG_ADDR, oldaddr); 292 293 return (0); 294 } 295 296 static int 297 mcommphy_yt8531_setup(struct mii_softc *sc) 298 { 299 struct mcommphy_softc *mcomm_sc = (struct mcommphy_softc *)sc; 300 mii_fdt_phy_config_t *cfg = mii_fdt_get_config(mcomm_sc->dev); 301 uint16_t reg, oldaddr; 302 303 if (OF_hasprop(cfg->phynode, "motorcomm,tx-clk-10-inverted")) 304 mcomm_sc->tx_10_inv = true; 305 306 if (OF_hasprop(cfg->phynode, "motorcomm,tx-clk-100-inverted")) 307 mcomm_sc->tx_100_inv = true; 308 309 if (OF_hasprop(cfg->phynode, "motorcomm,tx-clk-1000-inverted")) 310 mcomm_sc->tx_1000_inv = true; 311 312 mii_fdt_free_config(cfg); 313 314 oldaddr = PHY_READ(sc, EXT_REG_ADDR); 315 316 /* Modifying Pad Drive Strength register */ 317 PHY_WRITE(sc, EXT_REG_ADDR, YT8531_PAD_DRSTR_CFG); 318 reg = PHY_READ(sc, EXT_REG_DATA); 319 reg &= ~(PAD_RXC_MASK << PAD_RXC_SHIFT); 320 reg |= (JH7110_RGMII_RXC_STRENGTH << PAD_RXC_SHIFT); 321 PHY_WRITE(sc, EXT_REG_DATA, reg); 322 323 /* Modifying SyncE Config register */ 324 PHY_WRITE(sc, EXT_REG_ADDR, YT8531_SYNCE_CFG); 325 reg = PHY_READ(sc, EXT_REG_DATA); 326 reg &= ~(EN_SYNC_E); 327 PHY_WRITE(sc, EXT_REG_DATA, reg); 328 329 PHY_WRITE(sc, EXT_REG_ADDR, oldaddr); 330 331 if (mcommphy_yt8531_setup_delay(sc) != 0) 332 return (ENXIO); 333 334 return (0); 335 } 336 337 static int 338 mcommphy_attach(device_t dev) 339 { 340 struct mcommphy_softc *mcomm_sc = device_get_softc(dev); 341 mii_softc_t *mii_sc = &mcomm_sc->mii_sc; 342 int ret = 0; 343 344 mcomm_sc->dev = dev; 345 346 mii_phy_dev_attach(dev, MIIF_NOMANPAUSE, &mcommphy_funcs, 0); 347 348 PHY_RESET(mii_sc); 349 350 if (mii_sc->mii_mpd_model == MCOMMPHY_YT8511_MODEL) 351 mcommphy_yt8511_setup(mii_sc); 352 else if (mii_sc->mii_mpd_model == MCOMMPHY_YT8531_MODEL) 353 ret = mcommphy_yt8531_setup(mii_sc); 354 else { 355 device_printf(dev, "no PHY model detected\n"); 356 return (ENXIO); 357 } 358 if (ret) { 359 device_printf(dev, "PHY setup failed, error: %d\n", ret); 360 return (ret); 361 } 362 363 mii_sc->mii_capabilities = PHY_READ(mii_sc, MII_BMSR) & 364 mii_sc->mii_capmask; 365 if (mii_sc->mii_capabilities & BMSR_EXTSTAT) 366 mii_sc->mii_extcapabilities = PHY_READ(mii_sc, MII_EXTSR); 367 device_printf(dev, " "); 368 mii_phy_add_media(mii_sc); 369 printf("\n"); 370 371 MIIBUS_MEDIAINIT(mii_sc->mii_dev); 372 373 return (0); 374 } 375 376 static device_method_t mcommphy_methods[] = { 377 /* device interface */ 378 DEVMETHOD(device_probe, mcommphy_probe), 379 DEVMETHOD(device_attach, mcommphy_attach), 380 DEVMETHOD(device_detach, mii_phy_detach), 381 DEVMETHOD(device_shutdown, bus_generic_shutdown), 382 DEVMETHOD_END 383 }; 384 385 static driver_t mcommphy_driver = { 386 "mcommphy", 387 mcommphy_methods, 388 sizeof(struct mcommphy_softc) 389 }; 390 391 DRIVER_MODULE(mcommphy, miibus, mcommphy_driver, 0, 0); 392