1/* 2 * Linux device driver for 3 * Broadcom BCM47XX 10/100 Mbps Ethernet Controller 4 * 5 * Copyright 2007, Broadcom Corporation 6 * All Rights Reserved. 7 * 8 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; 9 * the contents of this file may not be disclosed to third parties, copied 10 * or duplicated in any form, in whole or in part, without the prior 11 * written permission of Broadcom Corporation. 12 * 13 * $Id: et_linux.c,v 1.1.1.1 2008/10/15 03:25:54 james26_jang Exp $ 14 */ 15 16#define __UNDEF_NO_VERSION__ 17 18#include <typedefs.h> 19 20#include <linux/module.h> 21#include <linuxver.h> 22#include <bcmdefs.h> 23#include <osl.h> 24 25#include <linux/types.h> 26#include <linux/errno.h> 27#include <linux/pci.h> 28#include <linux/init.h> 29#include <linux/kernel.h> 30#include <linux/netdevice.h> 31#include <linux/etherdevice.h> 32#include <linux/skbuff.h> 33#include <linux/delay.h> 34#include <linux/string.h> 35#include <linux/sockios.h> 36#ifdef SIOCETHTOOL 37#include <linux/ethtool.h> 38#endif /* SIOCETHTOOL */ 39#include <linux/ip.h> 40 41#include <asm/system.h> 42#include <asm/io.h> 43#include <asm/irq.h> 44#include <asm/pgtable.h> 45#include <asm/uaccess.h> 46 47#include <epivers.h> 48#include <bcmendian.h> 49#include <bcmdefs.h> 50#include <proto/ethernet.h> 51#include <proto/vlan.h> 52#include <bcmdevs.h> 53#include <bcmenetmib.h> 54#include <bcmenetrxh.h> 55#include <bcmenetphy.h> 56#include <etioctl.h> 57#include <bcmutils.h> 58#include <pcicfg.h> 59#include <et_dbg.h> 60#include <etc.h> 61 62#include <linux/proc_fs.h> 63 64typedef struct et_info { 65 etc_info_t *etc; /* pointer to common os-independent data */ 66 struct net_device *dev; /* backpoint to device */ 67 struct pci_dev *pdev; /* backpoint to pci_dev */ 68 void *osh; /* pointer to os handle */ 69 spinlock_t lock; /* per-device perimeter lock */ 70 struct sk_buff_head txq; /* send queue */ 71 void *regsva; /* opaque chip registers virtual address */ 72 struct timer_list timer; /* one second watchdog timer */ 73 struct net_device_stats stats; /* stat counter reporting structure */ 74 int events; /* bit channel between isr and dpc */ 75 struct tasklet_struct tasklet; /* dpc tasklet */ 76 struct et_info *next; /* pointer to next et_info_t in chain */ 77 bool resched; /* dpc was rescheduled */ 78} et_info_t; 79 80static int et_found = 0; 81static et_info_t *et_list = NULL; 82 83/* defines */ 84#define DATAHIWAT 50 /* data msg txq hiwat mark */ 85 86#define ET_INFO(dev) (et_info_t*)((dev)->priv) 87 88#define ET_LOCK(et) spin_lock_bh(&(et)->lock) 89#define ET_UNLOCK(et) spin_unlock_bh(&(et)->lock) 90 91#define INT_LOCK(flags) local_irq_save(flags) 92#define INT_UNLOCK(flags) local_irq_restore(flags) 93 94#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 5) 95#error Linux version must be newer than 2.4.5 96#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 5) */ 97 98/* prototypes called by etc.c */ 99void et_init(et_info_t *et); 100void et_reset(et_info_t *et); 101void et_link_up(et_info_t *et); 102void et_link_down(et_info_t *et); 103void et_up(et_info_t *et); 104void et_down(et_info_t *et, int reset); 105void et_dump(et_info_t *et, struct bcmstrbuf *b); 106 107unsigned int etc_arl_dump(etc_info_t *etc, char *buffer, int maxno); 108 109/* local prototypes */ 110static void et_free(et_info_t *et); 111static int et_open(struct net_device *dev); 112static int et_close(struct net_device *dev); 113static int et_start(struct sk_buff *skb, struct net_device *dev); 114static void et_sendnext(et_info_t *et); 115static struct net_device_stats *et_get_stats(struct net_device *dev); 116static int et_set_mac_address(struct net_device *dev, void *addr); 117static void et_set_multicast_list(struct net_device *dev); 118static void et_watchdog(ulong data); 119static int et_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); 120static irqreturn_t et_isr(int irq, void *dev_id, struct pt_regs *ptregs); 121static void et_dpc(ulong data); 122static void et_sendup(et_info_t *et, struct sk_buff *skb); 123 124/* recognized PCI IDs */ 125static struct pci_device_id et_id_table[] __devinitdata = { 126 { vendor: PCI_ANY_ID, 127 device: PCI_ANY_ID, 128 subvendor: PCI_ANY_ID, 129 subdevice: PCI_ANY_ID, 130 class: PCI_CLASS_NETWORK_ETHERNET << 8, 131 class_mask: 0xffff00, 132 driver_data: 0, 133 }, 134 { 0, } 135}; 136 137et_info_t *et_g=NULL; 138 139MODULE_DEVICE_TABLE(pci, et_id_table); 140 141 142static int __devinit 143et_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 144{ 145 struct net_device *dev; 146 et_info_t *et; 147 osl_t *osh; 148 char name[128]; 149 int unit = et_found; 150 151 152 ET_TRACE(("et%d: et_probe: bus %d slot %d func %d irq %d\n", unit, 153 pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq)); 154 155 if (!etc_chipmatch(pdev->vendor, pdev->device)) 156 return -ENODEV; 157 158 osh = osl_attach(pdev, PCI_BUS, FALSE); 159 ASSERT(osh); 160 161 pci_set_master(pdev); 162 pci_enable_device(pdev); 163 164 if (!(dev = (struct net_device *) MALLOC(osh, sizeof(struct net_device)))) { 165 ET_ERROR(("et%d: et_probe: out of memory, malloced %d bytes\n", unit, 166 MALLOCED(osh))); 167 osl_detach(osh); 168 return -ENOMEM; 169 } 170 bzero(dev, sizeof(struct net_device)); 171 172 if (!init_etherdev(dev, 0)) { 173 ET_ERROR(("et%d: et_probe: init_etherdev() failed\n", unit)); 174 MFREE(osh, dev, sizeof(struct net_device)); 175 osl_detach(osh); 176 return -ENOMEM; 177 } 178 179 /* allocate private info */ 180 if ((et = (et_info_t*) MALLOC(osh, sizeof(et_info_t))) == NULL) { 181 ET_ERROR(("et%d: et_probe: out of memory, malloced %d bytes\n", unit, 182 MALLOCED(osh))); 183 MFREE(osh, dev, sizeof(et_info_t)); 184 osl_detach(osh); 185 return -ENOMEM; 186 } 187 bzero(et, sizeof(et_info_t)); 188 dev->priv = (void*) et; 189 et->dev = dev; 190 et->pdev = pdev; 191 et->osh = osh; 192 pci_set_drvdata(pdev, et); 193 194 /* map chip registers (47xx: and sprom) */ 195 dev->base_addr = pci_resource_start(pdev, 0); 196 if ((et->regsva = ioremap_nocache(dev->base_addr, PCI_BAR0_WINSZ)) == NULL) { 197 ET_ERROR(("et%d: ioremap() failed\n", unit)); 198 goto fail; 199 } 200 201 spin_lock_init(&et->lock); 202 203 skb_queue_head_init(&et->txq); 204 205 /* common load-time initialization */ 206 if ((et->etc = etc_attach((void*)et, pdev->vendor, pdev->device, unit, osh, et->regsva)) == 207 NULL) { 208 ET_ERROR(("et%d: etc_attach() failed\n", unit)); 209 goto fail; 210 } 211 212 bcopy(&et->etc->cur_etheraddr, dev->dev_addr, ETHER_ADDR_LEN); 213 214 /* init 1 second watchdog timer */ 215 init_timer(&et->timer); 216 et->timer.data = (ulong)dev; 217 et->timer.function = et_watchdog; 218 219 /* setup the bottom half handler */ 220 tasklet_init(&et->tasklet, et_dpc, (ulong)et); 221 222 /* register our interrupt handler */ 223 if (request_irq(pdev->irq, et_isr, SA_SHIRQ, dev->name, et)) { 224 ET_ERROR(("et%d: request_irq() failed\n", unit)); 225 goto fail; 226 } 227 dev->irq = pdev->irq; 228 229 /* add us to the global linked list */ 230 et->next = et_list; 231 et_list = et; 232 233 /* print hello string */ 234 (*et->etc->chops->longname)(et->etc->ch, name, sizeof(name)); 235 printf("%s: %s %s\n", dev->name, name, EPI_VERSION_STR); 236 237 /* lastly, enable our entry points */ 238 dev->open = et_open; 239 dev->stop = et_close; 240 dev->hard_start_xmit = et_start; 241 dev->get_stats = et_get_stats; 242 dev->set_mac_address = et_set_mac_address; 243 dev->set_multicast_list = et_set_multicast_list; 244 dev->do_ioctl = et_ioctl; 245 246 if (register_netdev(dev)) { 247 ET_ERROR(("et%d: register_netdev() failed\n", unit)); 248 goto fail; 249 } 250 251 et_found++; 252 return (0); 253 254fail: 255 et_free(et); 256 return (-ENODEV); 257} 258 259static int 260et_suspend(struct pci_dev *pdev, u32 state) 261{ 262 et_info_t *et; 263 264 if ((et = (et_info_t *) pci_get_drvdata(pdev))) { 265 netif_device_detach(et->dev); 266 ET_LOCK(et); 267 et_down(et, 1); 268 ET_UNLOCK(et); 269 } 270 271 return 0; 272} 273 274static int 275et_resume(struct pci_dev *pdev) 276{ 277 et_info_t *et; 278 279 if ((et = (et_info_t *) pci_get_drvdata(pdev))) { 280 ET_LOCK(et); 281 et_up(et); 282 ET_UNLOCK(et); 283 netif_device_attach(et->dev); 284 } 285 286 return 0; 287} 288 289/* Compatibility routines */ 290#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) 291static void 292_et_suspend(struct pci_dev *pdev) 293{ 294 et_suspend(pdev, 0); 295} 296 297static void 298_et_resume(struct pci_dev *pdev) 299{ 300 et_resume(pdev); 301} 302#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) */ 303 304static void __devexit 305et_remove(struct pci_dev *pdev) 306{ 307 et_info_t *et; 308 309 if (!etc_chipmatch(pdev->vendor, pdev->device)) 310 return; 311 312 et_suspend(pdev, 0); 313 314 if ((et = (et_info_t *) pci_get_drvdata(pdev))) { 315 et_free(et); 316 pci_set_drvdata(pdev, NULL); 317 } 318} 319 320#define TBUFFER_LEN 32 321#define TBUFFER_COUNT 96 322 323static int et_arl_get_info(char *buffer, char **start, off_t offset, int length) 324{ 325 int len=0; 326 int size=0; 327 char tbuffer[TBUFFER_LEN*TBUFFER_COUNT]; 328 329 if(!et_g || !et_g->etc) return 0; 330 331 332 len = sprintf(tbuffer,"[MAC Address] [PO][ST][EXT]\n"); 333 /*XX:XX:XX:XX:XX:XX XX XX XX */ 334 335 size = etc_arl_dump(et_g->etc, tbuffer+len, TBUFFER_COUNT); 336 337 len += size; 338 len += sprintf(tbuffer+len,"\n"); 339 340 //printk("arl_get_info start %x %x %x\n", (int)offset, length, len); 341 342 strncpy(buffer, tbuffer, len+1); 343 //printk("*** test buffer %s. ***\n", buffer); 344 if(offset>len) 345 offset = len; 346 347 *start = buffer + offset; /* Start of wanted data */ 348 349 if(offset+length>len) 350 length = len-offset; 351 //printk("arl_get_info %x %x\n", (int)offset, length); 352 //printk("\n%s\n", tbuffer); 353 354 return length; 355} 356 357static struct pci_driver et_pci_driver = { 358 name: "et", 359 probe: et_probe, 360#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) 361 suspend: _et_suspend, 362 resume: _et_resume, 363#else 364 suspend: et_suspend, 365 resume: et_resume, 366#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) */ 367 remove: __devexit_p(et_remove), 368 id_table: et_id_table, 369 }; 370 371static int __init 372et_module_init(void) 373{ 374 proc_net_create ("arl", 0, et_arl_get_info); 375 return pci_module_init(&et_pci_driver); 376} 377 378static void __exit 379et_module_exit(void) 380{ 381 pci_unregister_driver(&et_pci_driver); 382} 383 384module_init(et_module_init); 385module_exit(et_module_exit); 386 387static void 388et_free(et_info_t *et) 389{ 390 et_info_t **prev; 391 osl_t *osh; 392 393 if (et == NULL) 394 return; 395 396 ET_TRACE(("et: et_free\n")); 397 398 if (et->dev && et->dev->irq) 399 free_irq(et->dev->irq, et); 400 401 if (et->dev) { 402 unregister_netdev(et->dev); 403 MFREE(et->osh, et->dev, sizeof(struct net_device)); 404 et->dev = NULL; 405 } 406 407 /* free common resources */ 408 if (et->etc) { 409 etc_detach(et->etc); 410 et->etc = NULL; 411 } 412 413 /* 414 * unregister_netdev() calls get_stats() which may read chip registers 415 * so we cannot unmap the chip registers until after calling unregister_netdev() . 416 */ 417 if (et->regsva) { 418 iounmap((void*)et->regsva); 419 et->regsva = NULL; 420 } 421 422 /* remove us from the global linked list */ 423 for (prev = &et_list; *prev; prev = &(*prev)->next) 424 if (*prev == et) { 425 *prev = et->next; 426 break; 427 } 428 429 osh = et->osh; 430 MFREE(et->osh, et, sizeof(et_info_t)); 431 432 ASSERT(MALLOCED(osh) == 0); 433 434 osl_detach(osh); 435} 436 437static int 438et_open(struct net_device *dev) 439{ 440 et_info_t *et; 441 442 et = ET_INFO(dev); 443 et_g = et; 444 445 ET_TRACE(("et%d: et_open\n", et->etc->unit)); 446 447 et->etc->promisc = (dev->flags & IFF_PROMISC)? TRUE: FALSE; 448 449 ET_LOCK(et); 450 et_up(et); 451 ET_UNLOCK(et); 452 453 OLD_MOD_INC_USE_COUNT; 454 455 return (0); 456} 457 458static int 459et_close(struct net_device *dev) 460{ 461 et_info_t *et; 462 463 et = ET_INFO(dev); 464 et_g = NULL; 465 466 ET_TRACE(("et%d: et_close\n", et->etc->unit)); 467 468 et->etc->promisc = FALSE; 469 470 ET_LOCK(et); 471 et_down(et, 1); 472 ET_UNLOCK(et); 473 474 OLD_MOD_DEC_USE_COUNT; 475 476 return (0); 477} 478 479/* 480 * Yeah, queueing the packets on a tx queue instead of throwing them 481 * directly into the descriptor ring in the case of dma is kinda lame, 482 * but this results in a unified transmit path for both dma and pio 483 * and localizes/simplifies the netif_*_queue semantics, too. 484 */ 485static int 486et_start(struct sk_buff *skb, struct net_device *dev) 487{ 488 et_info_t *et; 489 490 et = ET_INFO(dev); 491 492 ET_TRACE(("et%d: et_start: len %d\n", et->etc->unit, skb->len)); 493 ET_LOG("et%d: et_start: len %d", et->etc->unit, skb->len); 494 495 ET_LOCK(et); 496 497 /* put it on the tx queue and call sendnext */ 498 skb_queue_tail(&et->txq, skb); 499 et_sendnext(et); 500 501 ET_UNLOCK(et); 502 503 ET_LOG("et%d: et_start ret\n", et->etc->unit, 0); 504 505 return (0); 506} 507 508static void 509et_sendnext(et_info_t *et) 510{ 511 etc_info_t *etc; 512 struct sk_buff *skb; 513 void *p; 514 515 etc = et->etc; 516 517 ET_TRACE(("et%d: et_sendnext\n", etc->unit)); 518 ET_LOG("et%d: et_sendnext", etc->unit, 0); 519 520 /* dequeue and send each packet */ 521#ifdef DMA 522 while (*etc->txavail > 0) { 523#else 524 while (etc->pioactive == NULL) { 525#endif /* DMA */ 526 if ((skb = skb_dequeue(&et->txq)) == NULL) 527 break; 528 529 ET_PRHDR("tx", (struct ether_header*) skb->data, skb->len, etc->unit); 530 ET_PRPKT("txpkt", skb->data, skb->len, etc->unit); 531 532 /* Convert the packet. */ 533 if ((p = PKTFRMNATIVE(et->osh, skb)) == NULL) { 534 dev_kfree_skb_any(skb); 535 return; 536 } 537 538 (*etc->chops->tx)(etc->ch, p); 539 540 etc->txframe++; 541 etc->txbyte += skb->len; 542 } 543 544 /* stop the queue whenever txq fills */ 545 if ((skb_queue_len(&et->txq) > DATAHIWAT) && !netif_queue_stopped(et->dev)) 546 netif_stop_queue(et->dev); 547 else if (netif_queue_stopped(et->dev) && (skb_queue_len(&et->txq) < (DATAHIWAT/2))) { 548 netif_wake_queue(et->dev); 549 } 550} 551 552void 553et_init(et_info_t *et) 554{ 555 ET_TRACE(("et%d: et_init\n", et->etc->unit)); 556 ET_LOG("et%d: et_init", et->etc->unit, 0); 557 558 et_reset(et); 559 560 etc_init(et->etc); 561} 562 563 564void 565et_reset(et_info_t *et) 566{ 567 ET_TRACE(("et%d: et_reset\n", et->etc->unit)); 568 569 etc_reset(et->etc); 570 571 /* zap any pending dpc interrupt bits */ 572 et->events = 0; 573 574 /* dpc will not be rescheduled */ 575 et->resched = 0; 576} 577 578void 579et_up(et_info_t *et) 580{ 581 etc_info_t *etc; 582 583 etc = et->etc; 584 585 if (etc->up) 586 return; 587 588 ET_TRACE(("et%d: et_up\n", etc->unit)); 589 590 etc_up(etc); 591 592 /* schedule one second watchdog timer */ 593 et->timer.expires = jiffies + HZ; 594 add_timer(&et->timer); 595 596 netif_start_queue(et->dev); 597} 598 599void 600et_down(et_info_t *et, int reset) 601{ 602 etc_info_t *etc; 603 struct sk_buff *skb; 604 605 etc = et->etc; 606 607 ET_TRACE(("et%d: et_down\n", etc->unit)); 608 609 netif_down(et->dev); 610 netif_stop_queue(et->dev); 611 612 /* stop watchdog timer */ 613 del_timer(&et->timer); 614 615 etc_down(etc, reset); 616 617 /* flush the txq */ 618 while ((skb = skb_dequeue(&et->txq))) 619 dev_kfree_skb_any(skb); 620 621 /* kill dpc */ 622 ET_UNLOCK(et); 623 tasklet_kill(&et->tasklet); 624 ET_LOCK(et); 625} 626 627/* 628 * These are interrupt on/off entry points. Disable interrupts 629 * during interrupt state transition. 630 */ 631void 632et_intrson(et_info_t *et) 633{ 634 unsigned long flags; 635 INT_LOCK(flags); 636 (*et->etc->chops->intrson)(et->etc->ch); 637 INT_UNLOCK(flags); 638} 639 640static void 641et_watchdog(ulong data) 642{ 643 et_info_t *et; 644 645 et = ET_INFO((struct net_device*)data); 646 647 ET_LOCK(et); 648 649 etc_watchdog(et->etc); 650 651 /* reschedule one second watchdog timer */ 652 et->timer.expires = jiffies + HZ; 653 add_timer(&et->timer); 654 655 ET_UNLOCK(et); 656} 657 658 659#ifdef SIOCETHTOOL 660static int 661et_ethtool(et_info_t *et, struct ethtool_cmd *ecmd) 662{ 663 int ret = 0; 664 int speed; 665 struct ethtool_drvinfo *info; 666 667 ET_LOCK(et); 668 669 switch (ecmd->cmd) { 670 case ETHTOOL_GSET: 671 ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | 672 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | 673 SUPPORTED_Autoneg); 674 ecmd->advertising = ADVERTISED_TP; 675 ecmd->advertising |= (et->etc->advertise & ADV_10HALF) ? 676 ADVERTISED_10baseT_Half : 0; 677 ecmd->advertising |= (et->etc->advertise & ADV_10FULL) ? 678 ADVERTISED_10baseT_Full : 0; 679 ecmd->advertising |= (et->etc->advertise & ADV_100HALF) ? 680 ADVERTISED_100baseT_Half : 0; 681 ecmd->advertising |= (et->etc->advertise & ADV_100FULL) ? 682 ADVERTISED_100baseT_Full : 0; 683 if (et->etc->linkstate) { 684 ecmd->speed = (et->etc->speed == 100) ? SPEED_100 : SPEED_10; 685 ecmd->duplex = (et->etc->duplex == 1) ? DUPLEX_FULL : DUPLEX_HALF; 686 } else { 687 ecmd->speed = 0; 688 ecmd->duplex = 0; 689 } 690 ecmd->port = PORT_TP; 691 ecmd->phy_address = 0; 692 ecmd->transceiver = XCVR_INTERNAL; 693 ecmd->autoneg = (et->etc->forcespeed == ET_AUTO) ? AUTONEG_ENABLE : AUTONEG_DISABLE; 694 ecmd->maxtxpkt = 0; 695 ecmd->maxrxpkt = 0; 696 break; 697 case ETHTOOL_SSET: 698 if (!capable(CAP_NET_ADMIN)) { 699 ret = -EPERM; 700 break; 701 } 702 else if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_HALF) 703 speed = ET_10HALF; 704 else if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_FULL) 705 speed = ET_10FULL; 706 else if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_HALF) 707 speed = ET_100HALF; 708 else if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_FULL) 709 speed = ET_100FULL; 710 else if (ecmd->autoneg == AUTONEG_ENABLE) 711 speed = ET_AUTO; 712 else { 713 ret = -EINVAL; 714 break; 715 } 716 ret = etc_ioctl(et->etc, ETCSPEED, &speed); 717 break; 718 case ETHTOOL_GDRVINFO: 719 info = (struct ethtool_drvinfo *)ecmd; 720 bzero(info, sizeof(struct ethtool_drvinfo)); 721 info->cmd = ETHTOOL_GDRVINFO; 722 sprintf(info->driver, "et%d", et->etc->unit); 723 strcpy(info->version, EPI_VERSION_STR); 724 break; 725 default: 726 ret = -EINVAL; 727 break; 728 } 729 730 ET_UNLOCK(et); 731 732 return (ret); 733} 734#endif /* SIOCETHTOOL */ 735 736static int 737et_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 738{ 739 et_info_t *et; 740 int error; 741 char *buf; 742 int size; 743 bool get, set; 744 745 et = ET_INFO(dev); 746 747 ET_TRACE(("et%d: et_ioctl: cmd 0x%x\n", et->etc->unit, cmd)); 748 749 switch (cmd) { 750#ifdef SIOCETHTOOL 751 case SIOCETHTOOL: 752 if (((struct ethtool_cmd *)ifr->ifr_data)->cmd == ETHTOOL_GDRVINFO) 753 size = sizeof(struct ethtool_drvinfo); 754 else 755 size = sizeof(struct ethtool_cmd); 756 get = TRUE; set = TRUE; 757 break; 758#endif /* SIOCETHTOOL */ 759 case SIOCGETCDUMP: 760 size = 4096; 761 get = TRUE; set = FALSE; 762 break; 763 case SIOCGETCPHYRD: 764 case SIOCGETCPHYRD2: 765 case SIOCGETCROBORD: 766 size = sizeof(int) * 2; 767 get = TRUE; set = TRUE; 768 break; 769 case SIOCSETCPHYWR: 770 case SIOCSETCPHYWR2: 771 case SIOCSETCROBOWR: 772 size = sizeof(int) * 2; 773 get = FALSE; set = TRUE; 774 break; 775 default: 776 size = sizeof(int); 777 get = FALSE; set = TRUE; 778 break; 779 } 780 781 if ((buf = MALLOC(et->osh, size)) == NULL) { 782 ET_ERROR(("et: et_ioctl: out of memory, malloced %d bytes\n", MALLOCED(et->osh))); 783 return (-ENOMEM); 784 } 785 786 if (set && copy_from_user(buf, ifr->ifr_data, size)) { 787 MFREE(et->osh, buf, size); 788 return (-EFAULT); 789 } 790 791 switch (cmd) { 792#ifdef SIOCETHTOOL 793 case SIOCETHTOOL: 794 error = et_ethtool(et, (struct ethtool_cmd*)buf); 795 break; 796#endif /* SIOCETHTOOL */ 797 default: 798 ET_LOCK(et); 799 error = etc_ioctl(et->etc, cmd - SIOCSETCUP, buf) ? -EINVAL : 0; 800 ET_UNLOCK(et); 801 break; 802 } 803 804 if (!error && get) 805 error = copy_to_user(ifr->ifr_data, buf, size); 806 807 MFREE(et->osh, buf, size); 808 809 return (error); 810} 811 812static struct net_device_stats* 813et_get_stats(struct net_device *dev) 814{ 815 et_info_t *et; 816 etc_info_t *etc; 817 struct net_device_stats *stats; 818 819 et = ET_INFO(dev); 820 821 ET_TRACE(("et%d: et_get_stats\n", et->etc->unit)); 822 823 ET_LOCK(et); 824 825 etc = et->etc; 826 stats = &et->stats; 827 bzero(stats, sizeof(struct net_device_stats)); 828 829 /* refresh stats */ 830 if (et->etc->up) 831 (*etc->chops->statsupd)(etc->ch); 832 833 /* SWAG */ 834 stats->rx_packets = etc->rxframe; 835 stats->tx_packets = etc->txframe; 836 stats->rx_bytes = etc->rxbyte; 837 stats->tx_bytes = etc->txbyte; 838 stats->rx_errors = etc->rxerror; 839 stats->tx_errors = etc->txerror; 840 stats->collisions = etc->mib.tx_total_cols; 841 842 stats->rx_length_errors = (etc->mib.rx_oversize_pkts + etc->mib.rx_undersize); 843 stats->rx_over_errors = etc->rxoflo; 844 stats->rx_crc_errors = etc->mib.rx_crc_errs; 845 stats->rx_frame_errors = etc->mib.rx_align_errs; 846 stats->rx_fifo_errors = etc->rxoflo; 847 stats->rx_missed_errors = etc->mib.rx_missed_pkts; 848 849 stats->tx_fifo_errors = etc->txuflo; 850 851 ET_UNLOCK(et); 852 853 return (stats); 854} 855 856static int 857et_set_mac_address(struct net_device *dev, void *addr) 858{ 859 et_info_t *et; 860 struct sockaddr *sa = (struct sockaddr *) addr; 861 862 et = ET_INFO(dev); 863 ET_TRACE(("et%d: et_set_mac_address\n", et->etc->unit)); 864 865 if (et->etc->up) 866 return -EBUSY; 867 868 bcopy(sa->sa_data, dev->dev_addr, ETHER_ADDR_LEN); 869 bcopy(dev->dev_addr, &et->etc->cur_etheraddr, ETHER_ADDR_LEN); 870 871 return 0; 872} 873 874static void 875et_set_multicast_list(struct net_device *dev) 876{ 877 et_info_t *et; 878 etc_info_t *etc; 879 struct dev_mc_list *mclist; 880 int i; 881 882 et = ET_INFO(dev); 883 etc = et->etc; 884 885 ET_TRACE(("et%d: et_set_multicast_list\n", etc->unit)); 886 887 ET_LOCK(et); 888 889 if (etc->up) { 890 etc->promisc = (dev->flags & IFF_PROMISC)? TRUE: FALSE; 891 etc->allmulti = (dev->flags & IFF_ALLMULTI)? TRUE: FALSE; 892 893 /* copy the list of multicasts into our private table */ 894 for (i = 0, mclist = dev->mc_list; mclist && (i < dev->mc_count); 895 i++, mclist = mclist->next) { 896 if (i >= MAXMULTILIST) { 897 etc->allmulti = TRUE; 898 i = 0; 899 break; 900 } 901 etc->multicast[i] = *((struct ether_addr*) mclist->dmi_addr); 902 } 903 etc->nmulticast = i; 904 905 et_init(et); 906 } 907 908 ET_UNLOCK(et); 909} 910 911static irqreturn_t 912et_isr(int irq, void *dev_id, struct pt_regs *ptregs) 913{ 914 et_info_t *et; 915 struct chops *chops; 916 void *ch; 917 uint events = 0; 918 919 et = (et_info_t*) dev_id; 920 chops = et->etc->chops; 921 ch = et->etc->ch; 922 923 /* guard against shared interrupts */ 924 if (!et->etc->up) 925 goto done; 926 927 /* get interrupt condition bits */ 928 events = (*chops->getintrevents)(ch, TRUE); 929 930 /* not for us */ 931 if (!(events & INTR_NEW)) 932 goto done; 933 934 ET_TRACE(("et%d: et_isr: events 0x%x\n", et->etc->unit, events)); 935 ET_LOG("et%d: et_isr: events 0x%x", et->etc->unit, events); 936 937 /* disable interrupts */ 938 (*chops->intrsoff)(ch); 939 940 /* save intstatus bits */ 941 ASSERT(et->events == 0); 942 et->events = events; 943 944 /* schedule dpc */ 945 ASSERT(et->resched == FALSE); 946 tasklet_schedule(&et->tasklet); 947 948done: 949 ET_LOG("et%d: et_isr ret", et->etc->unit, 0); 950 951 return IRQ_RETVAL(events & INTR_NEW); 952} 953 954static void 955et_dpc(ulong data) 956{ 957 et_info_t *et; 958 struct chops *chops; 959 void *ch; 960 struct sk_buff *skb; 961 void *p; 962 osl_t *osh; 963 964 et = (et_info_t*) data; 965 chops = et->etc->chops; 966 ch = et->etc->ch; 967 osh = et->etc->osh; 968 969 ET_TRACE(("et%d: et_dpc: events 0x%x\n", et->etc->unit, et->events)); 970 ET_LOG("et%d: et_dpc: events 0x%x", et->etc->unit, et->events); 971 972 ET_LOCK(et); 973 974 if (!et->etc->up) 975 goto done; 976 977 /* get interrupt condition bits again when dpc was rescheduled */ 978 if (et->resched) { 979 et->events = (*chops->getintrevents)(ch, FALSE); 980 et->resched = FALSE; 981 } 982 983 if (et->events & INTR_RX) { 984 uint processed = 0; 985 while ((p = (*chops->rx)(ch))) { 986 skb = PKTTONATIVE(osh, p); 987 et_sendup(et, skb); 988 /* more frames, need to reschedule et_dpc() */ 989 if (++processed >= RXBND) { 990 et->resched = TRUE; 991 break; 992 } 993 } 994 995 /* post more rx bufs */ 996 (*chops->rxfill)(ch); 997 } 998 999 if (et->events & INTR_TX) 1000 (*chops->txreclaim)(ch, FALSE); 1001 1002 /* handle error condtions, if reset required leave interrupts off! */ 1003 if (et->events & INTR_ERROR) 1004 if ((*chops->errors)(ch)) 1005 et_init(et); 1006 1007 /* run the tx queue */ 1008 if (skb_queue_len(&et->txq) > 0) 1009 et_sendnext(et); 1010 1011 /* clear this before re-enabling interrupts */ 1012 et->events = 0; 1013 1014 /* something may bring the driver down */ 1015 if (!et->etc->up) { 1016 et->resched = FALSE; 1017 goto done; 1018 } 1019 1020 /* there may be frames left, reschedule et_dpc() */ 1021 if (et->resched) 1022 tasklet_schedule(&et->tasklet); 1023 /* re-enable interrupts */ 1024 else 1025 (*chops->intrson)(ch); 1026 1027done: 1028 ET_UNLOCK(et); 1029 1030 ET_LOG("et%d: et_dpc ret", et->etc->unit, 0); 1031} 1032 1033void 1034et_sendup(et_info_t *et, struct sk_buff *skb) 1035{ 1036 etc_info_t *etc; 1037 bcmenetrxh_t *rxh; 1038 uint16 flags; 1039 uchar eabuf[32]; 1040 1041 etc = et->etc; 1042 1043 /* packet buffer starts with rxhdr */ 1044 rxh = (bcmenetrxh_t*) skb->data; 1045 1046 /* strip off rxhdr */ 1047 skb_pull(skb, HWRXOFF); 1048 1049 ET_TRACE(("et%d: et_sendup: %d bytes\n", et->etc->unit, skb->len)); 1050 ET_LOG("et%d: et_sendup: len %d", et->etc->unit, skb->len); 1051 1052 etc->rxframe++; 1053 etc->rxbyte += skb->len; 1054 1055 /* eh should now be aligned 2-mod-4 */ 1056 ASSERT(((uint)skb->data & 3) == 2); 1057 1058 /* strip off crc32 */ 1059 skb_trim(skb, skb->len - ETHER_CRC_LEN); 1060 1061 ET_PRHDR("rx", (struct ether_header*) skb->data, skb->len, etc->unit); 1062 ET_PRPKT("rxpkt", skb->data, skb->len, etc->unit); 1063 1064 /* check for reported frame errors */ 1065 flags = ltoh16(rxh->flags); 1066 if (flags & (RXF_NO | RXF_RXER | RXF_CRC | RXF_OV)) 1067 goto err; 1068 1069 /* Extract priority from payload and store it out-of-band in skb->priority */ 1070 if (et->etc->qos) 1071 pktsetprio(skb, TRUE); 1072 1073 skb->dev = et->dev; 1074 skb->protocol = eth_type_trans(skb, et->dev); 1075 1076 /* send it up */ 1077 netif_rx(skb); 1078 1079 ET_LOG("et%d: et_sendup ret", et->etc->unit, 0); 1080 1081 return; 1082 1083err: 1084 bcm_ether_ntoa((struct ether_addr*)((struct ether_header*)skb->data)->ether_shost, eabuf); 1085 if (flags & RXF_NO) { 1086 ET_ERROR(("et%d: rx: crc error (odd nibbles) from %s\n", etc->unit, eabuf)); 1087 } 1088 if (flags & RXF_RXER) { 1089 ET_ERROR(("et%d: rx: symbol error from %s\n", etc->unit, eabuf)); 1090 } 1091 if ((flags & RXF_CRC) == RXF_CRC) { 1092 ET_ERROR(("et%d: rx: crc error from %s\n", etc->unit, eabuf)); 1093 } 1094 if (flags & RXF_OV) { 1095 ET_ERROR(("et%d: rx: fifo overflow\n", etc->unit)); 1096 } 1097 1098 dev_kfree_skb_any(skb); 1099 1100 return; 1101} 1102 1103void 1104et_dump(et_info_t *et, struct bcmstrbuf *b) 1105{ 1106 bcm_bprintf(b, "et%d: %s %s version %s\n", et->etc->unit, 1107 __DATE__, __TIME__, EPI_VERSION_STR); 1108 1109} 1110 1111 1112void 1113et_link_up(et_info_t *et) 1114{ 1115 ET_ERROR(("et%d: link up (%d%s)\n", 1116 et->etc->unit, et->etc->speed, (et->etc->duplex? "FD" : "HD"))); 1117} 1118 1119void 1120et_link_down(et_info_t *et) 1121{ 1122 ET_ERROR(("et%d: link down\n", et->etc->unit)); 1123} 1124 1125/* 1126 * 47XX-specific shared mdc/mdio contortion: 1127 * Find the et associated with the same chip as <et> 1128 * and coreunit matching <coreunit>. 1129 */ 1130void* 1131et_phyfind(et_info_t *et, uint coreunit) 1132{ 1133 et_info_t *tmp; 1134 uint bus, slot; 1135 1136 bus = et->pdev->bus->number; 1137 slot = PCI_SLOT(et->pdev->devfn); 1138 1139 /* walk the list et's */ 1140 for (tmp = et_list; tmp; tmp = tmp->next) { 1141 if (et->etc == NULL) 1142 continue; 1143 if (tmp->pdev == NULL) 1144 continue; 1145 if (tmp->pdev->bus->number != bus) 1146 continue; 1147 if (tmp->etc->nicmode) 1148 if (PCI_SLOT(tmp->pdev->devfn) != slot) 1149 continue; 1150 if (tmp->etc->coreunit != coreunit) 1151 continue; 1152 break; 1153 } 1154 return (tmp); 1155} 1156 1157/* shared phy read entry point */ 1158uint16 1159et_phyrd(et_info_t *et, uint phyaddr, uint reg) 1160{ 1161 uint16 val; 1162 1163 ET_LOCK(et); 1164 val = et->etc->chops->phyrd(et->etc->ch, phyaddr, reg); 1165 ET_UNLOCK(et); 1166 1167 return (val); 1168} 1169 1170/* shared phy write entry point */ 1171void 1172et_phywr(et_info_t *et, uint phyaddr, uint reg, uint16 val) 1173{ 1174 ET_LOCK(et); 1175 et->etc->chops->phywr(et->etc->ch, phyaddr, reg, val); 1176 ET_UNLOCK(et); 1177} 1178