1 /*- 2 * Copyright (c) 2015 The FreeBSD Foundation 3 * 4 * This software was developed by Semihalf under 5 * the sponsorship of the FreeBSD Foundation. 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/bus.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/resource.h> 35 #include <sys/rman.h> 36 #include <sys/socket.h> 37 #include <sys/queue.h> 38 39 #include <machine/bus.h> 40 #include <machine/resource.h> 41 42 #include <net/if.h> 43 #include <net/if_media.h> 44 #include <net/if_types.h> 45 #include <net/if_var.h> 46 47 #include <dev/mii/mii.h> 48 #include <dev/mii/miivar.h> 49 50 #include "thunder_mdio_var.h" 51 52 #include "lmac_if.h" 53 #include "miibus_if.h" 54 55 #define REG_BASE_RID 0 56 57 #define SMI_CMD 0x00 58 #define SMI_CMD_PHY_REG_ADR_SHIFT (0) 59 #define SMI_CMD_PHY_REG_ADR_MASK (0x1FUL << SMI_CMD_PHY_REG_ADR_SHIFT) 60 #define SMI_CMD_PHY_ADR_SHIFT (8) 61 #define SMI_CMD_PHY_ADR_MASK (0x1FUL << SMI_CMD_PHY_ADR_SHIFT) 62 #define SMI_CMD_PHY_OP_MASK (0x3UL << 16) 63 #define SMI_CMD_PHY_OP_C22_READ (0x1UL << 16) 64 #define SMI_CMD_PHY_OP_C22_WRITE (0x0UL << 16) 65 #define SMI_CMD_PHY_OP_C45_READ (0x3UL << 16) 66 #define SMI_CMD_PHY_OP_C45_WRITE (0x1UL << 16) 67 #define SMI_CMD_PHY_OP_C45_ADDR (0x0UL << 16) 68 69 #define SMI_WR_DAT 0x08 70 #define SMI_WR_DAT_PENDING (1UL << 17) 71 #define SMI_WR_DAT_VAL (1UL << 16) 72 #define SMI_WR_DAT_DAT_MASK (0xFFFFUL << 0) 73 74 #define SMI_RD_DAT 0x10 75 #define SMI_RD_DAT_PENDING (1UL << 17) 76 #define SMI_RD_DAT_VAL (1UL << 16) 77 #define SMI_RD_DAT_DAT_MASK (0xFFFFUL << 0) 78 79 #define SMI_CLK 0x18 80 #define SMI_CLK_PREAMBLE (1UL << 12) 81 #define SMI_CLK_MODE (1UL << 24) 82 83 #define SMI_EN 0x20 84 #define SMI_EN_EN (1UL << 0) /* Enable interface */ 85 86 #define SMI_DRV_CTL 0x28 87 88 static int thunder_mdio_detach(device_t); 89 90 static int thunder_mdio_read(device_t, int, int); 91 static int thunder_mdio_write(device_t, int, int, int); 92 93 static int thunder_ifmedia_change_stub(if_t); 94 static void thunder_ifmedia_status_stub(if_t, struct ifmediareq *); 95 96 static int thunder_mdio_media_status(device_t, int, int *, int *, int *); 97 static int thunder_mdio_media_change(device_t, int, int, int, int); 98 static int thunder_mdio_phy_connect(device_t, int, int); 99 static int thunder_mdio_phy_disconnect(device_t, int, int); 100 101 static device_method_t thunder_mdio_methods[] = { 102 /* Device interface */ 103 DEVMETHOD(device_detach, thunder_mdio_detach), 104 /* LMAC interface */ 105 DEVMETHOD(lmac_media_status, thunder_mdio_media_status), 106 DEVMETHOD(lmac_media_change, thunder_mdio_media_change), 107 DEVMETHOD(lmac_phy_connect, thunder_mdio_phy_connect), 108 DEVMETHOD(lmac_phy_disconnect, thunder_mdio_phy_disconnect), 109 /* MII interface */ 110 DEVMETHOD(miibus_readreg, thunder_mdio_read), 111 DEVMETHOD(miibus_writereg, thunder_mdio_write), 112 113 /* End */ 114 DEVMETHOD_END 115 }; 116 117 DEFINE_CLASS_0(thunder_mdio, thunder_mdio_driver, thunder_mdio_methods, 118 sizeof(struct thunder_mdio_softc)); 119 120 DRIVER_MODULE(miibus, thunder_mdio, miibus_driver, 0, 0); 121 MODULE_VERSION(thunder_mdio, 1); 122 MODULE_DEPEND(thunder_mdio, ether, 1, 1, 1); 123 MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1); 124 MODULE_DEPEND(thunder_mdio, mrmlbus, 1, 1, 1); 125 126 MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO", 127 "Cavium ThunderX MDIO dynamic memory"); 128 129 #define MDIO_LOCK_INIT(sc, name) \ 130 mtx_init(&(sc)->mtx, name, NULL, MTX_DEF) 131 132 #define MDIO_LOCK_DESTROY(sc) \ 133 mtx_destroy(&(sc)->mtx) 134 135 #define MDIO_LOCK(sc) mtx_lock(&(sc)->mtx) 136 #define MDIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 137 138 #define MDIO_LOCK_ASSERT(sc) \ 139 mtx_assert(&(sc)->mtx, MA_OWNED) 140 141 #define mdio_reg_read(sc, reg) \ 142 bus_read_8((sc)->reg_base, (reg)) 143 144 #define mdio_reg_write(sc, reg, val) \ 145 bus_write_8((sc)->reg_base, (reg), (val)) 146 147 int 148 thunder_mdio_attach(device_t dev) 149 { 150 struct thunder_mdio_softc *sc; 151 int rid; 152 153 sc = device_get_softc(dev); 154 sc->dev = dev; 155 156 /* Allocate memory resources */ 157 rid = REG_BASE_RID; 158 sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 159 RF_ACTIVE); 160 if (sc->reg_base == NULL) { 161 device_printf(dev, "Could not allocate memory\n"); 162 return (ENXIO); 163 } 164 165 TAILQ_INIT(&sc->phy_desc_head); 166 MDIO_LOCK_INIT(sc, "ThunderX MDIO lock"); 167 168 /* Enable SMI/MDIO interface */ 169 mdio_reg_write(sc, SMI_EN, SMI_EN_EN); 170 171 return (0); 172 } 173 174 static int 175 thunder_mdio_detach(device_t dev) 176 { 177 struct thunder_mdio_softc *sc; 178 179 sc = device_get_softc(dev); 180 181 if (sc->reg_base != NULL) { 182 bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID, 183 sc->reg_base); 184 } 185 186 return (0); 187 } 188 189 static __inline void 190 thunder_mdio_set_mode(struct thunder_mdio_softc *sc, 191 enum thunder_mdio_mode mode) 192 { 193 uint64_t smi_clk; 194 195 if (sc->mode == mode) 196 return; 197 198 /* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */ 199 smi_clk = mdio_reg_read(sc, SMI_CLK); 200 if (mode == MODE_IEEE_C22) 201 smi_clk &= ~SMI_CLK_MODE; 202 else 203 smi_clk |= SMI_CLK_MODE; 204 /* Enable sending 32 bit preable on SMI transactions */ 205 smi_clk |= SMI_CLK_PREAMBLE; 206 /* Saved settings */ 207 mdio_reg_write(sc, SMI_CLK, smi_clk); 208 sc->mode = mode; 209 } 210 211 static int 212 thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg) 213 { 214 uint64_t smi_cmd, smi_wr_dat; 215 ssize_t timeout; 216 217 thunder_mdio_set_mode(sc, MODE_IEEE_C45); 218 219 /* Prepare data for transmission */ 220 mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK); 221 /* 222 * Assemble command 223 */ 224 smi_cmd = 0; 225 /* Set opcode */ 226 smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE; 227 228 /* Set PHY address */ 229 smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); 230 /* Set PHY register offset */ 231 smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & 232 SMI_CMD_PHY_REG_ADR_MASK); 233 234 mdio_reg_write(sc, SMI_CMD, smi_cmd); 235 for (timeout = 1000; timeout > 0; timeout--) { 236 smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT); 237 if (smi_wr_dat & SMI_WR_DAT_PENDING) 238 DELAY(1000); 239 else 240 break; 241 } 242 243 if (timeout <= 0) 244 return (EIO); 245 else { 246 /* Return 0 on success */ 247 return (0); 248 } 249 } 250 251 static int 252 thunder_mdio_read(device_t dev, int phy, int reg) 253 { 254 struct thunder_mdio_softc *sc; 255 uint64_t smi_cmd, smi_rd_dat; 256 ssize_t timeout; 257 int err; 258 259 sc = device_get_softc(dev); 260 261 /* XXX Always C22 - for <= 1Gbps only */ 262 thunder_mdio_set_mode(sc, MODE_IEEE_C22); 263 264 /* 265 * Assemble command 266 */ 267 smi_cmd = 0; 268 /* Set opcode */ 269 if (sc->mode == MODE_IEEE_C22) 270 smi_cmd |= SMI_CMD_PHY_OP_C22_READ; 271 else { 272 smi_cmd |= SMI_CMD_PHY_OP_C45_READ; 273 err = thunder_mdio_c45_addr(sc, phy, reg); 274 if (err != 0) 275 return (err); 276 277 reg = (reg >> 16) & 0x1F; 278 } 279 280 /* Set PHY address */ 281 smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); 282 /* Set PHY register offset */ 283 smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & 284 SMI_CMD_PHY_REG_ADR_MASK); 285 286 mdio_reg_write(sc, SMI_CMD, smi_cmd); 287 for (timeout = 1000; timeout > 0; timeout--) { 288 smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT); 289 if (smi_rd_dat & SMI_RD_DAT_PENDING) 290 DELAY(1000); 291 else 292 break; 293 } 294 295 if (smi_rd_dat & SMI_RD_DAT_VAL) 296 return (smi_rd_dat & SMI_RD_DAT_DAT_MASK); 297 else { 298 /* Return 0 on error */ 299 return (0); 300 } 301 } 302 303 static int 304 thunder_mdio_write(device_t dev, int phy, int reg, int data) 305 { 306 struct thunder_mdio_softc *sc; 307 uint64_t smi_cmd, smi_wr_dat; 308 ssize_t timeout; 309 310 sc = device_get_softc(dev); 311 312 /* XXX Always C22 - for <= 1Gbps only */ 313 thunder_mdio_set_mode(sc, MODE_IEEE_C22); 314 315 /* Prepare data for transmission */ 316 mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK); 317 /* 318 * Assemble command 319 */ 320 smi_cmd = 0; 321 /* Set opcode */ 322 if (sc->mode == MODE_IEEE_C22) 323 smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE; 324 else 325 smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE; 326 327 /* Set PHY address */ 328 smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); 329 /* Set PHY register offset */ 330 smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & 331 SMI_CMD_PHY_REG_ADR_MASK); 332 333 mdio_reg_write(sc, SMI_CMD, smi_cmd); 334 for (timeout = 1000; timeout > 0; timeout--) { 335 smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT); 336 if (smi_wr_dat & SMI_WR_DAT_PENDING) 337 DELAY(1000); 338 else 339 break; 340 } 341 342 if (timeout <= 0) 343 return (EIO); 344 else { 345 /* Return 0 on success */ 346 return (0); 347 } 348 } 349 350 static int 351 thunder_ifmedia_change_stub(if_t ifp __unused) 352 { 353 /* Will never be called by if_media */ 354 return (0); 355 } 356 357 static void 358 thunder_ifmedia_status_stub(if_t ifp __unused, struct ifmediareq 359 *ifmr __unused) 360 { 361 /* Will never be called by if_media */ 362 } 363 364 static __inline struct phy_desc * 365 get_phy_desc(struct thunder_mdio_softc *sc, int lmacid) 366 { 367 struct phy_desc *pd = NULL; 368 369 MDIO_LOCK_ASSERT(sc); 370 TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) { 371 if (pd->lmacid == lmacid) 372 break; 373 } 374 375 return (pd); 376 } 377 static int 378 thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex, 379 int *speed) 380 { 381 struct thunder_mdio_softc *sc; 382 struct mii_data *mii_sc; 383 struct phy_desc *pd; 384 385 sc = device_get_softc(dev); 386 387 MDIO_LOCK(sc); 388 pd = get_phy_desc(sc, lmacid); 389 if (pd == NULL) { 390 /* Panic when invariants are enabled, fail otherwise. */ 391 KASSERT(0, ("%s: no PHY descriptor for LMAC%d", 392 __func__, lmacid)); 393 MDIO_UNLOCK(sc); 394 return (ENXIO); 395 } 396 mii_sc = device_get_softc(pd->miibus); 397 398 mii_tick(mii_sc); 399 if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 400 (IFM_ACTIVE | IFM_AVALID)) { 401 /* Link is up */ 402 *link = 1; 403 } else 404 *link = 0; 405 406 switch (IFM_SUBTYPE(mii_sc->mii_media_active)) { 407 case IFM_10_T: 408 *speed = 10; 409 break; 410 case IFM_100_TX: 411 *speed = 100; 412 break; 413 case IFM_1000_T: 414 *speed = 1000; 415 break; 416 default: 417 /* IFM_NONE */ 418 *speed = 0; 419 } 420 421 if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0) 422 *duplex = 1; 423 else 424 *duplex = 0; 425 426 MDIO_UNLOCK(sc); 427 428 return (0); 429 } 430 431 static int 432 thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex, 433 int speed) 434 { 435 436 return (EIO); 437 } 438 439 static int 440 thunder_mdio_phy_connect(device_t dev, int lmacid, int phy) 441 { 442 struct thunder_mdio_softc *sc; 443 struct phy_desc *pd; 444 int err; 445 446 sc = device_get_softc(dev); 447 448 MDIO_LOCK(sc); 449 pd = get_phy_desc(sc, lmacid); 450 MDIO_UNLOCK(sc); 451 if (pd == NULL) { 452 pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO)); 453 if (pd == NULL) 454 return (ENOMEM); 455 pd->ifp = if_alloc(IFT_ETHER); 456 if (pd->ifp == NULL) { 457 free(pd, M_THUNDER_MDIO); 458 return (ENOMEM); 459 } 460 pd->lmacid = lmacid; 461 } 462 463 err = mii_attach(dev, &pd->miibus, pd->ifp, 464 thunder_ifmedia_change_stub, thunder_ifmedia_status_stub, 465 BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 466 467 if (err != 0) { 468 device_printf(dev, "Could not attach PHY%d\n", phy); 469 if_free(pd->ifp); 470 free(pd, M_THUNDER_MDIO); 471 return (ENXIO); 472 } 473 474 MDIO_LOCK(sc); 475 TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list); 476 MDIO_UNLOCK(sc); 477 478 return (0); 479 } 480 481 static int 482 thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy) 483 { 484 struct thunder_mdio_softc *sc; 485 struct phy_desc *pd; 486 487 sc = device_get_softc(dev); 488 MDIO_LOCK(sc); 489 490 pd = get_phy_desc(sc, lmacid); 491 if (pd == NULL) { 492 MDIO_UNLOCK(sc); 493 return (EINVAL); 494 } 495 496 /* Remove this PHY descriptor from the list */ 497 TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list); 498 499 /* Detach miibus */ 500 bus_generic_detach(dev); 501 device_delete_child(dev, pd->miibus); 502 /* Free fake ifnet */ 503 if_free(pd->ifp); 504 /* Free memory under phy descriptor */ 505 free(pd, M_THUNDER_MDIO); 506 MDIO_UNLOCK(sc); 507 508 return (0); 509 } 510