1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright © 2021-2022 Bjoern A. Zeeb 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/bus.h> 34 #include <sys/rman.h> 35 #include <sys/endian.h> 36 #include <sys/socket.h> 37 38 #include <machine/bus.h> 39 #include <machine/resource.h> 40 41 #include <net/if.h> 42 #include <net/if_var.h> 43 #include <net/if_media.h> 44 45 #include <dev/mii/mii.h> 46 #include <dev/mii/miivar.h> 47 48 #include "memac_mdio.h" 49 #include "miibus_if.h" 50 51 /* #define MEMAC_MDIO_DEBUG */ 52 53 /* -------------------------------------------------------------------------- */ 54 55 int 56 memacphy_miibus_readreg(device_t dev, int phy, int reg) 57 { 58 59 return (MIIBUS_READREG(device_get_parent(dev), phy, reg)); 60 } 61 62 int 63 memacphy_miibus_writereg(device_t dev, int phy, int reg, int data) 64 { 65 66 return (MIIBUS_WRITEREG(device_get_parent(dev), phy, reg, data)); 67 } 68 69 void 70 memacphy_miibus_statchg(struct memacphy_softc_common *sc) 71 { 72 73 if (sc->dpnidev != NULL) 74 MIIBUS_STATCHG(sc->dpnidev); 75 } 76 77 int 78 memacphy_set_ni_dev(struct memacphy_softc_common *sc, device_t nidev) 79 { 80 81 if (nidev == NULL) 82 return (EINVAL); 83 84 #if defined(MEMAC_MDIO_DEBUG) 85 if (bootverbose) 86 device_printf(sc->dev, "setting nidev %p (%s)\n", 87 nidev, device_get_nameunit(nidev)); 88 #endif 89 90 if (sc->dpnidev != NULL) 91 return (EBUSY); 92 93 sc->dpnidev = nidev; 94 return (0); 95 } 96 97 int 98 memacphy_get_phy_loc(struct memacphy_softc_common *sc, int *phy_loc) 99 { 100 int error; 101 102 if (phy_loc == NULL) 103 return (EINVAL); 104 105 if (sc->phy == -1) { 106 *phy_loc = MII_PHY_ANY; 107 error = ENODEV; 108 } else { 109 *phy_loc = sc->phy; 110 error = 0; 111 } 112 113 #if defined(MEMAC_MDIO_DEBUG) 114 if (bootverbose) 115 device_printf(sc->dev, "returning phy_loc %d, error %d\n", 116 *phy_loc, error); 117 #endif 118 119 return (error); 120 } 121 122 /* -------------------------------------------------------------------------- */ 123 124 /* 125 * MDIO Ethernet Management Interface Registers (internal PCS MDIO PHY) 126 * 0x0030 MDIO Configuration Register (MDIO_CFG) 127 * 0x0034 MDIO Control Register (MDIO_CTL) 128 * 0x0038 MDIO Data Register (MDIO_DATA) 129 * 0x003c MDIO Register Address Register (MDIO_ADDR) 130 * 131 * External MDIO interfaces 132 * 0x0030 External MDIO Configuration Register (EMDIO_CFG) 133 * 0x0034 External MDIO Control Register (EMDIO_CTL) 134 * 0x0038 External MDIO Data Register (EMDIO_DATA) 135 * 0x003c External MDIO Register Address Register (EMDIO_ADDR) 136 */ 137 #define MDIO_CFG 0x00030 138 #define MDIO_CFG_MDIO_RD_ER (1 << 1) 139 #define MDIO_CFG_ENC45 (1 << 6) 140 #define MDIO_CFG_BUSY (1 << 31) 141 #define MDIO_CTL 0x00034 142 #define MDIO_CTL_READ (1 << 15) 143 #define MDIO_CTL_PORT_ADDR(_x) (((_x) & 0x1f) << 5) 144 #define MDIO_CTL_DEV_ADDR(_x) ((_x) & 0x1f) 145 #define MDIO_DATA 0x00038 146 #define MDIO_ADDR 0x0003c 147 148 static uint32_t 149 memac_read_4(struct memac_mdio_softc_common *sc, uint32_t reg) 150 { 151 uint32_t v, r; 152 153 v = bus_read_4(sc->mem_res, reg); 154 if (sc->is_little_endian) 155 r = le32toh(v); 156 else 157 r = be32toh(v); 158 159 return (r); 160 } 161 162 static void 163 memac_write_4(struct memac_mdio_softc_common *sc, uint32_t reg, uint32_t val) 164 { 165 uint32_t v; 166 167 if (sc->is_little_endian) 168 v = htole32(val); 169 else 170 v = htobe32(val); 171 bus_write_4(sc->mem_res, reg, v); 172 } 173 174 static uint32_t 175 memac_miibus_wait_no_busy(struct memac_mdio_softc_common *sc) 176 { 177 uint32_t count, val; 178 179 for (count = 1000; count > 0; count--) { 180 val = memac_read_4(sc, MDIO_CFG); 181 if ((val & MDIO_CFG_BUSY) == 0) 182 break; 183 DELAY(1); 184 } 185 186 if (count == 0) 187 return (0xffff); 188 189 return (0); 190 } 191 192 int 193 memac_miibus_readreg(struct memac_mdio_softc_common *sc, int phy, int reg) 194 { 195 uint32_t cfg, ctl, val; 196 197 /* Set proper Clause 45 mode. */ 198 cfg = memac_read_4(sc, MDIO_CFG); 199 /* XXX 45 support? */ 200 cfg &= ~MDIO_CFG_ENC45; /* Use Clause 22 */ 201 memac_write_4(sc, MDIO_CFG, cfg); 202 203 val = memac_miibus_wait_no_busy(sc); 204 if (val != 0) 205 return (0xffff); 206 207 /* To whom do we want to talk to.. */ 208 ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(reg); 209 /* XXX do we need two writes for this to work reliably? */ 210 memac_write_4(sc, MDIO_CTL, ctl | MDIO_CTL_READ); 211 212 val = memac_miibus_wait_no_busy(sc); 213 if (val != 0) 214 return (0xffff); 215 216 cfg = memac_read_4(sc, MDIO_CFG); 217 if (cfg & MDIO_CFG_MDIO_RD_ER) 218 return (0xffff); 219 220 val = memac_read_4(sc, MDIO_DATA); 221 val &= 0xffff; 222 223 #if defined(MEMAC_MDIO_DEBUG) 224 device_printf(sc->dev, "phy read %d:%d = %#06x\n", phy, reg, val); 225 #endif 226 227 return (val); 228 } 229 230 int 231 memac_miibus_writereg(struct memac_mdio_softc_common *sc, int phy, int reg, int data) 232 { 233 uint32_t cfg, ctl, val; 234 235 #if defined(MEMAC_MDIO_DEBUG) 236 device_printf(sc->dev, "phy write %d:%d\n", phy, reg); 237 #endif 238 239 /* Set proper Clause 45 mode. */ 240 cfg = memac_read_4(sc, MDIO_CFG); 241 /* XXX 45 support? */ 242 cfg &= ~MDIO_CFG_ENC45; /* Use Clause 22 */ 243 memac_write_4(sc, MDIO_CFG, cfg); 244 245 val = memac_miibus_wait_no_busy(sc); 246 if (val != 0) 247 return (0xffff); 248 249 /* To whom do we want to talk to.. */ 250 ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(reg); 251 memac_write_4(sc, MDIO_CTL, ctl); 252 253 memac_write_4(sc, MDIO_DATA, data & 0xffff); 254 255 val = memac_miibus_wait_no_busy(sc); 256 if (val != 0) 257 return (0xffff); 258 259 return (0); 260 } 261 262 ssize_t 263 memac_mdio_get_property(device_t dev, device_t child, const char *propname, 264 void *propvalue, size_t size, device_property_type_t type) 265 { 266 267 return (bus_generic_get_property(dev, child, propname, propvalue, size, type)); 268 } 269 270 int 271 memac_mdio_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 272 { 273 274 return (BUS_READ_IVAR(device_get_parent(dev), dev, index, result)); 275 } 276 277 278 int 279 memac_mdio_generic_attach(struct memac_mdio_softc_common *sc) 280 { 281 int rid; 282 283 rid = 0; 284 sc->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, 285 &rid, RF_ACTIVE | RF_SHAREABLE); 286 if (sc->mem_res == NULL) { 287 device_printf(sc->dev, "%s: cannot allocate mem resource\n", 288 __func__); 289 return (ENXIO); 290 } 291 292 sc->is_little_endian = device_has_property(sc->dev, "little-endian"); 293 294 return (0); 295 } 296 297 int 298 memac_mdio_generic_detach(struct memac_mdio_softc_common *sc) 299 { 300 301 if (sc->mem_res != NULL) 302 bus_release_resource(sc->dev, SYS_RES_MEMORY, 303 rman_get_rid(sc->mem_res), sc->mem_res); 304 305 return (0); 306 } 307