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