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