SSID: Respect ASCII character Label.
[tomato.git] / release / src / et / sys / et_linux.c
blobffc3491541574c289a321072cd201f61a299e88b
1 /*
2 * Linux device driver for
3 * Broadcom BCM47XX 10/100 Mbps Ethernet Controller
5 * Copyright 2006, 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.
13 * $Id: et_linux.c,v 1.1.1.1 2007/03/20 12:22:00 roly Exp $
16 #define __UNDEF_NO_VERSION__
18 #include <typedefs.h>
20 #include <linux/module.h>
21 #include <linuxver.h>
22 #include <bcmdefs.h>
23 #include <osl.h>
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/mii.h>
40 #include <linux/ip.h>
42 #include <asm/system.h>
43 #include <asm/io.h>
44 #include <asm/irq.h>
45 #include <asm/pgtable.h>
46 #include <asm/uaccess.h>
48 #include <epivers.h>
49 #include <bcmendian.h>
50 #include <bcmdefs.h>
51 #include <proto/ethernet.h>
52 #include <proto/vlan.h>
53 #include <bcmdevs.h>
54 #include <bcmenetmib.h>
55 #include <bcmenetrxh.h>
56 #include <bcmenetphy.h>
57 #include <etioctl.h>
58 #include <bcmutils.h>
59 #include <pcicfg.h>
60 #include <et_dbg.h>
61 #include <etc.h>
63 typedef struct et_info {
64 etc_info_t *etc; /* pointer to common os-independent data */
65 struct net_device *dev; /* backpoint to device */
66 struct pci_dev *pdev; /* backpoint to pci_dev */
67 void *osh; /* pointer to os handle */
68 spinlock_t lock; /* per-device perimeter lock */
69 struct sk_buff_head txq; /* send queue */
70 void *regsva; /* opaque chip registers virtual address */
71 struct timer_list timer; /* one second watchdog timer */
72 struct net_device_stats stats; /* stat counter reporting structure */
73 int events; /* bit channel between isr and dpc */
74 struct tasklet_struct tasklet; /* dpc tasklet */
75 struct et_info *next; /* pointer to next et_info_t in chain */
76 bool resched; /* dpc was rescheduled */
77 } et_info_t;
79 static int et_found = 0;
80 static et_info_t *et_list = NULL;
82 /* defines */
83 #define DATAHIWAT 50 /* data msg txq hiwat mark */
85 #define ET_INFO(dev) (et_info_t*)((dev)->priv)
87 #define ET_LOCK(et) spin_lock_bh(&(et)->lock)
88 #define ET_UNLOCK(et) spin_unlock_bh(&(et)->lock)
90 #define INT_LOCK(flags) local_irq_save(flags)
91 #define INT_UNLOCK(flags) local_irq_restore(flags)
93 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 5)
94 #error Linux version must be newer than 2.4.5
95 #endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 4, 5) */
97 /* prototypes called by etc.c */
98 void et_init(et_info_t *et);
99 void et_reset(et_info_t *et);
100 void et_link_up(et_info_t *et);
101 void et_link_down(et_info_t *et);
102 void et_up(et_info_t *et);
103 void et_down(et_info_t *et, int reset);
104 void et_dump(et_info_t *et, struct bcmstrbuf *b);
106 /* local prototypes */
107 static void et_free(et_info_t *et);
108 static int et_open(struct net_device *dev);
109 static int et_close(struct net_device *dev);
110 static int et_start(struct sk_buff *skb, struct net_device *dev);
111 static void et_sendnext(et_info_t *et);
112 static struct net_device_stats *et_get_stats(struct net_device *dev);
113 static int et_set_mac_address(struct net_device *dev, void *addr);
114 static void et_set_multicast_list(struct net_device *dev);
115 static void et_watchdog(ulong data);
116 static int et_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
117 static irqreturn_t et_isr(int irq, void *dev_id, struct pt_regs *ptregs);
118 static void et_dpc(ulong data);
119 static void et_sendup(et_info_t *et, struct sk_buff *skb);
121 /* recognized PCI IDs */
122 static struct pci_device_id et_id_table[] __devinitdata = {
123 { vendor: PCI_ANY_ID,
124 device: PCI_ANY_ID,
125 subvendor: PCI_ANY_ID,
126 subdevice: PCI_ANY_ID,
127 class: PCI_CLASS_NETWORK_ETHERNET << 8,
128 class_mask: 0xffff00,
129 driver_data: 0,
131 { 0, }
133 MODULE_DEVICE_TABLE(pci, et_id_table);
136 static int __devinit
137 et_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
139 struct net_device *dev;
140 et_info_t *et;
141 osl_t *osh;
142 char name[128];
143 int unit = et_found;
146 ET_TRACE(("et%d: et_probe: bus %d slot %d func %d irq %d\n", unit,
147 pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq));
149 if (!etc_chipmatch(pdev->vendor, pdev->device))
150 return -ENODEV;
152 osh = osl_attach(pdev, PCI_BUS, FALSE);
153 ASSERT(osh);
155 pci_set_master(pdev);
156 pci_enable_device(pdev);
158 if (!(dev = (struct net_device *) MALLOC(osh, sizeof(struct net_device)))) {
159 ET_ERROR(("et%d: et_probe: out of memory, malloced %d bytes\n", unit,
160 MALLOCED(osh)));
161 osl_detach(osh);
162 return -ENOMEM;
164 bzero(dev, sizeof(struct net_device));
166 if (!init_etherdev(dev, 0)) {
167 ET_ERROR(("et%d: et_probe: init_etherdev() failed\n", unit));
168 MFREE(osh, dev, sizeof(struct net_device));
169 osl_detach(osh);
170 return -ENOMEM;
173 /* allocate private info */
174 if ((et = (et_info_t*) MALLOC(osh, sizeof(et_info_t))) == NULL) {
175 ET_ERROR(("et%d: et_probe: out of memory, malloced %d bytes\n", unit,
176 MALLOCED(osh)));
177 MFREE(osh, dev, sizeof(et_info_t));
178 osl_detach(osh);
179 return -ENOMEM;
181 bzero(et, sizeof(et_info_t));
182 dev->priv = (void*) et;
183 et->dev = dev;
184 et->pdev = pdev;
185 et->osh = osh;
186 pci_set_drvdata(pdev, et);
188 /* map chip registers (47xx: and sprom) */
189 dev->base_addr = pci_resource_start(pdev, 0);
190 if ((et->regsva = ioremap_nocache(dev->base_addr, PCI_BAR0_WINSZ)) == NULL) {
191 ET_ERROR(("et%d: ioremap() failed\n", unit));
192 goto fail;
195 spin_lock_init(&et->lock);
197 skb_queue_head_init(&et->txq);
199 /* common load-time initialization */
200 if ((et->etc = etc_attach((void*)et, pdev->vendor, pdev->device, unit, osh, et->regsva)) ==
201 NULL) {
202 ET_ERROR(("et%d: etc_attach() failed\n", unit));
203 goto fail;
206 bcopy(&et->etc->cur_etheraddr, dev->dev_addr, ETHER_ADDR_LEN);
208 /* init 1 second watchdog timer */
209 init_timer(&et->timer);
210 et->timer.data = (ulong)dev;
211 et->timer.function = et_watchdog;
213 /* setup the bottom half handler */
214 tasklet_init(&et->tasklet, et_dpc, (ulong)et);
216 /* register our interrupt handler */
217 if (request_irq(pdev->irq, et_isr, SA_SHIRQ, dev->name, et)) {
218 ET_ERROR(("et%d: request_irq() failed\n", unit));
219 goto fail;
221 dev->irq = pdev->irq;
223 /* add us to the global linked list */
224 et->next = et_list;
225 et_list = et;
227 /* print hello string */
228 (*et->etc->chops->longname)(et->etc->ch, name, sizeof(name));
229 printf("%s: %s %s\n", dev->name, name, EPI_VERSION_STR);
231 /* lastly, enable our entry points */
232 dev->open = et_open;
233 dev->stop = et_close;
234 dev->hard_start_xmit = et_start;
235 dev->get_stats = et_get_stats;
236 dev->set_mac_address = et_set_mac_address;
237 dev->set_multicast_list = et_set_multicast_list;
238 dev->do_ioctl = et_ioctl;
240 if (register_netdev(dev)) {
241 ET_ERROR(("et%d: register_netdev() failed\n", unit));
242 goto fail;
245 et_found++;
246 return (0);
248 fail:
249 et_free(et);
250 return (-ENODEV);
253 static int
254 et_suspend(struct pci_dev *pdev, u32 state)
256 et_info_t *et;
258 if ((et = (et_info_t *) pci_get_drvdata(pdev))) {
259 netif_device_detach(et->dev);
260 ET_LOCK(et);
261 et_down(et, 1);
262 ET_UNLOCK(et);
265 return 0;
268 static int
269 et_resume(struct pci_dev *pdev)
271 et_info_t *et;
273 if ((et = (et_info_t *) pci_get_drvdata(pdev))) {
274 ET_LOCK(et);
275 et_up(et);
276 ET_UNLOCK(et);
277 netif_device_attach(et->dev);
280 return 0;
283 /* Compatibility routines */
284 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6)
285 static void
286 _et_suspend(struct pci_dev *pdev)
288 et_suspend(pdev, 0);
291 static void
292 _et_resume(struct pci_dev *pdev)
294 et_resume(pdev);
296 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) */
298 static void __devexit
299 et_remove(struct pci_dev *pdev)
301 et_info_t *et;
303 if (!etc_chipmatch(pdev->vendor, pdev->device))
304 return;
306 et_suspend(pdev, 0);
308 if ((et = (et_info_t *) pci_get_drvdata(pdev))) {
309 et_free(et);
310 pci_set_drvdata(pdev, NULL);
314 static struct pci_driver et_pci_driver = {
315 name: "et",
316 probe: et_probe,
317 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6)
318 suspend: _et_suspend,
319 resume: _et_resume,
320 #else
321 suspend: et_suspend,
322 resume: et_resume,
323 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 6) */
324 remove: __devexit_p(et_remove),
325 id_table: et_id_table,
328 static int __init
329 et_module_init(void)
331 return pci_module_init(&et_pci_driver);
334 static void __exit
335 et_module_exit(void)
337 pci_unregister_driver(&et_pci_driver);
340 module_init(et_module_init);
341 module_exit(et_module_exit);
343 static void
344 et_free(et_info_t *et)
346 et_info_t **prev;
347 osl_t *osh;
349 if (et == NULL)
350 return;
352 ET_TRACE(("et: et_free\n"));
354 if (et->dev && et->dev->irq)
355 free_irq(et->dev->irq, et);
357 if (et->dev) {
358 unregister_netdev(et->dev);
359 MFREE(et->osh, et->dev, sizeof(struct net_device));
360 et->dev = NULL;
363 /* free common resources */
364 if (et->etc) {
365 etc_detach(et->etc);
366 et->etc = NULL;
370 * unregister_netdev() calls get_stats() which may read chip registers
371 * so we cannot unmap the chip registers until after calling unregister_netdev() .
373 if (et->regsva) {
374 iounmap((void*)et->regsva);
375 et->regsva = NULL;
378 /* remove us from the global linked list */
379 for (prev = &et_list; *prev; prev = &(*prev)->next)
380 if (*prev == et) {
381 *prev = et->next;
382 break;
385 osh = et->osh;
386 MFREE(et->osh, et, sizeof(et_info_t));
388 ASSERT(MALLOCED(osh) == 0);
390 osl_detach(osh);
393 static int
394 et_open(struct net_device *dev)
396 et_info_t *et;
398 et = ET_INFO(dev);
400 ET_TRACE(("et%d: et_open\n", et->etc->unit));
402 et->etc->promisc = (dev->flags & IFF_PROMISC)? TRUE: FALSE;
404 ET_LOCK(et);
405 et_up(et);
406 ET_UNLOCK(et);
408 OLD_MOD_INC_USE_COUNT;
410 return (0);
413 static int
414 et_close(struct net_device *dev)
416 et_info_t *et;
418 et = ET_INFO(dev);
420 ET_TRACE(("et%d: et_close\n", et->etc->unit));
422 et->etc->promisc = FALSE;
424 ET_LOCK(et);
425 et_down(et, 1);
426 ET_UNLOCK(et);
428 OLD_MOD_DEC_USE_COUNT;
430 return (0);
434 * Yeah, queueing the packets on a tx queue instead of throwing them
435 * directly into the descriptor ring in the case of dma is kinda lame,
436 * but this results in a unified transmit path for both dma and pio
437 * and localizes/simplifies the netif_*_queue semantics, too.
439 static int
440 et_start(struct sk_buff *skb, struct net_device *dev)
442 et_info_t *et;
444 et = ET_INFO(dev);
446 ET_TRACE(("et%d: et_start: len %d\n", et->etc->unit, skb->len));
447 ET_LOG("et%d: et_start: len %d", et->etc->unit, skb->len);
449 ET_LOCK(et);
451 /* put it on the tx queue and call sendnext */
452 skb_queue_tail(&et->txq, skb);
453 et_sendnext(et);
455 ET_UNLOCK(et);
457 ET_LOG("et%d: et_start ret\n", et->etc->unit, 0);
459 return (0);
462 static void
463 et_sendnext(et_info_t *et)
465 etc_info_t *etc;
466 struct sk_buff *skb;
467 void *p;
469 etc = et->etc;
471 ET_TRACE(("et%d: et_sendnext\n", etc->unit));
472 ET_LOG("et%d: et_sendnext", etc->unit, 0);
474 /* dequeue and send each packet */
475 #ifdef DMA
476 while (*etc->txavail > 0) {
477 #else
478 while (etc->pioactive == NULL) {
479 #endif /* DMA */
480 if ((skb = skb_dequeue(&et->txq)) == NULL)
481 break;
483 ET_PRHDR("tx", (struct ether_header*) skb->data, skb->len, etc->unit);
484 ET_PRPKT("txpkt", skb->data, skb->len, etc->unit);
486 /* Convert the packet. */
487 if ((p = PKTFRMNATIVE(et->osh, skb)) == NULL) {
488 dev_kfree_skb_any(skb);
489 return;
492 (*etc->chops->tx)(etc->ch, p);
494 etc->txframe++;
495 etc->txbyte += skb->len;
498 /* stop the queue whenever txq fills */
499 if ((skb_queue_len(&et->txq) > DATAHIWAT) && !netif_queue_stopped(et->dev))
500 netif_stop_queue(et->dev);
501 else if (netif_queue_stopped(et->dev) && (skb_queue_len(&et->txq) < (DATAHIWAT/2))) {
502 netif_wake_queue(et->dev);
506 void
507 et_init(et_info_t *et)
509 ET_TRACE(("et%d: et_init\n", et->etc->unit));
510 ET_LOG("et%d: et_init", et->etc->unit, 0);
512 et_reset(et);
514 etc_init(et->etc);
518 void
519 et_reset(et_info_t *et)
521 ET_TRACE(("et%d: et_reset\n", et->etc->unit));
523 etc_reset(et->etc);
525 /* zap any pending dpc interrupt bits */
526 et->events = 0;
528 /* dpc will not be rescheduled */
529 et->resched = 0;
532 void
533 et_up(et_info_t *et)
535 etc_info_t *etc;
537 etc = et->etc;
539 if (etc->up)
540 return;
542 ET_TRACE(("et%d: et_up\n", etc->unit));
544 etc_up(etc);
546 /* schedule one second watchdog timer */
547 et->timer.expires = jiffies + HZ;
548 add_timer(&et->timer);
550 netif_start_queue(et->dev);
553 void
554 et_down(et_info_t *et, int reset)
556 etc_info_t *etc;
557 struct sk_buff *skb;
559 etc = et->etc;
561 ET_TRACE(("et%d: et_down\n", etc->unit));
563 netif_down(et->dev);
564 netif_stop_queue(et->dev);
566 /* stop watchdog timer */
567 del_timer(&et->timer);
569 etc_down(etc, reset);
571 /* flush the txq */
572 while ((skb = skb_dequeue(&et->txq)))
573 dev_kfree_skb_any(skb);
575 /* kill dpc */
576 ET_UNLOCK(et);
577 tasklet_kill(&et->tasklet);
578 ET_LOCK(et);
582 * These are interrupt on/off entry points. Disable interrupts
583 * during interrupt state transition.
585 void
586 et_intrson(et_info_t *et)
588 unsigned long flags;
589 INT_LOCK(flags);
590 (*et->etc->chops->intrson)(et->etc->ch);
591 INT_UNLOCK(flags);
594 static void
595 et_watchdog(ulong data)
597 et_info_t *et;
599 et = ET_INFO((struct net_device*)data);
601 ET_LOCK(et);
603 etc_watchdog(et->etc);
605 /* reschedule one second watchdog timer */
606 et->timer.expires = jiffies + HZ;
607 add_timer(&et->timer);
609 ET_UNLOCK(et);
613 #ifdef SIOCETHTOOL
614 static int
615 et_ethtool(et_info_t *et, struct ethtool_cmd *ecmd)
617 int ret = 0;
618 int speed;
619 struct ethtool_drvinfo *info;
621 ET_LOCK(et);
623 switch (ecmd->cmd) {
624 case ETHTOOL_GSET:
625 ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
626 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
627 SUPPORTED_Autoneg);
628 ecmd->advertising = ADVERTISED_TP;
629 ecmd->advertising |= (et->etc->advertise & ADV_10HALF) ?
630 ADVERTISED_10baseT_Half : 0;
631 ecmd->advertising |= (et->etc->advertise & ADV_10FULL) ?
632 ADVERTISED_10baseT_Full : 0;
633 ecmd->advertising |= (et->etc->advertise & ADV_100HALF) ?
634 ADVERTISED_100baseT_Half : 0;
635 ecmd->advertising |= (et->etc->advertise & ADV_100FULL) ?
636 ADVERTISED_100baseT_Full : 0;
637 if (et->etc->linkstate) {
638 ecmd->speed = (et->etc->speed == 100) ? SPEED_100 : SPEED_10;
639 ecmd->duplex = (et->etc->duplex == 1) ? DUPLEX_FULL : DUPLEX_HALF;
640 } else {
641 ecmd->speed = 0;
642 ecmd->duplex = 0;
644 ecmd->port = PORT_TP;
645 ecmd->phy_address = 0;
646 ecmd->transceiver = XCVR_INTERNAL;
647 ecmd->autoneg = (et->etc->forcespeed == ET_AUTO) ? AUTONEG_ENABLE : AUTONEG_DISABLE;
648 ecmd->maxtxpkt = 0;
649 ecmd->maxrxpkt = 0;
650 break;
651 case ETHTOOL_SSET:
652 if (!capable(CAP_NET_ADMIN)) {
653 ret = -EPERM;
654 break;
656 else if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_HALF)
657 speed = ET_10HALF;
658 else if (ecmd->speed == SPEED_10 && ecmd->duplex == DUPLEX_FULL)
659 speed = ET_10FULL;
660 else if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_HALF)
661 speed = ET_100HALF;
662 else if (ecmd->speed == SPEED_100 && ecmd->duplex == DUPLEX_FULL)
663 speed = ET_100FULL;
664 else if (ecmd->autoneg == AUTONEG_ENABLE)
665 speed = ET_AUTO;
666 else {
667 ret = -EINVAL;
668 break;
670 ret = etc_ioctl(et->etc, ETCSPEED, &speed);
671 break;
672 case ETHTOOL_GDRVINFO:
673 info = (struct ethtool_drvinfo *)ecmd;
674 bzero(info, sizeof(struct ethtool_drvinfo));
675 info->cmd = ETHTOOL_GDRVINFO;
676 sprintf(info->driver, "et%d", et->etc->unit);
677 strcpy(info->version, EPI_VERSION_STR);
678 break;
679 default:
680 ret = -EINVAL;
681 break;
684 ET_UNLOCK(et);
686 return (ret);
688 #endif /* SIOCETHTOOL */
690 static int
691 et_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
693 et_info_t *et;
694 int error;
695 char *buf;
696 int size;
697 bool get, set;
698 struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;
700 et = ET_INFO(dev);
702 ET_TRACE(("et%d: et_ioctl: cmd 0x%x\n", et->etc->unit, cmd));
704 switch (cmd) {
705 #ifdef SIOCETHTOOL
706 case SIOCETHTOOL:
707 if (((struct ethtool_cmd *)ifr->ifr_data)->cmd == ETHTOOL_GDRVINFO)
708 size = sizeof(struct ethtool_drvinfo);
709 else
710 size = sizeof(struct ethtool_cmd);
711 get = TRUE; set = TRUE;
712 break;
713 #endif /* SIOCETHTOOL */
714 case SIOCGETCDUMP:
715 size = 4096;
716 get = TRUE; set = FALSE;
717 break;
718 case SIOCGETCPHYRD:
719 case SIOCGETCPHYRD2:
720 case SIOCGETCROBORD:
721 size = sizeof(int) * 2;
722 get = TRUE; set = TRUE;
723 break;
724 case SIOCSETCPHYWR:
725 case SIOCSETCPHYWR2:
726 case SIOCSETCROBOWR:
727 size = sizeof(int) * 2;
728 get = FALSE; set = TRUE;
729 break;
730 case SIOCGMIIPHY:
731 data->phy_id = et->etc->phyaddr;
732 case SIOCGMIIREG:
733 data->val_out = (*et->etc->chops->phyrd)(et->etc->ch, data->phy_id, data->reg_num);
734 return 0;
735 case SIOCSMIIREG:
736 (*et->etc->chops->phywr)(et->etc->ch, data->phy_id, data->reg_num, data->val_in);
737 return 0;
738 default:
739 size = sizeof(int);
740 get = FALSE; set = TRUE;
741 break;
744 if ((buf = MALLOC(et->osh, size)) == NULL) {
745 ET_ERROR(("et: et_ioctl: out of memory, malloced %d bytes\n", MALLOCED(et->osh)));
746 return (-ENOMEM);
749 if (set && copy_from_user(buf, ifr->ifr_data, size)) {
750 MFREE(et->osh, buf, size);
751 return (-EFAULT);
754 switch (cmd) {
755 #ifdef SIOCETHTOOL
756 case SIOCETHTOOL:
757 error = et_ethtool(et, (struct ethtool_cmd*)buf);
758 break;
759 #endif /* SIOCETHTOOL */
760 default:
761 ET_LOCK(et);
762 error = etc_ioctl(et->etc, cmd - SIOCSETCUP, buf) ? -EINVAL : 0;
763 ET_UNLOCK(et);
764 break;
767 if (!error && get)
768 error = copy_to_user(ifr->ifr_data, buf, size);
770 MFREE(et->osh, buf, size);
772 return (error);
775 static struct net_device_stats*
776 et_get_stats(struct net_device *dev)
778 et_info_t *et;
779 etc_info_t *etc;
780 struct net_device_stats *stats;
782 et = ET_INFO(dev);
784 ET_TRACE(("et%d: et_get_stats\n", et->etc->unit));
786 ET_LOCK(et);
788 etc = et->etc;
789 stats = &et->stats;
790 bzero(stats, sizeof(struct net_device_stats));
792 /* refresh stats */
793 if (et->etc->up)
794 (*etc->chops->statsupd)(etc->ch);
796 /* SWAG */
797 stats->rx_packets = etc->rxframe;
798 stats->tx_packets = etc->txframe;
799 stats->rx_bytes = etc->rxbyte;
800 stats->tx_bytes = etc->txbyte;
801 stats->rx_errors = etc->rxerror;
802 stats->tx_errors = etc->txerror;
803 stats->collisions = etc->mib.tx_total_cols;
805 stats->rx_length_errors = (etc->mib.rx_oversize_pkts + etc->mib.rx_undersize);
806 stats->rx_over_errors = etc->rxoflo;
807 stats->rx_crc_errors = etc->mib.rx_crc_errs;
808 stats->rx_frame_errors = etc->mib.rx_align_errs;
809 stats->rx_fifo_errors = etc->rxoflo;
810 stats->rx_missed_errors = etc->mib.rx_missed_pkts;
812 stats->tx_fifo_errors = etc->txuflo;
814 ET_UNLOCK(et);
816 return (stats);
819 static int
820 et_set_mac_address(struct net_device *dev, void *addr)
822 et_info_t *et;
823 struct sockaddr *sa = (struct sockaddr *) addr;
825 et = ET_INFO(dev);
826 ET_TRACE(("et%d: et_set_mac_address\n", et->etc->unit));
828 if (et->etc->up)
829 return -EBUSY;
831 bcopy(sa->sa_data, dev->dev_addr, ETHER_ADDR_LEN);
832 bcopy(dev->dev_addr, &et->etc->cur_etheraddr, ETHER_ADDR_LEN);
834 return 0;
837 static void
838 et_set_multicast_list(struct net_device *dev)
840 et_info_t *et;
841 etc_info_t *etc;
842 struct dev_mc_list *mclist;
843 int i;
845 et = ET_INFO(dev);
846 etc = et->etc;
848 ET_TRACE(("et%d: et_set_multicast_list\n", etc->unit));
850 ET_LOCK(et);
852 if (etc->up) {
853 etc->promisc = (dev->flags & IFF_PROMISC)? TRUE: FALSE;
854 etc->allmulti = (dev->flags & IFF_ALLMULTI)? TRUE: FALSE;
856 /* copy the list of multicasts into our private table */
857 for (i = 0, mclist = dev->mc_list; mclist && (i < dev->mc_count);
858 i++, mclist = mclist->next) {
859 if (i >= MAXMULTILIST) {
860 etc->allmulti = TRUE;
861 i = 0;
862 break;
864 etc->multicast[i] = *((struct ether_addr*) mclist->dmi_addr);
866 etc->nmulticast = i;
868 et_init(et);
871 ET_UNLOCK(et);
874 static irqreturn_t
875 et_isr(int irq, void *dev_id, struct pt_regs *ptregs)
877 et_info_t *et;
878 struct chops *chops;
879 void *ch;
880 uint events = 0;
882 et = (et_info_t*) dev_id;
883 chops = et->etc->chops;
884 ch = et->etc->ch;
886 /* guard against shared interrupts */
887 if (!et->etc->up)
888 goto done;
890 /* get interrupt condition bits */
891 events = (*chops->getintrevents)(ch, TRUE);
893 /* not for us */
894 if (!(events & INTR_NEW))
895 goto done;
897 ET_TRACE(("et%d: et_isr: events 0x%x\n", et->etc->unit, events));
898 ET_LOG("et%d: et_isr: events 0x%x", et->etc->unit, events);
900 /* disable interrupts */
901 (*chops->intrsoff)(ch);
903 /* save intstatus bits */
904 ASSERT(et->events == 0);
905 et->events = events;
907 /* schedule dpc */
908 ASSERT(et->resched == FALSE);
909 tasklet_schedule(&et->tasklet);
911 done:
912 ET_LOG("et%d: et_isr ret", et->etc->unit, 0);
914 return IRQ_RETVAL(events & INTR_NEW);
917 static void
918 et_dpc(ulong data)
920 et_info_t *et;
921 struct chops *chops;
922 void *ch;
923 struct sk_buff *skb;
924 void *p;
925 osl_t *osh;
927 et = (et_info_t*) data;
928 chops = et->etc->chops;
929 ch = et->etc->ch;
930 osh = et->etc->osh;
932 ET_TRACE(("et%d: et_dpc: events 0x%x\n", et->etc->unit, et->events));
933 ET_LOG("et%d: et_dpc: events 0x%x", et->etc->unit, et->events);
935 ET_LOCK(et);
937 if (!et->etc->up)
938 goto done;
940 /* get interrupt condition bits again when dpc was rescheduled */
941 if (et->resched) {
942 et->events = (*chops->getintrevents)(ch, FALSE);
943 et->resched = FALSE;
946 if (et->events & INTR_RX) {
947 uint processed = 0;
948 while ((p = (*chops->rx)(ch))) {
949 skb = PKTTONATIVE(osh, p);
950 et_sendup(et, skb);
951 /* more frames, need to reschedule et_dpc() */
952 if (++processed >= RXBND) {
953 et->resched = TRUE;
954 break;
958 /* post more rx bufs */
959 (*chops->rxfill)(ch);
962 if (et->events & INTR_TX)
963 (*chops->txreclaim)(ch, FALSE);
965 if (et->events & INTR_ERROR)
966 if ((*chops->errors)(ch))
967 et_init(et);
969 /* run the tx queue */
970 if (skb_queue_len(&et->txq) > 0)
971 et_sendnext(et);
973 /* clear this before re-enabling interrupts */
974 et->events = 0;
976 /* something may bring the driver down */
977 if (!et->etc->up) {
978 et->resched = FALSE;
979 goto done;
982 /* there may be frames left, reschedule et_dpc() */
983 if (et->resched)
984 tasklet_schedule(&et->tasklet);
985 /* re-enable interrupts */
986 else
987 (*chops->intrson)(ch);
989 done:
990 ET_UNLOCK(et);
992 ET_LOG("et%d: et_dpc ret", et->etc->unit, 0);
995 void
996 et_sendup(et_info_t *et, struct sk_buff *skb)
998 etc_info_t *etc;
999 bcmenetrxh_t *rxh;
1000 uint16 flags;
1001 uchar eabuf[32];
1003 etc = et->etc;
1005 /* packet buffer starts with rxhdr */
1006 rxh = (bcmenetrxh_t*) skb->data;
1008 /* strip off rxhdr */
1009 skb_pull(skb, HWRXOFF);
1011 ET_TRACE(("et%d: et_sendup: %d bytes\n", et->etc->unit, skb->len));
1012 ET_LOG("et%d: et_sendup: len %d", et->etc->unit, skb->len);
1014 etc->rxframe++;
1015 etc->rxbyte += skb->len;
1017 /* eh should now be aligned 2-mod-4 */
1018 ASSERT(((uint)skb->data & 3) == 2);
1020 /* strip off crc32 */
1021 skb_trim(skb, skb->len - ETHER_CRC_LEN);
1023 ET_PRHDR("rx", (struct ether_header*) skb->data, skb->len, etc->unit);
1024 ET_PRPKT("rxpkt", skb->data, skb->len, etc->unit);
1026 /* check for reported frame errors */
1027 flags = ltoh16(rxh->flags);
1028 if (flags & (RXF_NO | RXF_RXER | RXF_CRC | RXF_OV))
1029 goto err;
1031 /* check for invalid data on the unit 1, workaround hw bug */
1032 if (etc->chip == BCM4710_CHIP_ID && etc->unit == 1)
1034 uint8 *ether_dhost = ((struct ether_header*)skb->data)->ether_dhost;
1035 if ( !(flags & (RXF_MULT | RXF_BRDCAST)) != !ETHER_ISMULTI(ether_dhost) ||
1036 !(flags & RXF_BRDCAST) != !ETHER_ISBCAST(ether_dhost) ||
1037 ((flags & (RXF_MULT | RXF_BRDCAST | RXF_MISS)) == 0 &&
1038 ether_cmp(ether_dhost, &etc->cur_etheraddr)))
1040 bcm_ether_ntoa((struct ether_addr*)ether_dhost, eabuf);
1041 ET_ERROR(("et%d: rx: bad dest address %s [%c%c%c]\n",
1042 etc->unit, eabuf, (flags & RXF_MULT) ? 'M' : ' ',
1043 (flags & RXF_BRDCAST) ? 'B' : ' ', (flags & RXF_MISS) ? 'P' : ' '));
1044 /* schedule reset */
1045 et->events |= INTR_ERROR;
1046 goto err;
1050 /* Extract priority from payload and store it out-of-band in skb->priority */
1051 if (et->etc->qos)
1052 pktsetprio(skb, TRUE);
1054 skb->dev = et->dev;
1055 skb->protocol = eth_type_trans(skb, et->dev);
1057 /* send it up */
1058 netif_rx(skb);
1060 ET_LOG("et%d: et_sendup ret", et->etc->unit, 0);
1062 return;
1064 err:
1065 bcm_ether_ntoa((struct ether_addr*)((struct ether_header*)skb->data)->ether_shost, eabuf);
1066 if (flags & RXF_NO) {
1067 ET_ERROR(("et%d: rx: crc error (odd nibbles) from %s\n", etc->unit, eabuf));
1069 if (flags & RXF_RXER) {
1070 ET_ERROR(("et%d: rx: symbol error from %s\n", etc->unit, eabuf));
1072 if ((flags & RXF_CRC) == RXF_CRC) {
1073 ET_ERROR(("et%d: rx: crc error from %s\n", etc->unit, eabuf));
1075 if (flags & RXF_OV) {
1076 ET_ERROR(("et%d: rx: fifo overflow\n", etc->unit));
1079 dev_kfree_skb_any(skb);
1081 return;
1084 void
1085 et_dump(et_info_t *et, struct bcmstrbuf *b)
1087 bcm_bprintf(b, "et%d: %s %s version %s\n", et->etc->unit,
1088 __DATE__, __TIME__, EPI_VERSION_STR);
1093 void
1094 et_link_up(et_info_t *et)
1096 ET_ERROR(("et%d: link up (%d%s)\n",
1097 et->etc->unit, et->etc->speed, (et->etc->duplex? "FD" : "HD")));
1100 void
1101 et_link_down(et_info_t *et)
1103 ET_ERROR(("et%d: link down\n", et->etc->unit));
1107 * 47XX-specific shared mdc/mdio contortion:
1108 * Find the et associated with the same chip as <et>
1109 * and coreunit matching <coreunit>.
1111 void*
1112 et_phyfind(et_info_t *et, uint coreunit)
1114 et_info_t *tmp;
1115 uint bus, slot;
1117 bus = et->pdev->bus->number;
1118 slot = PCI_SLOT(et->pdev->devfn);
1120 /* walk the list et's */
1121 for (tmp = et_list; tmp; tmp = tmp->next) {
1122 if (et->etc == NULL)
1123 continue;
1124 if (tmp->pdev == NULL)
1125 continue;
1126 if (tmp->pdev->bus->number != bus)
1127 continue;
1128 if (tmp->etc->nicmode)
1129 if (PCI_SLOT(tmp->pdev->devfn) != slot)
1130 continue;
1131 if (tmp->etc->coreunit != coreunit)
1132 continue;
1133 break;
1135 return (tmp);
1138 /* shared phy read entry point */
1139 uint16
1140 et_phyrd(et_info_t *et, uint phyaddr, uint reg)
1142 uint16 val;
1144 ET_LOCK(et);
1145 val = et->etc->chops->phyrd(et->etc->ch, phyaddr, reg);
1146 ET_UNLOCK(et);
1148 return (val);
1151 /* shared phy write entry point */
1152 void
1153 et_phywr(et_info_t *et, uint phyaddr, uint reg, uint16 val)
1155 ET_LOCK(et);
1156 et->etc->chops->phywr(et->etc->ch, phyaddr, reg, val);
1157 ET_UNLOCK(et);