1 /*- 2 * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/bus.h> 29 #include <sys/kernel.h> 30 #include <sys/lock.h> 31 #include <sys/malloc.h> 32 #include <sys/mbuf.h> 33 #include <sys/module.h> 34 #include <sys/mutex.h> 35 #include <sys/rman.h> 36 #include <sys/socket.h> 37 38 #include <net/if.h> 39 #include <net/ethernet.h> 40 #include <net/if_dl.h> 41 #include <net/if_media.h> 42 43 #include <machine/bus.h> 44 45 #include <dev/clk/clk.h> 46 #include <dev/hwreset/hwreset.h> 47 48 #include <dev/mii/mii.h> 49 #include <dev/mii/miivar.h> 50 #include <dev/ofw/ofw_bus.h> 51 #include <dev/ofw/ofw_bus_subr.h> 52 #include <dev/mii/mii_fdt.h> 53 54 #include <dev/dwc/if_dwcvar.h> 55 #include <dev/dwc/dwc4_reg.h> 56 #include <dev/dwc/dwc4_dma.h> 57 58 #include "if_dwc_if.h" 59 60 struct dwc_hash_maddr_ctx { 61 struct dwc_softc *sc; 62 uint32_t hash[8]; 63 }; 64 65 #define STATS_HARVEST_INTERVAL 2 66 67 /* Pause time field in the transmitted control frame */ 68 static int dwc_pause_time = 0xffff; 69 TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time); 70 71 /* 72 * MIIBUS functions 73 */ 74 75 int 76 dwc4_miibus_read_reg(device_t dev, int phy, int reg) 77 { 78 struct dwc_softc *sc; 79 uint32_t mii; 80 size_t cnt; 81 int rv = 0; 82 83 sc = device_get_softc(dev); 84 85 mii = ((phy << MACMDIOAR_PA_S) & MACMDIOAR_PA_M) 86 | ((reg << MACMDIOAR_RDA_S) & MACMDIOAR_RDA_M) 87 | (sc->mii_clk << MACMDIOAR_CR_S) 88 | MACMDIOAR_READ 89 | MACMDIOAR_GB; /* Busy flag */ 90 91 WRITE4(sc, ETH_MACMDIOAR, mii); 92 93 for (cnt = 0; cnt < 1000; cnt++) { 94 if (!(READ4(sc, ETH_MACMDIOAR) & MACMDIOAR_GB)) { 95 rv = READ4(sc, ETH_MACMDIODR); 96 break; 97 } 98 DELAY(10); 99 } 100 101 return rv; 102 } 103 104 int 105 dwc4_miibus_write_reg(device_t dev, int phy, int reg, int val) 106 { 107 struct dwc_softc *sc; 108 uint32_t mii; 109 size_t cnt; 110 111 sc = device_get_softc(dev); 112 113 mii = ((phy << MACMDIOAR_PA_S) & MACMDIOAR_PA_M) 114 | ((reg << MACMDIOAR_RDA_S) & MACMDIOAR_RDA_M) 115 | (sc->mii_clk << MACMDIOAR_CR_S) 116 | MACMDIOAR_WRITE 117 | MACMDIOAR_GB; /* Busy flag */ 118 119 WRITE4(sc, ETH_MACMDIODR, val); 120 WRITE4(sc, ETH_MACMDIOAR, mii); 121 122 for (cnt = 0; cnt < 1000; cnt++) { 123 if (!(READ4(sc, ETH_MACMDIOAR) & MACMDIOAR_GB)) { 124 break; 125 } 126 DELAY(10); 127 } 128 129 return (0); 130 } 131 132 void 133 dwc4_miibus_statchg(device_t dev) 134 { 135 struct dwc_softc *sc; 136 struct mii_data *mii; 137 uint32_t reg; 138 139 /* 140 * Called by the MII bus driver when the PHY establishes 141 * link to set the MAC interface registers. 142 */ 143 144 sc = device_get_softc(dev); 145 146 DWC_ASSERT_LOCKED(sc); 147 148 mii = sc->mii_softc; 149 150 if (mii->mii_media_status & IFM_ACTIVE) 151 sc->link_is_up = true; 152 else 153 sc->link_is_up = false; 154 155 reg = READ4(sc, ETH_MACCR); 156 switch (IFM_SUBTYPE(mii->mii_media_active)) { 157 case IFM_1000_T: 158 case IFM_1000_SX: 159 reg &= ~(MACCR_FES | MACCR_PS); 160 break; 161 case IFM_100_TX: 162 reg |= (MACCR_FES | MACCR_PS); 163 break; 164 case IFM_10_T: 165 reg &= ~(MACCR_FES); 166 reg |= (MACCR_PS); 167 break; 168 case IFM_NONE: 169 sc->link_is_up = false; 170 return; 171 default: 172 sc->link_is_up = false; 173 device_printf(dev, "Unsupported media %u\n", 174 IFM_SUBTYPE(mii->mii_media_active)); 175 return; 176 } 177 if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) 178 reg |= (MACCR_DM); 179 else 180 reg &= ~(MACCR_DM); 181 WRITE4(sc, ETH_MACCR, reg); 182 183 IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active)); 184 185 } 186 187 void 188 dwc4_core_setup(struct dwc_softc *sc) 189 { 190 uint32_t reg; 191 192 DWC_ASSERT_LOCKED(sc); 193 194 reg = READ4(sc, ETH_MACVR); 195 device_printf(sc->dev, "MAC Version: %x\n", reg); 196 197 /* Enable core */ 198 reg = READ4(sc, ETH_MACCR); 199 reg |= (MACCR_JD | MACCR_ACS | MACCR_BE); 200 WRITE4(sc, ETH_MACCR, reg); 201 } 202 203 void 204 dwc4_enable_mac(struct dwc_softc *sc, bool enable) 205 { 206 uint32_t reg; 207 208 DWC_ASSERT_LOCKED(sc); 209 210 reg = READ4(sc, ETH_MACCR); 211 if (enable) 212 reg |= MACCR_TE | MACCR_RE; 213 else 214 reg &= ~(MACCR_TE | MACCR_RE); 215 WRITE4(sc, ETH_MACCR, reg); 216 } 217 218 void 219 dwc4_enable_csum_offload(struct dwc_softc *sc) 220 { 221 uint32_t reg; 222 223 DWC_ASSERT_LOCKED(sc); 224 reg = READ4(sc, ETH_MACCR); 225 if ((if_getcapenable(sc->ifp) & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6)) != 226 0) 227 reg |= MACCR_IPC; 228 else 229 reg &= ~MACCR_IPC; 230 WRITE4(sc, ETH_MACCR, reg); 231 } 232 233 static const uint8_t nibbletab[] = { 234 /* 0x0 0000 -> 0000 */ 0x0, 235 /* 0x1 0001 -> 1000 */ 0x8, 236 /* 0x2 0010 -> 0100 */ 0x4, 237 /* 0x3 0011 -> 1100 */ 0xc, 238 /* 0x4 0100 -> 0010 */ 0x2, 239 /* 0x5 0101 -> 1010 */ 0xa, 240 /* 0x6 0110 -> 0110 */ 0x6, 241 /* 0x7 0111 -> 1110 */ 0xe, 242 /* 0x8 1000 -> 0001 */ 0x1, 243 /* 0x9 1001 -> 1001 */ 0x9, 244 /* 0xa 1010 -> 0101 */ 0x5, 245 /* 0xb 1011 -> 1101 */ 0xd, 246 /* 0xc 1100 -> 0011 */ 0x3, 247 /* 0xd 1101 -> 1011 */ 0xb, 248 /* 0xe 1110 -> 0111 */ 0x7, 249 /* 0xf 1111 -> 1111 */ 0xf, }; 250 251 static uint8_t 252 bitreverse(uint8_t x) 253 { 254 255 return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4]; 256 } 257 258 static u_int 259 dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 260 { 261 struct dwc_hash_maddr_ctx *ctx = arg; 262 uint32_t crc, hashbit, hashreg; 263 uint8_t val; 264 265 crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN); 266 /* Take lower 8 bits and reverse it */ 267 val = bitreverse(~crc & 0xff); 268 /* 269 * TODO: There is probably a HW_FEATURES bit which isn't 270 * related to the extended descriptors that describe this 271 */ 272 if (!ctx->sc->dma_ext_desc) 273 val >>= 2; /* Only need lower 6 bits */ 274 hashreg = (val >> 5); 275 hashbit = (val & 31); 276 ctx->hash[hashreg] |= (1 << hashbit); 277 278 return (1); 279 } 280 281 void 282 dwc4_setup_rxfilter(struct dwc_softc *sc) 283 { 284 struct dwc_hash_maddr_ctx ctx; 285 if_t ifp; 286 uint8_t *eaddr; 287 uint32_t ffval, hi, lo; 288 int nhash, i; 289 290 DWC_ASSERT_LOCKED(sc); 291 292 ifp = sc->ifp; 293 /* 294 * TODO: There is probably a HW_FEATURES bit which isn't 295 * related to the extended descriptors that describe this 296 */ 297 nhash = sc->dma_ext_desc == false ? 2 : 8; 298 299 /* 300 * Set the multicast (group) filter hash. 301 */ 302 if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) { 303 ffval = (MACPFR_PM); 304 for (i = 0; i < nhash; i++) 305 ctx.hash[i] = ~0; 306 } else { 307 ffval = (MACPFR_HMC); 308 for (i = 0; i < nhash; i++) 309 ctx.hash[i] = 0; 310 ctx.sc = sc; 311 if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx); 312 } 313 314 /* 315 * Set the individual address filter hash. 316 */ 317 if ((if_getflags(ifp) & IFF_PROMISC) != 0) 318 ffval |= MACPFR_PR; 319 320 /* 321 * Set the primary address. 322 */ 323 eaddr = if_getlladdr(ifp); 324 lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) | 325 (eaddr[3] << 24); 326 hi = eaddr[4] | (eaddr[5] << 8); 327 WRITE4(sc, ETH_MACA0LR, lo); 328 WRITE4(sc, ETH_MACA0HR, hi); 329 330 WRITE4(sc, ETH_MACPFR, ffval); 331 if (!sc->dma_ext_desc) { 332 WRITE4(sc, ETH_MACHT0R, ctx.hash[0]); 333 WRITE4(sc, ETH_MACHT1R, ctx.hash[1]); 334 } else 335 panic("implement me: ext_desc"); 336 } 337 338 void 339 dwc4_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr) 340 { 341 uint32_t hi, lo, rnd; 342 343 /* 344 * Try to recover a MAC address from the running hardware. If there's 345 * something non-zero there, assume the bootloader did the right thing 346 * and just use it. 347 * 348 * Otherwise, set the address to a convenient locally assigned address, 349 * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally 350 * assigned bit set, and the broadcast/multicast bit clear. 351 */ 352 lo = READ4(sc, ETH_MACA0LR); 353 hi = READ4(sc, ETH_MACA0HR) & 0xffff; 354 if ((lo != 0xffffffff) || (hi != 0xffff)) { 355 hwaddr[0] = (lo >> 0) & 0xff; 356 hwaddr[1] = (lo >> 8) & 0xff; 357 hwaddr[2] = (lo >> 16) & 0xff; 358 hwaddr[3] = (lo >> 24) & 0xff; 359 hwaddr[4] = (hi >> 0) & 0xff; 360 hwaddr[5] = (hi >> 8) & 0xff; 361 } else { 362 rnd = arc4random() & 0x00ffffff; 363 hwaddr[0] = 'b'; 364 hwaddr[1] = 's'; 365 hwaddr[2] = 'd'; 366 hwaddr[3] = rnd >> 16; 367 hwaddr[4] = rnd >> 8; 368 hwaddr[5] = rnd >> 0; 369 } 370 } 371 372 /* 373 * Stats 374 */ 375 376 static void 377 dwc4_clear_stats(struct dwc_softc *sc) 378 { 379 uint32_t reg; 380 381 reg = READ4(sc, ETH_MMC_CONTROL); 382 reg |= MMC_CONTROL_CNTRST; 383 WRITE4(sc, ETH_MMC_CONTROL, reg); 384 } 385 386 void 387 dwc4_harvest_stats(struct dwc_softc *sc) 388 { 389 if_t ifp __unused; 390 int val; 391 392 /* We don't need to harvest too often. */ 393 if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL) 394 return; 395 396 sc->stats_harvest_count = 0; 397 ifp = sc->ifp; 398 399 val = READ4(sc, ETH_RX_RECEIVE_ERROR); 400 if_inc_counter(ifp, IFCOUNTER_IERRORS, val); 401 402 val = READ4(sc, ETH_TX_PACKET_COUNT_GOOD_BAD); 403 val -= READ4(sc, ETH_TX_PACKET_COUNT_GOOD); 404 if_inc_counter(ifp, IFCOUNTER_OERRORS, val); 405 406 val = READ4(sc, ETH_TX_SINGLE_COLLISION_GOOD_PACKETS); 407 val += READ4(sc, ETH_TX_MULTIPLE_COLLISION_GOOD_PACKETS); 408 val += READ4(sc, ETH_TX_LATE_COLLISION_PACKETS); 409 val += READ4(sc, ETH_TX_EXCESSIVE_COLLISION_PACKETS); 410 if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val); 411 412 dwc4_clear_stats(sc); 413 } 414 415 void 416 dwc4_intr(struct dwc_softc *sc) 417 { 418 uint32_t reg; 419 420 DWC_ASSERT_LOCKED(sc); 421 422 reg = READ4(sc, ETH_MACISR); 423 if (reg) 424 READ4(sc, ETH_MACPHYCSR); 425 } 426 427 void 428 dwc4_intr_disable(struct dwc_softc *sc) 429 { 430 431 WRITE4(sc, ETH_MACIER, 0); 432 } 433