1441d6780SZbigniew Bodek /*- 2441d6780SZbigniew Bodek * Copyright (c) 2015 The FreeBSD Foundation 3441d6780SZbigniew Bodek * 4441d6780SZbigniew Bodek * This software was developed by Semihalf under 5441d6780SZbigniew Bodek * the sponsorship of the FreeBSD Foundation. 6441d6780SZbigniew Bodek * 7441d6780SZbigniew Bodek * Redistribution and use in source and binary forms, with or without 8441d6780SZbigniew Bodek * modification, are permitted provided that the following conditions 9441d6780SZbigniew Bodek * are met: 10441d6780SZbigniew Bodek * 1. Redistributions of source code must retain the above copyright 11441d6780SZbigniew Bodek * notice, this list of conditions and the following disclaimer. 12441d6780SZbigniew Bodek * 2. Redistributions in binary form must reproduce the above copyright 13441d6780SZbigniew Bodek * notice, this list of conditions and the following disclaimer in the 14441d6780SZbigniew Bodek * documentation and/or other materials provided with the distribution. 15441d6780SZbigniew Bodek * 16441d6780SZbigniew Bodek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17441d6780SZbigniew Bodek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18441d6780SZbigniew Bodek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19441d6780SZbigniew Bodek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20441d6780SZbigniew Bodek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21441d6780SZbigniew Bodek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22441d6780SZbigniew Bodek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23441d6780SZbigniew Bodek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24441d6780SZbigniew Bodek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25441d6780SZbigniew Bodek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26441d6780SZbigniew Bodek * SUCH DAMAGE. 27441d6780SZbigniew Bodek */ 28441d6780SZbigniew Bodek 29441d6780SZbigniew Bodek #include <sys/cdefs.h> 30441d6780SZbigniew Bodek __FBSDID("$FreeBSD$"); 31441d6780SZbigniew Bodek 32441d6780SZbigniew Bodek #include <sys/param.h> 33441d6780SZbigniew Bodek #include <sys/systm.h> 34441d6780SZbigniew Bodek #include <sys/bus.h> 35441d6780SZbigniew Bodek #include <sys/kernel.h> 36441d6780SZbigniew Bodek #include <sys/module.h> 37441d6780SZbigniew Bodek #include <sys/resource.h> 38441d6780SZbigniew Bodek #include <sys/rman.h> 39441d6780SZbigniew Bodek #include <sys/socket.h> 40441d6780SZbigniew Bodek #include <sys/queue.h> 41441d6780SZbigniew Bodek 42441d6780SZbigniew Bodek #include <machine/bus.h> 43441d6780SZbigniew Bodek #include <machine/resource.h> 44441d6780SZbigniew Bodek 45441d6780SZbigniew Bodek #include <net/if.h> 46441d6780SZbigniew Bodek #include <net/if_media.h> 47441d6780SZbigniew Bodek #include <net/if_types.h> 48441d6780SZbigniew Bodek #include <net/if_var.h> 49441d6780SZbigniew Bodek 50441d6780SZbigniew Bodek #include <dev/mii/mii.h> 51441d6780SZbigniew Bodek #include <dev/mii/miivar.h> 52441d6780SZbigniew Bodek 53441d6780SZbigniew Bodek #include "thunder_mdio_var.h" 54441d6780SZbigniew Bodek 55441d6780SZbigniew Bodek #include "lmac_if.h" 56441d6780SZbigniew Bodek #include "miibus_if.h" 57441d6780SZbigniew Bodek 58441d6780SZbigniew Bodek #define REG_BASE_RID 0 59441d6780SZbigniew Bodek 60441d6780SZbigniew Bodek #define SMI_CMD 0x00 61441d6780SZbigniew Bodek #define SMI_CMD_PHY_REG_ADR_SHIFT (0) 62441d6780SZbigniew Bodek #define SMI_CMD_PHY_REG_ADR_MASK (0x1FUL << SMI_CMD_PHY_REG_ADR_SHIFT) 63441d6780SZbigniew Bodek #define SMI_CMD_PHY_ADR_SHIFT (8) 64441d6780SZbigniew Bodek #define SMI_CMD_PHY_ADR_MASK (0x1FUL << SMI_CMD_PHY_ADR_SHIFT) 65441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_MASK (0x3UL << 16) 66441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C22_READ (0x1UL << 16) 67441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C22_WRITE (0x0UL << 16) 68441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C45_READ (0x3UL << 16) 69441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C45_WRITE (0x1UL << 16) 70441d6780SZbigniew Bodek #define SMI_CMD_PHY_OP_C45_ADDR (0x0UL << 16) 71441d6780SZbigniew Bodek 72441d6780SZbigniew Bodek #define SMI_WR_DAT 0x08 73441d6780SZbigniew Bodek #define SMI_WR_DAT_PENDING (1UL << 17) 74441d6780SZbigniew Bodek #define SMI_WR_DAT_VAL (1UL << 16) 75441d6780SZbigniew Bodek #define SMI_WR_DAT_DAT_MASK (0xFFFFUL << 0) 76441d6780SZbigniew Bodek 77441d6780SZbigniew Bodek #define SMI_RD_DAT 0x10 78441d6780SZbigniew Bodek #define SMI_RD_DAT_PENDING (1UL << 17) 79441d6780SZbigniew Bodek #define SMI_RD_DAT_VAL (1UL << 16) 80441d6780SZbigniew Bodek #define SMI_RD_DAT_DAT_MASK (0xFFFFUL << 0) 81441d6780SZbigniew Bodek 82441d6780SZbigniew Bodek #define SMI_CLK 0x18 83441d6780SZbigniew Bodek #define SMI_CLK_PREAMBLE (1UL << 12) 84441d6780SZbigniew Bodek #define SMI_CLK_MODE (1UL << 24) 85441d6780SZbigniew Bodek 86441d6780SZbigniew Bodek #define SMI_EN 0x20 87*b73763fcSGordon Bergling #define SMI_EN_EN (1UL << 0) /* Enable interface */ 88441d6780SZbigniew Bodek 89441d6780SZbigniew Bodek #define SMI_DRV_CTL 0x28 90441d6780SZbigniew Bodek 91441d6780SZbigniew Bodek static int thunder_mdio_detach(device_t); 92441d6780SZbigniew Bodek 93441d6780SZbigniew Bodek static int thunder_mdio_read(device_t, int, int); 94441d6780SZbigniew Bodek static int thunder_mdio_write(device_t, int, int, int); 95441d6780SZbigniew Bodek 96b9545c57SJustin Hibbits static int thunder_ifmedia_change_stub(if_t); 97b9545c57SJustin Hibbits static void thunder_ifmedia_status_stub(if_t, struct ifmediareq *); 98441d6780SZbigniew Bodek 99441d6780SZbigniew Bodek static int thunder_mdio_media_status(device_t, int, int *, int *, int *); 100441d6780SZbigniew Bodek static int thunder_mdio_media_change(device_t, int, int, int, int); 101441d6780SZbigniew Bodek static int thunder_mdio_phy_connect(device_t, int, int); 102441d6780SZbigniew Bodek static int thunder_mdio_phy_disconnect(device_t, int, int); 103441d6780SZbigniew Bodek 104441d6780SZbigniew Bodek static device_method_t thunder_mdio_methods[] = { 105441d6780SZbigniew Bodek /* Device interface */ 106441d6780SZbigniew Bodek DEVMETHOD(device_detach, thunder_mdio_detach), 107441d6780SZbigniew Bodek /* LMAC interface */ 108441d6780SZbigniew Bodek DEVMETHOD(lmac_media_status, thunder_mdio_media_status), 109441d6780SZbigniew Bodek DEVMETHOD(lmac_media_change, thunder_mdio_media_change), 110441d6780SZbigniew Bodek DEVMETHOD(lmac_phy_connect, thunder_mdio_phy_connect), 111441d6780SZbigniew Bodek DEVMETHOD(lmac_phy_disconnect, thunder_mdio_phy_disconnect), 112441d6780SZbigniew Bodek /* MII interface */ 113441d6780SZbigniew Bodek DEVMETHOD(miibus_readreg, thunder_mdio_read), 114441d6780SZbigniew Bodek DEVMETHOD(miibus_writereg, thunder_mdio_write), 115441d6780SZbigniew Bodek 116441d6780SZbigniew Bodek /* End */ 117441d6780SZbigniew Bodek DEVMETHOD_END 118441d6780SZbigniew Bodek }; 119441d6780SZbigniew Bodek 120441d6780SZbigniew Bodek DEFINE_CLASS_0(thunder_mdio, thunder_mdio_driver, thunder_mdio_methods, 121441d6780SZbigniew Bodek sizeof(struct thunder_mdio_softc)); 122441d6780SZbigniew Bodek 1233e38757dSJohn Baldwin DRIVER_MODULE(miibus, thunder_mdio, miibus_driver, 0, 0); 124f4aafb9eSWojciech Macek MODULE_VERSION(thunder_mdio, 1); 125441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, ether, 1, 1, 1); 126441d6780SZbigniew Bodek MODULE_DEPEND(thunder_mdio, miibus, 1, 1, 1); 127f4aafb9eSWojciech Macek MODULE_DEPEND(thunder_mdio, mrmlbus, 1, 1, 1); 128441d6780SZbigniew Bodek 129441d6780SZbigniew Bodek MALLOC_DEFINE(M_THUNDER_MDIO, "ThunderX MDIO", 130441d6780SZbigniew Bodek "Cavium ThunderX MDIO dynamic memory"); 131441d6780SZbigniew Bodek 132441d6780SZbigniew Bodek #define MDIO_LOCK_INIT(sc, name) \ 133441d6780SZbigniew Bodek mtx_init(&(sc)->mtx, name, NULL, MTX_DEF) 134441d6780SZbigniew Bodek 135441d6780SZbigniew Bodek #define MDIO_LOCK_DESTROY(sc) \ 136441d6780SZbigniew Bodek mtx_destroy(&(sc)->mtx) 137441d6780SZbigniew Bodek 138441d6780SZbigniew Bodek #define MDIO_LOCK(sc) mtx_lock(&(sc)->mtx) 139441d6780SZbigniew Bodek #define MDIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 140441d6780SZbigniew Bodek 141441d6780SZbigniew Bodek #define MDIO_LOCK_ASSERT(sc) \ 142441d6780SZbigniew Bodek mtx_assert(&(sc)->mtx, MA_OWNED) 143441d6780SZbigniew Bodek 144441d6780SZbigniew Bodek #define mdio_reg_read(sc, reg) \ 145441d6780SZbigniew Bodek bus_read_8((sc)->reg_base, (reg)) 146441d6780SZbigniew Bodek 147441d6780SZbigniew Bodek #define mdio_reg_write(sc, reg, val) \ 148441d6780SZbigniew Bodek bus_write_8((sc)->reg_base, (reg), (val)) 149441d6780SZbigniew Bodek 150441d6780SZbigniew Bodek int 151441d6780SZbigniew Bodek thunder_mdio_attach(device_t dev) 152441d6780SZbigniew Bodek { 153441d6780SZbigniew Bodek struct thunder_mdio_softc *sc; 154441d6780SZbigniew Bodek int rid; 155441d6780SZbigniew Bodek 156441d6780SZbigniew Bodek sc = device_get_softc(dev); 157441d6780SZbigniew Bodek sc->dev = dev; 158441d6780SZbigniew Bodek 159441d6780SZbigniew Bodek /* Allocate memory resources */ 160441d6780SZbigniew Bodek rid = REG_BASE_RID; 161441d6780SZbigniew Bodek sc->reg_base = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 162441d6780SZbigniew Bodek RF_ACTIVE); 163441d6780SZbigniew Bodek if (sc->reg_base == NULL) { 164441d6780SZbigniew Bodek device_printf(dev, "Could not allocate memory\n"); 165441d6780SZbigniew Bodek return (ENXIO); 166441d6780SZbigniew Bodek } 167441d6780SZbigniew Bodek 168441d6780SZbigniew Bodek TAILQ_INIT(&sc->phy_desc_head); 169441d6780SZbigniew Bodek MDIO_LOCK_INIT(sc, "ThunderX MDIO lock"); 170441d6780SZbigniew Bodek 171441d6780SZbigniew Bodek /* Enable SMI/MDIO interface */ 172441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_EN, SMI_EN_EN); 173441d6780SZbigniew Bodek 174441d6780SZbigniew Bodek return (0); 175441d6780SZbigniew Bodek } 176441d6780SZbigniew Bodek 177441d6780SZbigniew Bodek static int 178441d6780SZbigniew Bodek thunder_mdio_detach(device_t dev) 179441d6780SZbigniew Bodek { 180441d6780SZbigniew Bodek struct thunder_mdio_softc *sc; 181441d6780SZbigniew Bodek 182441d6780SZbigniew Bodek sc = device_get_softc(dev); 183441d6780SZbigniew Bodek 184441d6780SZbigniew Bodek if (sc->reg_base != NULL) { 185441d6780SZbigniew Bodek bus_release_resource(dev, SYS_RES_MEMORY, REG_BASE_RID, 186441d6780SZbigniew Bodek sc->reg_base); 187441d6780SZbigniew Bodek } 188441d6780SZbigniew Bodek 189441d6780SZbigniew Bodek return (0); 190441d6780SZbigniew Bodek } 191441d6780SZbigniew Bodek 192441d6780SZbigniew Bodek static __inline void 193441d6780SZbigniew Bodek thunder_mdio_set_mode(struct thunder_mdio_softc *sc, 194441d6780SZbigniew Bodek enum thunder_mdio_mode mode) 195441d6780SZbigniew Bodek { 196441d6780SZbigniew Bodek uint64_t smi_clk; 197441d6780SZbigniew Bodek 198441d6780SZbigniew Bodek if (sc->mode == mode) 199441d6780SZbigniew Bodek return; 200441d6780SZbigniew Bodek 201441d6780SZbigniew Bodek /* Set mode, IEEE CLAUSE 22 or IEEE CAUSE 45 */ 202441d6780SZbigniew Bodek smi_clk = mdio_reg_read(sc, SMI_CLK); 203441d6780SZbigniew Bodek if (mode == MODE_IEEE_C22) 204441d6780SZbigniew Bodek smi_clk &= ~SMI_CLK_MODE; 205441d6780SZbigniew Bodek else 206441d6780SZbigniew Bodek smi_clk |= SMI_CLK_MODE; 207441d6780SZbigniew Bodek /* Enable sending 32 bit preable on SMI transactions */ 208441d6780SZbigniew Bodek smi_clk |= SMI_CLK_PREAMBLE; 209fafb1c57SGordon Bergling /* Saved settings */ 210441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CLK, smi_clk); 211441d6780SZbigniew Bodek sc->mode = mode; 212441d6780SZbigniew Bodek } 213441d6780SZbigniew Bodek 214441d6780SZbigniew Bodek static int 215441d6780SZbigniew Bodek thunder_mdio_c45_addr(struct thunder_mdio_softc *sc, int phy, int reg) 216441d6780SZbigniew Bodek { 217441d6780SZbigniew Bodek uint64_t smi_cmd, smi_wr_dat; 218441d6780SZbigniew Bodek ssize_t timeout; 219441d6780SZbigniew Bodek 220441d6780SZbigniew Bodek thunder_mdio_set_mode(sc, MODE_IEEE_C45); 221441d6780SZbigniew Bodek 222441d6780SZbigniew Bodek /* Prepare data for transmission */ 223441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_WR_DAT, reg & SMI_WR_DAT_DAT_MASK); 224441d6780SZbigniew Bodek /* 225441d6780SZbigniew Bodek * Assemble command 226441d6780SZbigniew Bodek */ 227441d6780SZbigniew Bodek smi_cmd = 0; 228441d6780SZbigniew Bodek /* Set opcode */ 229441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE; 230441d6780SZbigniew Bodek 231441d6780SZbigniew Bodek /* Set PHY address */ 232441d6780SZbigniew Bodek smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); 233441d6780SZbigniew Bodek /* Set PHY register offset */ 234441d6780SZbigniew Bodek smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & 235441d6780SZbigniew Bodek SMI_CMD_PHY_REG_ADR_MASK); 236441d6780SZbigniew Bodek 237441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CMD, smi_cmd); 238441d6780SZbigniew Bodek for (timeout = 1000; timeout > 0; timeout--) { 239441d6780SZbigniew Bodek smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT); 240441d6780SZbigniew Bodek if (smi_wr_dat & SMI_WR_DAT_PENDING) 241441d6780SZbigniew Bodek DELAY(1000); 242441d6780SZbigniew Bodek else 243441d6780SZbigniew Bodek break; 244441d6780SZbigniew Bodek } 245441d6780SZbigniew Bodek 246441d6780SZbigniew Bodek if (timeout <= 0) 247441d6780SZbigniew Bodek return (EIO); 248441d6780SZbigniew Bodek else { 249441d6780SZbigniew Bodek /* Return 0 on success */ 250441d6780SZbigniew Bodek return (0); 251441d6780SZbigniew Bodek } 252441d6780SZbigniew Bodek } 253441d6780SZbigniew Bodek 254441d6780SZbigniew Bodek static int 255441d6780SZbigniew Bodek thunder_mdio_read(device_t dev, int phy, int reg) 256441d6780SZbigniew Bodek { 257441d6780SZbigniew Bodek struct thunder_mdio_softc *sc; 258441d6780SZbigniew Bodek uint64_t smi_cmd, smi_rd_dat; 259441d6780SZbigniew Bodek ssize_t timeout; 260441d6780SZbigniew Bodek int err; 261441d6780SZbigniew Bodek 262441d6780SZbigniew Bodek sc = device_get_softc(dev); 263441d6780SZbigniew Bodek 264441d6780SZbigniew Bodek /* XXX Always C22 - for <= 1Gbps only */ 265441d6780SZbigniew Bodek thunder_mdio_set_mode(sc, MODE_IEEE_C22); 266441d6780SZbigniew Bodek 267441d6780SZbigniew Bodek /* 268441d6780SZbigniew Bodek * Assemble command 269441d6780SZbigniew Bodek */ 270441d6780SZbigniew Bodek smi_cmd = 0; 271441d6780SZbigniew Bodek /* Set opcode */ 272441d6780SZbigniew Bodek if (sc->mode == MODE_IEEE_C22) 273441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C22_READ; 274441d6780SZbigniew Bodek else { 275441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C45_READ; 276441d6780SZbigniew Bodek err = thunder_mdio_c45_addr(sc, phy, reg); 277441d6780SZbigniew Bodek if (err != 0) 278441d6780SZbigniew Bodek return (err); 279441d6780SZbigniew Bodek 280441d6780SZbigniew Bodek reg = (reg >> 16) & 0x1F; 281441d6780SZbigniew Bodek } 282441d6780SZbigniew Bodek 283441d6780SZbigniew Bodek /* Set PHY address */ 284441d6780SZbigniew Bodek smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); 285441d6780SZbigniew Bodek /* Set PHY register offset */ 286441d6780SZbigniew Bodek smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & 287441d6780SZbigniew Bodek SMI_CMD_PHY_REG_ADR_MASK); 288441d6780SZbigniew Bodek 289441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CMD, smi_cmd); 290441d6780SZbigniew Bodek for (timeout = 1000; timeout > 0; timeout--) { 291441d6780SZbigniew Bodek smi_rd_dat = mdio_reg_read(sc, SMI_RD_DAT); 292441d6780SZbigniew Bodek if (smi_rd_dat & SMI_RD_DAT_PENDING) 293441d6780SZbigniew Bodek DELAY(1000); 294441d6780SZbigniew Bodek else 295441d6780SZbigniew Bodek break; 296441d6780SZbigniew Bodek } 297441d6780SZbigniew Bodek 298441d6780SZbigniew Bodek if (smi_rd_dat & SMI_RD_DAT_VAL) 299441d6780SZbigniew Bodek return (smi_rd_dat & SMI_RD_DAT_DAT_MASK); 300441d6780SZbigniew Bodek else { 301441d6780SZbigniew Bodek /* Return 0 on error */ 302441d6780SZbigniew Bodek return (0); 303441d6780SZbigniew Bodek } 304441d6780SZbigniew Bodek } 305441d6780SZbigniew Bodek 306441d6780SZbigniew Bodek static int 307441d6780SZbigniew Bodek thunder_mdio_write(device_t dev, int phy, int reg, int data) 308441d6780SZbigniew Bodek { 309441d6780SZbigniew Bodek struct thunder_mdio_softc *sc; 310441d6780SZbigniew Bodek uint64_t smi_cmd, smi_wr_dat; 311441d6780SZbigniew Bodek ssize_t timeout; 312441d6780SZbigniew Bodek 313441d6780SZbigniew Bodek sc = device_get_softc(dev); 314441d6780SZbigniew Bodek 315441d6780SZbigniew Bodek /* XXX Always C22 - for <= 1Gbps only */ 316441d6780SZbigniew Bodek thunder_mdio_set_mode(sc, MODE_IEEE_C22); 317441d6780SZbigniew Bodek 318441d6780SZbigniew Bodek /* Prepare data for transmission */ 319441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_WR_DAT, data & SMI_WR_DAT_DAT_MASK); 320441d6780SZbigniew Bodek /* 321441d6780SZbigniew Bodek * Assemble command 322441d6780SZbigniew Bodek */ 323441d6780SZbigniew Bodek smi_cmd = 0; 324441d6780SZbigniew Bodek /* Set opcode */ 325441d6780SZbigniew Bodek if (sc->mode == MODE_IEEE_C22) 326441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C22_WRITE; 327441d6780SZbigniew Bodek else 328441d6780SZbigniew Bodek smi_cmd |= SMI_CMD_PHY_OP_C45_WRITE; 329441d6780SZbigniew Bodek 330441d6780SZbigniew Bodek /* Set PHY address */ 331441d6780SZbigniew Bodek smi_cmd |= ((phy << SMI_CMD_PHY_ADR_SHIFT) & SMI_CMD_PHY_ADR_MASK); 332441d6780SZbigniew Bodek /* Set PHY register offset */ 333441d6780SZbigniew Bodek smi_cmd |= ((reg << SMI_CMD_PHY_REG_ADR_SHIFT) & 334441d6780SZbigniew Bodek SMI_CMD_PHY_REG_ADR_MASK); 335441d6780SZbigniew Bodek 336441d6780SZbigniew Bodek mdio_reg_write(sc, SMI_CMD, smi_cmd); 337441d6780SZbigniew Bodek for (timeout = 1000; timeout > 0; timeout--) { 338441d6780SZbigniew Bodek smi_wr_dat = mdio_reg_read(sc, SMI_WR_DAT); 339441d6780SZbigniew Bodek if (smi_wr_dat & SMI_WR_DAT_PENDING) 340441d6780SZbigniew Bodek DELAY(1000); 341441d6780SZbigniew Bodek else 342441d6780SZbigniew Bodek break; 343441d6780SZbigniew Bodek } 344441d6780SZbigniew Bodek 345441d6780SZbigniew Bodek if (timeout <= 0) 346441d6780SZbigniew Bodek return (EIO); 347441d6780SZbigniew Bodek else { 348441d6780SZbigniew Bodek /* Return 0 on success */ 349441d6780SZbigniew Bodek return (0); 350441d6780SZbigniew Bodek } 351441d6780SZbigniew Bodek } 352441d6780SZbigniew Bodek 353441d6780SZbigniew Bodek static int 354b9545c57SJustin Hibbits thunder_ifmedia_change_stub(if_t ifp __unused) 355441d6780SZbigniew Bodek { 356441d6780SZbigniew Bodek /* Will never be called by if_media */ 357441d6780SZbigniew Bodek return (0); 358441d6780SZbigniew Bodek } 359441d6780SZbigniew Bodek 360441d6780SZbigniew Bodek static void 361b9545c57SJustin Hibbits thunder_ifmedia_status_stub(if_t ifp __unused, struct ifmediareq 362441d6780SZbigniew Bodek *ifmr __unused) 363441d6780SZbigniew Bodek { 364441d6780SZbigniew Bodek /* Will never be called by if_media */ 365441d6780SZbigniew Bodek } 366441d6780SZbigniew Bodek 367441d6780SZbigniew Bodek static __inline struct phy_desc * 368441d6780SZbigniew Bodek get_phy_desc(struct thunder_mdio_softc *sc, int lmacid) 369441d6780SZbigniew Bodek { 370441d6780SZbigniew Bodek struct phy_desc *pd = NULL; 371441d6780SZbigniew Bodek 372441d6780SZbigniew Bodek MDIO_LOCK_ASSERT(sc); 373441d6780SZbigniew Bodek TAILQ_FOREACH(pd, &sc->phy_desc_head, phy_desc_list) { 374441d6780SZbigniew Bodek if (pd->lmacid == lmacid) 375441d6780SZbigniew Bodek break; 376441d6780SZbigniew Bodek } 377441d6780SZbigniew Bodek 378441d6780SZbigniew Bodek return (pd); 379441d6780SZbigniew Bodek } 380441d6780SZbigniew Bodek static int 381441d6780SZbigniew Bodek thunder_mdio_media_status(device_t dev, int lmacid, int *link, int *duplex, 382441d6780SZbigniew Bodek int *speed) 383441d6780SZbigniew Bodek { 384441d6780SZbigniew Bodek struct thunder_mdio_softc *sc; 385441d6780SZbigniew Bodek struct mii_data *mii_sc; 386441d6780SZbigniew Bodek struct phy_desc *pd; 387441d6780SZbigniew Bodek 388441d6780SZbigniew Bodek sc = device_get_softc(dev); 389441d6780SZbigniew Bodek 390441d6780SZbigniew Bodek MDIO_LOCK(sc); 391441d6780SZbigniew Bodek pd = get_phy_desc(sc, lmacid); 392441d6780SZbigniew Bodek if (pd == NULL) { 393441d6780SZbigniew Bodek /* Panic when invariants are enabled, fail otherwise. */ 394441d6780SZbigniew Bodek KASSERT(0, ("%s: no PHY descriptor for LMAC%d", 395441d6780SZbigniew Bodek __func__, lmacid)); 396441d6780SZbigniew Bodek MDIO_UNLOCK(sc); 397441d6780SZbigniew Bodek return (ENXIO); 398441d6780SZbigniew Bodek } 399441d6780SZbigniew Bodek mii_sc = device_get_softc(pd->miibus); 400441d6780SZbigniew Bodek 401441d6780SZbigniew Bodek mii_tick(mii_sc); 402441d6780SZbigniew Bodek if ((mii_sc->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 403441d6780SZbigniew Bodek (IFM_ACTIVE | IFM_AVALID)) { 404441d6780SZbigniew Bodek /* Link is up */ 405441d6780SZbigniew Bodek *link = 1; 406441d6780SZbigniew Bodek } else 407441d6780SZbigniew Bodek *link = 0; 408441d6780SZbigniew Bodek 409441d6780SZbigniew Bodek switch (IFM_SUBTYPE(mii_sc->mii_media_active)) { 410441d6780SZbigniew Bodek case IFM_10_T: 411441d6780SZbigniew Bodek *speed = 10; 412441d6780SZbigniew Bodek break; 413441d6780SZbigniew Bodek case IFM_100_TX: 414441d6780SZbigniew Bodek *speed = 100; 415441d6780SZbigniew Bodek break; 416441d6780SZbigniew Bodek case IFM_1000_T: 417441d6780SZbigniew Bodek *speed = 1000; 418441d6780SZbigniew Bodek break; 419441d6780SZbigniew Bodek default: 420441d6780SZbigniew Bodek /* IFM_NONE */ 421441d6780SZbigniew Bodek *speed = 0; 422441d6780SZbigniew Bodek } 423441d6780SZbigniew Bodek 424441d6780SZbigniew Bodek if ((IFM_OPTIONS(mii_sc->mii_media_active) & IFM_FDX) != 0) 425441d6780SZbigniew Bodek *duplex = 1; 426441d6780SZbigniew Bodek else 427441d6780SZbigniew Bodek *duplex = 0; 428441d6780SZbigniew Bodek 429441d6780SZbigniew Bodek MDIO_UNLOCK(sc); 430441d6780SZbigniew Bodek 431441d6780SZbigniew Bodek return (0); 432441d6780SZbigniew Bodek } 433441d6780SZbigniew Bodek 434441d6780SZbigniew Bodek static int 435441d6780SZbigniew Bodek thunder_mdio_media_change(device_t dev, int lmacid, int link, int duplex, 436441d6780SZbigniew Bodek int speed) 437441d6780SZbigniew Bodek { 438441d6780SZbigniew Bodek 439441d6780SZbigniew Bodek return (EIO); 440441d6780SZbigniew Bodek } 441441d6780SZbigniew Bodek 442441d6780SZbigniew Bodek static int 443441d6780SZbigniew Bodek thunder_mdio_phy_connect(device_t dev, int lmacid, int phy) 444441d6780SZbigniew Bodek { 445441d6780SZbigniew Bodek struct thunder_mdio_softc *sc; 446441d6780SZbigniew Bodek struct phy_desc *pd; 447441d6780SZbigniew Bodek int err; 448441d6780SZbigniew Bodek 449441d6780SZbigniew Bodek sc = device_get_softc(dev); 450441d6780SZbigniew Bodek 451441d6780SZbigniew Bodek MDIO_LOCK(sc); 452441d6780SZbigniew Bodek pd = get_phy_desc(sc, lmacid); 453441d6780SZbigniew Bodek MDIO_UNLOCK(sc); 454441d6780SZbigniew Bodek if (pd == NULL) { 455441d6780SZbigniew Bodek pd = malloc(sizeof(*pd), M_THUNDER_MDIO, (M_NOWAIT | M_ZERO)); 456441d6780SZbigniew Bodek if (pd == NULL) 457441d6780SZbigniew Bodek return (ENOMEM); 458441d6780SZbigniew Bodek pd->ifp = if_alloc(IFT_ETHER); 459441d6780SZbigniew Bodek if (pd->ifp == NULL) { 460441d6780SZbigniew Bodek free(pd, M_THUNDER_MDIO); 461441d6780SZbigniew Bodek return (ENOMEM); 462441d6780SZbigniew Bodek } 463441d6780SZbigniew Bodek pd->lmacid = lmacid; 464441d6780SZbigniew Bodek } 465441d6780SZbigniew Bodek 466441d6780SZbigniew Bodek err = mii_attach(dev, &pd->miibus, pd->ifp, 467441d6780SZbigniew Bodek thunder_ifmedia_change_stub, thunder_ifmedia_status_stub, 468441d6780SZbigniew Bodek BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 469441d6780SZbigniew Bodek 470441d6780SZbigniew Bodek if (err != 0) { 471441d6780SZbigniew Bodek device_printf(dev, "Could not attach PHY%d\n", phy); 472441d6780SZbigniew Bodek if_free(pd->ifp); 473441d6780SZbigniew Bodek free(pd, M_THUNDER_MDIO); 474441d6780SZbigniew Bodek return (ENXIO); 475441d6780SZbigniew Bodek } 476441d6780SZbigniew Bodek 477441d6780SZbigniew Bodek MDIO_LOCK(sc); 478441d6780SZbigniew Bodek TAILQ_INSERT_TAIL(&sc->phy_desc_head, pd, phy_desc_list); 479441d6780SZbigniew Bodek MDIO_UNLOCK(sc); 480441d6780SZbigniew Bodek 481441d6780SZbigniew Bodek return (0); 482441d6780SZbigniew Bodek } 483441d6780SZbigniew Bodek 484441d6780SZbigniew Bodek static int 485441d6780SZbigniew Bodek thunder_mdio_phy_disconnect(device_t dev, int lmacid, int phy) 486441d6780SZbigniew Bodek { 487441d6780SZbigniew Bodek struct thunder_mdio_softc *sc; 488441d6780SZbigniew Bodek struct phy_desc *pd; 489441d6780SZbigniew Bodek 490441d6780SZbigniew Bodek sc = device_get_softc(dev); 491441d6780SZbigniew Bodek MDIO_LOCK(sc); 492441d6780SZbigniew Bodek 493441d6780SZbigniew Bodek pd = get_phy_desc(sc, lmacid); 494441d6780SZbigniew Bodek if (pd == NULL) { 495441d6780SZbigniew Bodek MDIO_UNLOCK(sc); 496441d6780SZbigniew Bodek return (EINVAL); 497441d6780SZbigniew Bodek } 498441d6780SZbigniew Bodek 499441d6780SZbigniew Bodek /* Remove this PHY descriptor from the list */ 500441d6780SZbigniew Bodek TAILQ_REMOVE(&sc->phy_desc_head, pd, phy_desc_list); 501441d6780SZbigniew Bodek 502441d6780SZbigniew Bodek /* Detach miibus */ 503441d6780SZbigniew Bodek bus_generic_detach(dev); 504441d6780SZbigniew Bodek device_delete_child(dev, pd->miibus); 505441d6780SZbigniew Bodek /* Free fake ifnet */ 506441d6780SZbigniew Bodek if_free(pd->ifp); 507441d6780SZbigniew Bodek /* Free memory under phy descriptor */ 508441d6780SZbigniew Bodek free(pd, M_THUNDER_MDIO); 509441d6780SZbigniew Bodek MDIO_UNLOCK(sc); 510441d6780SZbigniew Bodek 511441d6780SZbigniew Bodek return (0); 512441d6780SZbigniew Bodek } 513