1 /* $NetBSD: if_ec.c,v 1.36 2020/03/19 14:01:48 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matthew Fredette. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * 3Com 3C400 device driver 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: if_ec.c,v 1.36 2020/03/19 14:01:48 thorpej Exp $"); 38 39 #include "opt_inet.h" 40 #include "opt_ns.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/errno.h> 45 #include <sys/ioctl.h> 46 #include <sys/mbuf.h> 47 #include <sys/socket.h> 48 #include <sys/syslog.h> 49 #include <sys/device.h> 50 #include <sys/endian.h> 51 #include <sys/rndsource.h> 52 53 #include <net/if.h> 54 #include <net/if_dl.h> 55 #include <net/if_types.h> 56 #include <net/if_ether.h> 57 #include <net/if_media.h> 58 #include <net/bpf.h> 59 60 #ifdef INET 61 #include <netinet/in.h> 62 #include <netinet/in_systm.h> 63 #include <netinet/in_var.h> 64 #include <netinet/ip.h> 65 #include <netinet/if_inarp.h> 66 #endif 67 68 #include <machine/cpu.h> 69 #include <machine/autoconf.h> 70 #include <machine/idprom.h> 71 #include <machine/bus.h> 72 #include <machine/intr.h> 73 74 #include <sun2/dev/if_ecreg.h> 75 76 /* 77 * Interface softc. 78 */ 79 struct ec_softc { 80 device_t sc_dev; 81 void *sc_ih; 82 83 struct ethercom sc_ethercom; /* ethernet common */ 84 struct ifmedia sc_media; /* our supported media */ 85 86 bus_space_tag_t sc_iot; /* bus space tag */ 87 bus_space_handle_t sc_ioh; /* bus space handle */ 88 89 bool sc_txbusy; 90 u_char sc_jammed; /* nonzero if the net is jammed */ 91 u_char sc_colliding; /* nonzero if the net is colliding */ 92 uint32_t sc_backoff_seed; /* seed for the backoff PRNG */ 93 94 krndsource_t rnd_source; 95 }; 96 97 /* Macros to read and write the CSR. */ 98 #define ECREG_CSR_RD bus_space_read_2(sc->sc_iot, sc->sc_ioh, ECREG_CSR) 99 #define ECREG_CSR_WR(val) bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECREG_CSR, val) 100 101 /* After this many collisions, the packet is dropped. */ 102 #define EC_COLLISIONS_JAMMED 16 103 104 /* 105 * Various constants used in the backoff pseudorandom 106 * number generator. 107 */ 108 #define EC_BACKOFF_PRNG_COLL_MAX 10 109 #define EC_BACKOFF_PRNG_MUL 1103515245 110 #define EC_BACKOFF_PRNG_ADD 12345 111 #define EC_BACKOFF_PRNG_MASK 0x7fffffff 112 113 /* 114 * Prototypes 115 */ 116 int ec_intr(void *); 117 void ec_reset(struct ifnet *); 118 int ec_init(struct ifnet *); 119 int ec_ioctl(struct ifnet *, u_long, void *); 120 void ec_watchdog(struct ifnet *); 121 void ec_start(struct ifnet *); 122 123 void ec_recv(struct ec_softc *, int); 124 void ec_coll(struct ec_softc *); 125 void ec_copyin(struct ec_softc *, void *, int, size_t); 126 void ec_copyout(struct ec_softc *, const void *, int, size_t); 127 128 int ec_mediachange(struct ifnet *); 129 void ec_mediastatus(struct ifnet *, struct ifmediareq *); 130 131 int ec_match(device_t, cfdata_t, void *); 132 void ec_attach(device_t, device_t, void *); 133 134 CFATTACH_DECL_NEW(ec, sizeof(struct ec_softc), 135 ec_match, ec_attach, NULL, NULL); 136 137 /* 138 * Copy board memory to kernel. 139 */ 140 void 141 ec_copyin(struct ec_softc *sc, void *p, int offset, size_t size) 142 { 143 144 bus_space_copyin(sc->sc_iot, sc->sc_ioh, offset, p, size); 145 } 146 147 /* 148 * Copy from kernel space to board memory. 149 */ 150 void 151 ec_copyout(struct ec_softc *sc, const void *p, int offset, size_t size) 152 { 153 154 bus_space_copyout(sc->sc_iot, sc->sc_ioh, offset, p, size); 155 } 156 157 int 158 ec_match(device_t parent, cfdata_t cf, void *aux) 159 { 160 struct mbmem_attach_args *mbma = aux; 161 bus_space_handle_t bh; 162 bool matched; 163 164 /* No default Multibus address. */ 165 if (mbma->mbma_paddr == -1) 166 return 0; 167 168 /* Make sure there is something there... */ 169 if (bus_space_map(mbma->mbma_bustag, mbma->mbma_paddr, ECREG_BANK_SZ, 170 0, &bh)) 171 return 0; 172 matched = (bus_space_peek_2(mbma->mbma_bustag, bh, 0, NULL) == 0); 173 bus_space_unmap(mbma->mbma_bustag, bh, ECREG_BANK_SZ); 174 if (!matched) 175 return 0; 176 177 /* Default interrupt priority. */ 178 if (mbma->mbma_pri == -1) 179 mbma->mbma_pri = 3; 180 181 return 1; 182 } 183 184 void 185 ec_attach(device_t parent, device_t self, void *aux) 186 { 187 struct ec_softc *sc = device_private(self); 188 struct mbmem_attach_args *mbma = aux; 189 struct ifnet *ifp = &sc->sc_ethercom.ec_if; 190 uint8_t myaddr[ETHER_ADDR_LEN]; 191 192 sc->sc_dev = self; 193 194 aprint_normal("\n"); 195 196 /* Map in the board control regs. */ 197 sc->sc_iot = mbma->mbma_bustag; 198 if (bus_space_map(mbma->mbma_bustag, mbma->mbma_paddr, ECREG_BANK_SZ, 199 0, &sc->sc_ioh)) 200 panic("%s: can't map regs", __func__); 201 202 /* Reset the board. */ 203 ECREG_CSR_WR(EC_CSR_RESET); 204 delay(160); 205 206 /* 207 * Copy out the board ROM Ethernet address, 208 * and use the non-vendor-ID part to seed 209 * our backoff pseudorandom number generator. 210 */ 211 bus_space_read_region_1(sc->sc_iot, sc->sc_ioh, 212 ECREG_AROM, myaddr, ETHER_ADDR_LEN); 213 sc->sc_backoff_seed = 214 (myaddr[3] << 16) | (myaddr[4] << 8) | (myaddr[5]) | 1; 215 216 /* Initialize ifnet structure. */ 217 strcpy(ifp->if_xname, device_xname(self)); 218 ifp->if_softc = sc; 219 ifp->if_start = ec_start; 220 ifp->if_ioctl = ec_ioctl; 221 ifp->if_init = ec_init; 222 ifp->if_watchdog = ec_watchdog; 223 ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 224 IFQ_SET_READY(&ifp->if_snd); 225 226 /* Initialize ifmedia structures. */ 227 sc->sc_ethercom.ec_ifmedia = &sc->sc_media; 228 ifmedia_init(&sc->sc_media, 0, ec_mediachange, ec_mediastatus); 229 ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_MANUAL, 0, NULL); 230 ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_MANUAL); 231 232 /* Now we can attach the interface. */ 233 if_attach(ifp); 234 if_deferred_start_init(ifp, NULL); 235 idprom_etheraddr(myaddr); 236 ether_ifattach(ifp, myaddr); 237 aprint_normal_dev(self, "address %s\n", ether_sprintf(myaddr)); 238 239 bus_intr_establish(mbma->mbma_bustag, mbma->mbma_pri, IPL_NET, 0, 240 ec_intr, sc); 241 242 rnd_attach_source(&sc->rnd_source, device_xname(self), 243 RND_TYPE_NET, RND_FLAG_DEFAULT); 244 } 245 246 /* 247 * Reset interface. 248 */ 249 void 250 ec_reset(struct ifnet *ifp) 251 { 252 int s; 253 254 s = splnet(); 255 ec_init(ifp); 256 splx(s); 257 } 258 259 260 /* 261 * Initialize interface. 262 */ 263 int 264 ec_init(struct ifnet *ifp) 265 { 266 struct ec_softc *sc = ifp->if_softc; 267 268 /* Reset the board. */ 269 ECREG_CSR_WR(EC_CSR_RESET); 270 delay(160); 271 272 /* Set the Ethernet address. */ 273 bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, 274 ECREG_ARAM, CLLADDR(sc->sc_ethercom.ec_if.if_sadl), ETHER_ADDR_LEN); 275 ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | EC_CSR_AMSW); 276 ECREG_CSR_WR(ECREG_CSR_RD & 0); 277 278 /* Enable interrupts. */ 279 ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | 280 EC_CSR_BBSW | EC_CSR_ABSW | EC_CSR_BINT | EC_CSR_AINT | 281 (ifp->if_flags & IFF_PROMISC ? EC_CSR_PROMISC : EC_CSR_PA)); 282 283 /* Set flags appropriately. */ 284 ifp->if_flags |= IFF_RUNNING; 285 sc->sc_txbusy = false; 286 287 /* Start output. */ 288 ec_start(ifp); 289 290 return 0; 291 } 292 293 /* 294 * Start output on interface. 295 */ 296 void 297 ec_start(struct ifnet *ifp) 298 { 299 struct ec_softc *sc = ifp->if_softc; 300 struct mbuf *m, *m0; 301 int s; 302 u_int count, realcount; 303 bus_size_t off; 304 static uint8_t padding[ETHER_MIN_LEN - ETHER_CRC_LEN] = {0}; 305 306 s = splnet(); 307 308 /* Don't do anything if output is active. */ 309 if (sc->sc_txbusy) { 310 splx(s); 311 return; 312 } 313 /* Don't do anything if the output queue is empty. */ 314 IFQ_DEQUEUE(&ifp->if_snd, m0); 315 if (m0 == NULL) { 316 splx(s); 317 return; 318 } 319 320 /* The BPF tap. */ 321 bpf_mtap(ifp, m0, BPF_D_OUT); 322 323 /* Size the packet. */ 324 count = EC_BUF_SZ - m0->m_pkthdr.len; 325 326 /* Copy the packet into the xmit buffer. */ 327 realcount = MIN(count, EC_PKT_MAXTDOFF); 328 bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECREG_TBUF, realcount); 329 for (off = realcount, m = m0; m != 0; off += m->m_len, m = m->m_next) 330 ec_copyout(sc, mtod(m, uint8_t *), ECREG_TBUF + off, m->m_len); 331 m_freem(m0); 332 if (count - realcount) 333 ec_copyout(sc, padding, ECREG_TBUF + off, count - realcount); 334 335 /* Enable the transmitter. */ 336 ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_PA) | 337 EC_CSR_TBSW | EC_CSR_TINT | EC_CSR_JINT); 338 sc->sc_txbusy = true; 339 340 /* Done. */ 341 splx(s); 342 } 343 344 /* 345 * Controller interrupt. 346 */ 347 int 348 ec_intr(void *arg) 349 { 350 struct ec_softc *sc = arg; 351 struct ifnet *ifp = &sc->sc_ethercom.ec_if; 352 int recv_first; 353 int recv_second; 354 int retval; 355 356 retval = 0; 357 358 /* Check for received packet(s). */ 359 recv_first = recv_second = 0; 360 switch (ECREG_CSR_RD & (EC_CSR_BBSW | EC_CSR_ABSW | EC_CSR_RBBA)) { 361 362 case (EC_CSR_BBSW | EC_CSR_ABSW): 363 case (EC_CSR_BBSW | EC_CSR_ABSW | EC_CSR_RBBA): 364 /* Neither buffer is full. Is this a transmit interrupt? 365 * Acknowledge the interrupt ourselves. */ 366 ECREG_CSR_WR(ECREG_CSR_RD & 367 (EC_CSR_TINT | EC_CSR_JINT | EC_CSR_PAMASK)); 368 ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | 369 EC_CSR_BINT | EC_CSR_AINT); 370 break; 371 372 case EC_CSR_BBSW: 373 case (EC_CSR_BBSW | EC_CSR_RBBA): 374 /* Only the A buffer is full. */ 375 recv_first = EC_CSR_AINT; 376 break; 377 378 case EC_CSR_ABSW: 379 case (EC_CSR_ABSW | EC_CSR_RBBA): 380 /* Only the B buffer is full. */ 381 recv_first = EC_CSR_BINT; 382 break; 383 384 case 0: 385 /* Both the A buffer and the B buffer are full, and the A 386 * buffer is older than the B buffer. */ 387 recv_first = EC_CSR_AINT; 388 recv_second = EC_CSR_BINT; 389 break; 390 391 case EC_CSR_RBBA: 392 /* Both the A buffer and the B buffer are full, and the B 393 * buffer is older than the A buffer. */ 394 recv_first = EC_CSR_BINT; 395 recv_second = EC_CSR_AINT; 396 break; 397 } 398 399 /* Receive packets. */ 400 if (recv_first) { 401 402 /* Acknowledge the interrupt. */ 403 ECREG_CSR_WR(ECREG_CSR_RD & 404 ((EC_CSR_BINT | EC_CSR_AINT | EC_CSR_TINT | EC_CSR_JINT | 405 EC_CSR_PAMASK) ^ (recv_first | recv_second))); 406 407 /* Receive a packet. */ 408 ec_recv(sc, recv_first); 409 410 /* Receive a packet. */ 411 if (recv_second) 412 ec_recv(sc, recv_second); 413 414 retval++; 415 } 416 /* Check for a transmitted packet. */ 417 if (sc->sc_txbusy) { 418 419 /* If we got a collision. */ 420 if (ECREG_CSR_RD & EC_CSR_JAM) { 421 ECREG_CSR_WR(ECREG_CSR_RD & 422 (EC_CSR_BINT | EC_CSR_AINT | EC_CSR_PAMASK)); 423 if_statinc(ifp, if_collisions); 424 retval++; 425 ec_coll(sc); 426 427 } 428 /* If we transmitted a packet. */ 429 else if ((ECREG_CSR_RD & EC_CSR_TBSW) == 0) { 430 ECREG_CSR_WR(ECREG_CSR_RD & 431 (EC_CSR_BINT | EC_CSR_AINT | EC_CSR_PAMASK)); 432 retval++; 433 if_statinc(ifp, if_opackets); 434 sc->sc_jammed = 0; 435 sc->sc_txbusy = false; 436 if_schedule_deferred_start(ifp); 437 } 438 } else { 439 440 /* Make sure we disable transmitter interrupts. */ 441 ECREG_CSR_WR(ECREG_CSR_RD & 442 (EC_CSR_BINT | EC_CSR_AINT | EC_CSR_PAMASK)); 443 } 444 445 return retval; 446 } 447 448 /* 449 * Read in a packet from the board. 450 */ 451 void 452 ec_recv(struct ec_softc *sc, int intbit) 453 { 454 struct ifnet *ifp = &sc->sc_ethercom.ec_if; 455 struct mbuf *m0, *m, *newm; 456 bus_size_t buf; 457 uint16_t status; 458 uint16_t doff; 459 int length, total_length; 460 461 buf = EC_CSR_INT_BUF(intbit); 462 463 /* Read in the packet status. */ 464 status = bus_space_read_2(sc->sc_iot, sc->sc_ioh, buf); 465 doff = status & EC_PKT_DOFF; 466 467 for (total_length = -1, m0 = NULL;;) { 468 469 /* Check for an error. */ 470 if (status & (EC_PKT_FCSERR | EC_PKT_RGERR | EC_PKT_FRERR) || 471 doff < EC_PKT_MINRDOFF || 472 doff > EC_PKT_MAXRDOFF) { 473 printf("%s: garbled packet, status 0x%04x; dropping\n", 474 device_xname(sc->sc_dev), (unsigned int)status); 475 break; 476 } 477 478 /* Adjust for the header. */ 479 total_length = doff - EC_PKT_RDOFF; 480 buf += EC_PKT_RDOFF; 481 482 /* XXX - sometimes the card reports a large data offset. */ 483 if (total_length > (ETHER_MAX_LEN - ETHER_CRC_LEN)) { 484 #ifdef DEBUG 485 printf("%s: fixing too-large length of %d\n", 486 device_xname(sc->sc_dev), total_length); 487 #endif 488 total_length = (ETHER_MAX_LEN - ETHER_CRC_LEN); 489 } 490 491 MGETHDR(m0, M_DONTWAIT, MT_DATA); 492 if (m0 == NULL) 493 break; 494 m_set_rcvif(m0, ifp); 495 m0->m_pkthdr.len = total_length; 496 length = MHLEN; 497 m = m0; 498 499 while (total_length > 0) { 500 if (total_length >= MINCLSIZE) { 501 MCLGET(m, M_DONTWAIT); 502 if ((m->m_flags & M_EXT) == 0) 503 break; 504 length = MCLBYTES; 505 } 506 m->m_len = length = uimin(total_length, length); 507 ec_copyin(sc, mtod(m, uint8_t *), buf, length); 508 total_length -= length; 509 buf += length; 510 511 if (total_length > 0) { 512 MGET(newm, M_DONTWAIT, MT_DATA); 513 if (newm == NULL) 514 break; 515 length = MLEN; 516 m = m->m_next = newm; 517 } 518 } 519 break; 520 } 521 522 if (total_length == 0) { 523 /* Pass the packet up. */ 524 if_percpuq_enqueue(ifp->if_percpuq, m0); 525 526 } else { 527 /* Something went wrong. */ 528 if (m0 != NULL) 529 m_freem(m0); 530 if_statinc(ifp, if_ierrors); 531 } 532 533 /* Give the receive buffer back to the card. */ 534 buf = EC_CSR_INT_BUF(intbit); 535 bus_space_write_2(sc->sc_iot, sc->sc_ioh, buf, 0); 536 ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | 537 EC_CSR_INT_BSW(intbit) | intbit); 538 } 539 540 int 541 ec_mediachange(struct ifnet *ifp) 542 { 543 544 return 0; 545 } 546 547 void 548 ec_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) 549 { 550 551 if ((ifp->if_flags & IFF_UP) == 0) 552 return; 553 554 ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 555 } 556 557 /* 558 * Process an ioctl request. This code needs some work - it looks pretty ugly. 559 */ 560 int 561 ec_ioctl(struct ifnet *ifp, u_long cmd, void *data) 562 { 563 struct ifaddr *ifa = (struct ifaddr *)data; 564 int s, error = 0; 565 566 s = splnet(); 567 568 switch (cmd) { 569 570 case SIOCINITIFADDR: 571 ifp->if_flags |= IFF_UP; 572 573 switch (ifa->ifa_addr->sa_family) { 574 #ifdef INET 575 case AF_INET: 576 ec_init(ifp); 577 arp_ifinit(ifp, ifa); 578 break; 579 #endif 580 default: 581 ec_init(ifp); 582 break; 583 } 584 break; 585 586 case SIOCSIFFLAGS: 587 if ((error = ifioctl_common(ifp, cmd, data)) != 0) 588 break; 589 switch (ifp->if_flags & (IFF_UP | IFF_RUNNING)) { 590 case IFF_RUNNING: 591 /* 592 * If interface is marked down and it is running, then 593 * stop it. 594 */ 595 ifp->if_flags &= ~IFF_RUNNING; 596 break; 597 case IFF_UP: 598 /* 599 * If interface is marked up and it is stopped, then 600 * start it. 601 */ 602 ec_init(ifp); 603 break; 604 default: 605 /* 606 * Some other important flag might have changed, so 607 * reset. 608 */ 609 ec_reset(ifp); 610 break; 611 } 612 break; 613 614 default: 615 error = ether_ioctl(ifp, cmd, data); 616 break; 617 } 618 619 splx(s); 620 return error; 621 } 622 623 /* 624 * Collision routine. 625 */ 626 void 627 ec_coll(struct ec_softc *sc) 628 { 629 struct ifnet *ifp = &sc->sc_ethercom.ec_if; 630 u_short jams; 631 632 if ((++sc->sc_colliding) >= EC_COLLISIONS_JAMMED) { 633 if_statinc(ifp, if_oerrors); 634 if (!sc->sc_jammed) 635 printf("%s: ethernet jammed\n", 636 device_xname(sc->sc_dev)); 637 sc->sc_jammed = 1; 638 sc->sc_colliding = 0; 639 sc->sc_txbusy = false; 640 if_schedule_deferred_start(ifp); 641 } else { 642 jams = MAX(sc->sc_colliding, EC_BACKOFF_PRNG_COLL_MAX); 643 sc->sc_backoff_seed = 644 ((sc->sc_backoff_seed * EC_BACKOFF_PRNG_MUL) + 645 EC_BACKOFF_PRNG_ADD) & EC_BACKOFF_PRNG_MASK; 646 bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECREG_BACKOFF, 647 -(((sc->sc_backoff_seed >> 8) & ~(-1 << jams)) + 1)); 648 ECREG_CSR_WR((ECREG_CSR_RD & EC_CSR_INTPA) | 649 EC_CSR_JAM | EC_CSR_TINT | EC_CSR_JINT); 650 } 651 } 652 653 /* 654 * Device timeout routine. 655 */ 656 void 657 ec_watchdog(struct ifnet *ifp) 658 { 659 struct ec_softc *sc = ifp->if_softc; 660 661 log(LOG_ERR, "%s: device timeout\n", device_xname(sc->sc_dev)); 662 if_statinc(ifp, if_oerrors); 663 664 ec_reset(ifp); 665 } 666