diff options
Diffstat (limited to 'drivers/net/mac89x0.c')
-rw-r--r-- | drivers/net/mac89x0.c | 666 |
1 files changed, 666 insertions, 0 deletions
diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c new file mode 100644 index 000000000000..f65b0db111b8 --- /dev/null +++ b/drivers/net/mac89x0.c | |||
@@ -0,0 +1,666 @@ | |||
1 | /* mac89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */ | ||
2 | /* | ||
3 | Written 1996 by Russell Nelson, with reference to skeleton.c | ||
4 | written 1993-1994 by Donald Becker. | ||
5 | |||
6 | This software may be used and distributed according to the terms | ||
7 | of the GNU General Public License, incorporated herein by reference. | ||
8 | |||
9 | The author may be reached at nelson@crynwr.com, Crynwr | ||
10 | Software, 11 Grant St., Potsdam, NY 13676 | ||
11 | |||
12 | Changelog: | ||
13 | |||
14 | Mike Cruse : mcruse@cti-ltd.com | ||
15 | : Changes for Linux 2.0 compatibility. | ||
16 | : Added dev_id parameter in net_interrupt(), | ||
17 | : request_irq() and free_irq(). Just NULL for now. | ||
18 | |||
19 | Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros | ||
20 | : in net_open() and net_close() so kerneld would know | ||
21 | : that the module is in use and wouldn't eject the | ||
22 | : driver prematurely. | ||
23 | |||
24 | Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c | ||
25 | : as an example. Disabled autoprobing in init_module(), | ||
26 | : not a good thing to do to other devices while Linux | ||
27 | : is running from all accounts. | ||
28 | |||
29 | Alan Cox : Removed 1.2 support, added 2.1 extra counters. | ||
30 | |||
31 | David Huggins-Daines <dhd@debian.org> | ||
32 | |||
33 | Split this off into mac89x0.c, and gutted it of all parts which are | ||
34 | not relevant to the existing CS8900 cards on the Macintosh | ||
35 | (i.e. basically the Daynaport CS and LC cards). To be precise: | ||
36 | |||
37 | * Removed all the media-detection stuff, because these cards are | ||
38 | TP-only. | ||
39 | |||
40 | * Lobotomized the ISA interrupt bogosity, because these cards use | ||
41 | a hardwired NuBus interrupt and a magic ISAIRQ value in the card. | ||
42 | |||
43 | * Basically eliminated everything not relevant to getting the | ||
44 | cards minimally functioning on the Macintosh. | ||
45 | |||
46 | I might add that these cards are badly designed even from the Mac | ||
47 | standpoint, in that Dayna, in their infinite wisdom, used NuBus slot | ||
48 | I/O space and NuBus interrupts for these cards, but neglected to | ||
49 | provide anything even remotely resembling a NuBus ROM. Therefore we | ||
50 | have to probe for them in a brain-damaged ISA-like fashion. | ||
51 | |||
52 | Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 11/01/2001 | ||
53 | check kmalloc and release the allocated memory on failure in | ||
54 | mac89x0_probe and in init_module | ||
55 | use local_irq_{save,restore}(flags) in net_get_stat, not just | ||
56 | local_irq_{dis,en}able() | ||
57 | */ | ||
58 | |||
59 | static char *version = | ||
60 | "cs89x0.c:v1.02 11/26/96 Russell Nelson <nelson@crynwr.com>\n"; | ||
61 | |||
62 | /* ======================= configure the driver here ======================= */ | ||
63 | |||
64 | /* use 0 for production, 1 for verification, >2 for debug */ | ||
65 | #ifndef NET_DEBUG | ||
66 | #define NET_DEBUG 0 | ||
67 | #endif | ||
68 | |||
69 | /* ======================= end of configuration ======================= */ | ||
70 | |||
71 | |||
72 | /* Always include 'config.h' first in case the user wants to turn on | ||
73 | or override something. */ | ||
74 | #include <linux/module.h> | ||
75 | |||
76 | #define PRINTK(x) printk x | ||
77 | |||
78 | /* | ||
79 | Sources: | ||
80 | |||
81 | Crynwr packet driver epktisa. | ||
82 | |||
83 | Crystal Semiconductor data sheets. | ||
84 | |||
85 | */ | ||
86 | |||
87 | #include <linux/kernel.h> | ||
88 | #include <linux/types.h> | ||
89 | #include <linux/fcntl.h> | ||
90 | #include <linux/interrupt.h> | ||
91 | #include <linux/ioport.h> | ||
92 | #include <linux/in.h> | ||
93 | #include <linux/slab.h> | ||
94 | #include <linux/string.h> | ||
95 | #include <linux/nubus.h> | ||
96 | #include <linux/errno.h> | ||
97 | #include <linux/init.h> | ||
98 | #include <linux/netdevice.h> | ||
99 | #include <linux/etherdevice.h> | ||
100 | #include <linux/skbuff.h> | ||
101 | #include <linux/delay.h> | ||
102 | |||
103 | #include <asm/system.h> | ||
104 | #include <asm/bitops.h> | ||
105 | #include <asm/io.h> | ||
106 | #include <asm/hwtest.h> | ||
107 | #include <asm/macints.h> | ||
108 | |||
109 | #include "cs89x0.h" | ||
110 | |||
111 | static unsigned int net_debug = NET_DEBUG; | ||
112 | |||
113 | /* Information that need to be kept for each board. */ | ||
114 | struct net_local { | ||
115 | struct net_device_stats stats; | ||
116 | int chip_type; /* one of: CS8900, CS8920, CS8920M */ | ||
117 | char chip_revision; /* revision letter of the chip ('A'...) */ | ||
118 | int send_cmd; /* the propercommand used to send a packet. */ | ||
119 | int rx_mode; | ||
120 | int curr_rx_cfg; | ||
121 | int send_underrun; /* keep track of how many underruns in a row we get */ | ||
122 | struct sk_buff *skb; | ||
123 | }; | ||
124 | |||
125 | /* Index to functions, as function prototypes. */ | ||
126 | |||
127 | #if 0 | ||
128 | extern void reset_chip(struct net_device *dev); | ||
129 | #endif | ||
130 | static int net_open(struct net_device *dev); | ||
131 | static int net_send_packet(struct sk_buff *skb, struct net_device *dev); | ||
132 | static irqreturn_t net_interrupt(int irq, void *dev_id, struct pt_regs *regs); | ||
133 | static void set_multicast_list(struct net_device *dev); | ||
134 | static void net_rx(struct net_device *dev); | ||
135 | static int net_close(struct net_device *dev); | ||
136 | static struct net_device_stats *net_get_stats(struct net_device *dev); | ||
137 | static int set_mac_address(struct net_device *dev, void *addr); | ||
138 | |||
139 | |||
140 | /* Example routines you must write ;->. */ | ||
141 | #define tx_done(dev) 1 | ||
142 | |||
143 | /* For reading/writing registers ISA-style */ | ||
144 | static inline int | ||
145 | readreg_io(struct net_device *dev, int portno) | ||
146 | { | ||
147 | nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); | ||
148 | return swab16(nubus_readw(dev->base_addr + DATA_PORT)); | ||
149 | } | ||
150 | |||
151 | static inline void | ||
152 | writereg_io(struct net_device *dev, int portno, int value) | ||
153 | { | ||
154 | nubus_writew(swab16(portno), dev->base_addr + ADD_PORT); | ||
155 | nubus_writew(swab16(value), dev->base_addr + DATA_PORT); | ||
156 | } | ||
157 | |||
158 | /* These are for reading/writing registers in shared memory */ | ||
159 | static inline int | ||
160 | readreg(struct net_device *dev, int portno) | ||
161 | { | ||
162 | return swab16(nubus_readw(dev->mem_start + portno)); | ||
163 | } | ||
164 | |||
165 | static inline void | ||
166 | writereg(struct net_device *dev, int portno, int value) | ||
167 | { | ||
168 | nubus_writew(swab16(value), dev->mem_start + portno); | ||
169 | } | ||
170 | |||
171 | /* Probe for the CS8900 card in slot E. We won't bother looking | ||
172 | anywhere else until we have a really good reason to do so. */ | ||
173 | struct net_device * __init mac89x0_probe(int unit) | ||
174 | { | ||
175 | struct net_device *dev; | ||
176 | static int once_is_enough; | ||
177 | struct net_local *lp; | ||
178 | static unsigned version_printed; | ||
179 | int i, slot; | ||
180 | unsigned rev_type = 0; | ||
181 | unsigned long ioaddr; | ||
182 | unsigned short sig; | ||
183 | int err = -ENODEV; | ||
184 | |||
185 | dev = alloc_etherdev(sizeof(struct net_local)); | ||
186 | if (!dev) | ||
187 | return ERR_PTR(-ENOMEM); | ||
188 | |||
189 | if (unit >= 0) { | ||
190 | sprintf(dev->name, "eth%d", unit); | ||
191 | netdev_boot_setup_check(dev); | ||
192 | } | ||
193 | |||
194 | SET_MODULE_OWNER(dev); | ||
195 | |||
196 | if (once_is_enough) | ||
197 | goto out; | ||
198 | once_is_enough = 1; | ||
199 | |||
200 | /* We might have to parameterize this later */ | ||
201 | slot = 0xE; | ||
202 | /* Get out now if there's a real NuBus card in slot E */ | ||
203 | if (nubus_find_slot(slot, NULL) != NULL) | ||
204 | goto out; | ||
205 | |||
206 | /* The pseudo-ISA bits always live at offset 0x300 (gee, | ||
207 | wonder why...) */ | ||
208 | ioaddr = (unsigned long) | ||
209 | nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE); | ||
210 | { | ||
211 | unsigned long flags; | ||
212 | int card_present; | ||
213 | |||
214 | local_irq_save(flags); | ||
215 | card_present = hwreg_present((void*) ioaddr+4) | ||
216 | && hwreg_present((void*) ioaddr + DATA_PORT); | ||
217 | local_irq_restore(flags); | ||
218 | |||
219 | if (!card_present) | ||
220 | goto out; | ||
221 | } | ||
222 | |||
223 | nubus_writew(0, ioaddr + ADD_PORT); | ||
224 | sig = nubus_readw(ioaddr + DATA_PORT); | ||
225 | if (sig != swab16(CHIP_EISA_ID_SIG)) | ||
226 | goto out; | ||
227 | |||
228 | /* Initialize the net_device structure. */ | ||
229 | lp = netdev_priv(dev); | ||
230 | |||
231 | /* Fill in the 'dev' fields. */ | ||
232 | dev->base_addr = ioaddr; | ||
233 | dev->mem_start = (unsigned long) | ||
234 | nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE); | ||
235 | dev->mem_end = dev->mem_start + 0x1000; | ||
236 | |||
237 | /* Turn on shared memory */ | ||
238 | writereg_io(dev, PP_BusCTL, MEMORY_ON); | ||
239 | |||
240 | /* get the chip type */ | ||
241 | rev_type = readreg(dev, PRODUCT_ID_ADD); | ||
242 | lp->chip_type = rev_type &~ REVISON_BITS; | ||
243 | lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; | ||
244 | |||
245 | /* Check the chip type and revision in order to set the correct send command | ||
246 | CS8920 revision C and CS8900 revision F can use the faster send. */ | ||
247 | lp->send_cmd = TX_AFTER_381; | ||
248 | if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') | ||
249 | lp->send_cmd = TX_NOW; | ||
250 | if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') | ||
251 | lp->send_cmd = TX_NOW; | ||
252 | |||
253 | if (net_debug && version_printed++ == 0) | ||
254 | printk(version); | ||
255 | |||
256 | printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx", | ||
257 | dev->name, | ||
258 | lp->chip_type==CS8900?'0':'2', | ||
259 | lp->chip_type==CS8920M?"M":"", | ||
260 | lp->chip_revision, | ||
261 | dev->base_addr); | ||
262 | |||
263 | /* Try to read the MAC address */ | ||
264 | if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) { | ||
265 | printk("\nmac89x0: No EEPROM, giving up now.\n"); | ||
266 | goto out1; | ||
267 | } else { | ||
268 | for (i = 0; i < ETH_ALEN; i += 2) { | ||
269 | /* Big-endian (why??!) */ | ||
270 | unsigned short s = readreg(dev, PP_IA + i); | ||
271 | dev->dev_addr[i] = s >> 8; | ||
272 | dev->dev_addr[i+1] = s & 0xff; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | dev->irq = SLOT2IRQ(slot); | ||
277 | printk(" IRQ %d ADDR ", dev->irq); | ||
278 | |||
279 | /* print the ethernet address. */ | ||
280 | for (i = 0; i < ETH_ALEN; i++) | ||
281 | printk("%2.2x%s", dev->dev_addr[i], | ||
282 | ((i < ETH_ALEN-1) ? ":" : "")); | ||
283 | printk("\n"); | ||
284 | |||
285 | dev->open = net_open; | ||
286 | dev->stop = net_close; | ||
287 | dev->hard_start_xmit = net_send_packet; | ||
288 | dev->get_stats = net_get_stats; | ||
289 | dev->set_multicast_list = &set_multicast_list; | ||
290 | dev->set_mac_address = &set_mac_address; | ||
291 | |||
292 | err = register_netdev(dev); | ||
293 | if (err) | ||
294 | goto out1; | ||
295 | return 0; | ||
296 | out1: | ||
297 | nubus_writew(0, dev->base_addr + ADD_PORT); | ||
298 | out: | ||
299 | free_netdev(dev); | ||
300 | return ERR_PTR(err); | ||
301 | } | ||
302 | |||
303 | #if 0 | ||
304 | /* This is useful for something, but I don't know what yet. */ | ||
305 | void __init reset_chip(struct net_device *dev) | ||
306 | { | ||
307 | int reset_start_time; | ||
308 | |||
309 | writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); | ||
310 | |||
311 | /* wait 30 ms */ | ||
312 | msleep_interruptible(30); | ||
313 | |||
314 | /* Wait until the chip is reset */ | ||
315 | reset_start_time = jiffies; | ||
316 | while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) | ||
317 | ; | ||
318 | } | ||
319 | #endif | ||
320 | |||
321 | /* Open/initialize the board. This is called (in the current kernel) | ||
322 | sometime after booting when the 'ifconfig' program is run. | ||
323 | |||
324 | This routine should set everything up anew at each open, even | ||
325 | registers that "should" only need to be set once at boot, so that | ||
326 | there is non-reboot way to recover if something goes wrong. | ||
327 | */ | ||
328 | static int | ||
329 | net_open(struct net_device *dev) | ||
330 | { | ||
331 | struct net_local *lp = netdev_priv(dev); | ||
332 | int i; | ||
333 | |||
334 | /* Disable the interrupt for now */ | ||
335 | writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); | ||
336 | |||
337 | /* Grab the interrupt */ | ||
338 | if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) | ||
339 | return -EAGAIN; | ||
340 | |||
341 | /* Set up the IRQ - Apparently magic */ | ||
342 | if (lp->chip_type == CS8900) | ||
343 | writereg(dev, PP_CS8900_ISAINT, 0); | ||
344 | else | ||
345 | writereg(dev, PP_CS8920_ISAINT, 0); | ||
346 | |||
347 | /* set the Ethernet address */ | ||
348 | for (i=0; i < ETH_ALEN/2; i++) | ||
349 | writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); | ||
350 | |||
351 | /* Turn on both receive and transmit operations */ | ||
352 | writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); | ||
353 | |||
354 | /* Receive only error free packets addressed to this card */ | ||
355 | lp->rx_mode = 0; | ||
356 | writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); | ||
357 | |||
358 | lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; | ||
359 | |||
360 | writereg(dev, PP_RxCFG, lp->curr_rx_cfg); | ||
361 | |||
362 | writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | | ||
363 | TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); | ||
364 | |||
365 | writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | | ||
366 | TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); | ||
367 | |||
368 | /* now that we've got our act together, enable everything */ | ||
369 | writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ); | ||
370 | netif_start_queue(dev); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static int | ||
375 | net_send_packet(struct sk_buff *skb, struct net_device *dev) | ||
376 | { | ||
377 | if (dev->tbusy) { | ||
378 | /* If we get here, some higher level has decided we are broken. | ||
379 | There should really be a "kick me" function call instead. */ | ||
380 | int tickssofar = jiffies - dev->trans_start; | ||
381 | if (tickssofar < 5) | ||
382 | return 1; | ||
383 | if (net_debug > 0) printk("%s: transmit timed out, %s?\n", dev->name, | ||
384 | tx_done(dev) ? "IRQ conflict" : "network cable problem"); | ||
385 | /* Try to restart the adaptor. */ | ||
386 | dev->tbusy=0; | ||
387 | dev->trans_start = jiffies; | ||
388 | } | ||
389 | |||
390 | /* Block a timer-based transmit from overlapping. This could better be | ||
391 | done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ | ||
392 | if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) | ||
393 | printk("%s: Transmitter access conflict.\n", dev->name); | ||
394 | else { | ||
395 | struct net_local *lp = netdev_priv(dev); | ||
396 | unsigned long flags; | ||
397 | |||
398 | if (net_debug > 3) | ||
399 | printk("%s: sent %d byte packet of type %x\n", | ||
400 | dev->name, skb->len, | ||
401 | (skb->data[ETH_ALEN+ETH_ALEN] << 8) | ||
402 | | skb->data[ETH_ALEN+ETH_ALEN+1]); | ||
403 | |||
404 | /* keep the upload from being interrupted, since we | ||
405 | ask the chip to start transmitting before the | ||
406 | whole packet has been completely uploaded. */ | ||
407 | local_irq_save(flags); | ||
408 | |||
409 | /* initiate a transmit sequence */ | ||
410 | writereg(dev, PP_TxCMD, lp->send_cmd); | ||
411 | writereg(dev, PP_TxLength, skb->len); | ||
412 | |||
413 | /* Test to see if the chip has allocated memory for the packet */ | ||
414 | if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { | ||
415 | /* Gasp! It hasn't. But that shouldn't happen since | ||
416 | we're waiting for TxOk, so return 1 and requeue this packet. */ | ||
417 | local_irq_restore(flags); | ||
418 | return 1; | ||
419 | } | ||
420 | |||
421 | /* Write the contents of the packet */ | ||
422 | memcpy_toio(dev->mem_start + PP_TxFrame, skb->data, skb->len+1); | ||
423 | |||
424 | local_irq_restore(flags); | ||
425 | dev->trans_start = jiffies; | ||
426 | } | ||
427 | dev_kfree_skb (skb); | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | /* The typical workload of the driver: | ||
433 | Handle the network interface interrupts. */ | ||
434 | static irqreturn_t net_interrupt(int irq, void *dev_id, struct pt_regs * regs) | ||
435 | { | ||
436 | struct net_device *dev = dev_id; | ||
437 | struct net_local *lp; | ||
438 | int ioaddr, status; | ||
439 | |||
440 | if (dev == NULL) { | ||
441 | printk ("net_interrupt(): irq %d for unknown device.\n", irq); | ||
442 | return IRQ_NONE; | ||
443 | } | ||
444 | if (dev->interrupt) | ||
445 | printk("%s: Re-entering the interrupt handler.\n", dev->name); | ||
446 | dev->interrupt = 1; | ||
447 | |||
448 | ioaddr = dev->base_addr; | ||
449 | lp = netdev_priv(dev); | ||
450 | |||
451 | /* we MUST read all the events out of the ISQ, otherwise we'll never | ||
452 | get interrupted again. As a consequence, we can't have any limit | ||
453 | on the number of times we loop in the interrupt handler. The | ||
454 | hardware guarantees that eventually we'll run out of events. Of | ||
455 | course, if you're on a slow machine, and packets are arriving | ||
456 | faster than you can read them off, you're screwed. Hasta la | ||
457 | vista, baby! */ | ||
458 | while ((status = swab16(nubus_readw(dev->base_addr + ISQ_PORT)))) { | ||
459 | if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); | ||
460 | switch(status & ISQ_EVENT_MASK) { | ||
461 | case ISQ_RECEIVER_EVENT: | ||
462 | /* Got a packet(s). */ | ||
463 | net_rx(dev); | ||
464 | break; | ||
465 | case ISQ_TRANSMITTER_EVENT: | ||
466 | lp->stats.tx_packets++; | ||
467 | dev->tbusy = 0; | ||
468 | mark_bh(NET_BH); /* Inform upper layers. */ | ||
469 | if ((status & TX_OK) == 0) lp->stats.tx_errors++; | ||
470 | if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++; | ||
471 | if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++; | ||
472 | if (status & TX_LATE_COL) lp->stats.tx_window_errors++; | ||
473 | if (status & TX_16_COL) lp->stats.tx_aborted_errors++; | ||
474 | break; | ||
475 | case ISQ_BUFFER_EVENT: | ||
476 | if (status & READY_FOR_TX) { | ||
477 | /* we tried to transmit a packet earlier, | ||
478 | but inexplicably ran out of buffers. | ||
479 | That shouldn't happen since we only ever | ||
480 | load one packet. Shrug. Do the right | ||
481 | thing anyway. */ | ||
482 | dev->tbusy = 0; | ||
483 | mark_bh(NET_BH); /* Inform upper layers. */ | ||
484 | } | ||
485 | if (status & TX_UNDERRUN) { | ||
486 | if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); | ||
487 | lp->send_underrun++; | ||
488 | if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; | ||
489 | else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; | ||
490 | } | ||
491 | break; | ||
492 | case ISQ_RX_MISS_EVENT: | ||
493 | lp->stats.rx_missed_errors += (status >>6); | ||
494 | break; | ||
495 | case ISQ_TX_COL_EVENT: | ||
496 | lp->stats.collisions += (status >>6); | ||
497 | break; | ||
498 | } | ||
499 | } | ||
500 | dev->interrupt = 0; | ||
501 | return IRQ_HANDLED; | ||
502 | } | ||
503 | |||
504 | /* We have a good packet(s), get it/them out of the buffers. */ | ||
505 | static void | ||
506 | net_rx(struct net_device *dev) | ||
507 | { | ||
508 | struct net_local *lp = netdev_priv(dev); | ||
509 | struct sk_buff *skb; | ||
510 | int status, length; | ||
511 | |||
512 | status = readreg(dev, PP_RxStatus); | ||
513 | if ((status & RX_OK) == 0) { | ||
514 | lp->stats.rx_errors++; | ||
515 | if (status & RX_RUNT) lp->stats.rx_length_errors++; | ||
516 | if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++; | ||
517 | if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT))) | ||
518 | /* per str 172 */ | ||
519 | lp->stats.rx_crc_errors++; | ||
520 | if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++; | ||
521 | return; | ||
522 | } | ||
523 | |||
524 | length = readreg(dev, PP_RxLength); | ||
525 | /* Malloc up new buffer. */ | ||
526 | skb = alloc_skb(length, GFP_ATOMIC); | ||
527 | if (skb == NULL) { | ||
528 | printk("%s: Memory squeeze, dropping packet.\n", dev->name); | ||
529 | lp->stats.rx_dropped++; | ||
530 | return; | ||
531 | } | ||
532 | skb_put(skb, length); | ||
533 | skb->dev = dev; | ||
534 | |||
535 | memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length); | ||
536 | |||
537 | if (net_debug > 3)printk("%s: received %d byte packet of type %x\n", | ||
538 | dev->name, length, | ||
539 | (skb->data[ETH_ALEN+ETH_ALEN] << 8) | ||
540 | | skb->data[ETH_ALEN+ETH_ALEN+1]); | ||
541 | |||
542 | skb->protocol=eth_type_trans(skb,dev); | ||
543 | netif_rx(skb); | ||
544 | dev->last_rx = jiffies; | ||
545 | lp->stats.rx_packets++; | ||
546 | lp->stats.rx_bytes += length; | ||
547 | } | ||
548 | |||
549 | /* The inverse routine to net_open(). */ | ||
550 | static int | ||
551 | net_close(struct net_device *dev) | ||
552 | { | ||
553 | |||
554 | writereg(dev, PP_RxCFG, 0); | ||
555 | writereg(dev, PP_TxCFG, 0); | ||
556 | writereg(dev, PP_BufCFG, 0); | ||
557 | writereg(dev, PP_BusCTL, 0); | ||
558 | |||
559 | netif_stop_queue(dev); | ||
560 | |||
561 | free_irq(dev->irq, dev); | ||
562 | |||
563 | /* Update the statistics here. */ | ||
564 | |||
565 | return 0; | ||
566 | |||
567 | } | ||
568 | |||
569 | /* Get the current statistics. This may be called with the card open or | ||
570 | closed. */ | ||
571 | static struct net_device_stats * | ||
572 | net_get_stats(struct net_device *dev) | ||
573 | { | ||
574 | struct net_local *lp = netdev_priv(dev); | ||
575 | unsigned long flags; | ||
576 | |||
577 | local_irq_save(flags); | ||
578 | /* Update the statistics from the device registers. */ | ||
579 | lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); | ||
580 | lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6); | ||
581 | local_irq_restore(flags); | ||
582 | |||
583 | return &lp->stats; | ||
584 | } | ||
585 | |||
586 | static void set_multicast_list(struct net_device *dev) | ||
587 | { | ||
588 | struct net_local *lp = netdev_priv(dev); | ||
589 | |||
590 | if(dev->flags&IFF_PROMISC) | ||
591 | { | ||
592 | lp->rx_mode = RX_ALL_ACCEPT; | ||
593 | } | ||
594 | else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) | ||
595 | { | ||
596 | /* The multicast-accept list is initialized to accept-all, and we | ||
597 | rely on higher-level filtering for now. */ | ||
598 | lp->rx_mode = RX_MULTCAST_ACCEPT; | ||
599 | } | ||
600 | else | ||
601 | lp->rx_mode = 0; | ||
602 | |||
603 | writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); | ||
604 | |||
605 | /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ | ||
606 | writereg(dev, PP_RxCFG, lp->curr_rx_cfg | | ||
607 | (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); | ||
608 | } | ||
609 | |||
610 | |||
611 | static int set_mac_address(struct net_device *dev, void *addr) | ||
612 | { | ||
613 | int i; | ||
614 | if (dev->start) | ||
615 | return -EBUSY; | ||
616 | printk("%s: Setting MAC address to ", dev->name); | ||
617 | for (i = 0; i < 6; i++) | ||
618 | printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); | ||
619 | printk(".\n"); | ||
620 | /* set the Ethernet address */ | ||
621 | for (i=0; i < ETH_ALEN/2; i++) | ||
622 | writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | #ifdef MODULE | ||
628 | |||
629 | static struct net_device *dev_cs89x0; | ||
630 | static int debug; | ||
631 | |||
632 | MODULE_PARM(debug, "i"); | ||
633 | MODULE_PARM_DESC(debug, "CS89[02]0 debug level (0-5)"); | ||
634 | MODULE_LICENSE("GPL"); | ||
635 | |||
636 | int | ||
637 | init_module(void) | ||
638 | { | ||
639 | net_debug = debug; | ||
640 | dev_cs89x0 = mac89x0_probe(-1); | ||
641 | if (IS_ERR(dev_cs89x0)) { | ||
642 | printk(KERN_WARNING "mac89x0.c: No card found\n"); | ||
643 | return PTR_ERR(dev_cs89x0); | ||
644 | } | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | void | ||
649 | cleanup_module(void) | ||
650 | { | ||
651 | unregister_netdev(dev_cs89x0); | ||
652 | nubus_writew(0, dev_cs89x0->base_addr + ADD_PORT); | ||
653 | free_netdev(dev_cs89x0); | ||
654 | } | ||
655 | #endif /* MODULE */ | ||
656 | |||
657 | /* | ||
658 | * Local variables: | ||
659 | * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o mac89x0.o mac89x0.c" | ||
660 | * version-control: t | ||
661 | * kept-new-versions: 5 | ||
662 | * c-indent-level: 8 | ||
663 | * tab-width: 8 | ||
664 | * End: | ||
665 | * | ||
666 | */ | ||