OSDN Git Service

[PATCH] PPP: fix crash using usb-serial on high speed devices
[linux-kernel-docs/linux-2.4.36.git] / drivers / net / cirrus.c
1
2 /*
3  * linux/drivers/net/cirrus.c
4  *
5  * Author: Abraham van der Merwe <abraham@2d3d.co.za>
6  *
7  * A Cirrus Logic CS8900A driver for Linux
8  * based on the cs89x0 driver written by Russell Nelson,
9  * Donald Becker, and others.
10  *
11  * This source code is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * version 2 as published by the Free Software Foundation.
14  */
15
16 /*
17  * At the moment the driver does not support memory mode operation.
18  * It is trivial to implement this, but not worth the effort.
19  */
20
21 /*
22  * TODO:
23  *
24  *   1. If !ready in send_start(), queue buffer and send it in interrupt handler
25  *      when we receive a BufEvent with Rdy4Tx, send it again. dangerous!
26  *   2. how do we prevent interrupt handler destroying integrity of get_stats()?
27  *   3. Change reset code to check status.
28  *   4. Implement set_mac_address and remove fake mac address
29  *   5. Link status detection stuff
30  *   6. Write utility to write EEPROM, do self testing, etc.
31  *   7. Implement DMA routines (I need a board w/ DMA support for that)
32  *   8. Power management
33  *   9. Add support for multiple ethernet chips
34  *  10. Add support for other cs89xx chips (need hardware for that)
35  */
36
37 #include <linux/config.h>
38 #include <linux/version.h>
39 #include <linux/module.h>
40
41 #include <linux/kernel.h>
42 #include <linux/types.h>
43 #include <linux/errno.h>
44 #include <linux/ioport.h>
45 #include <linux/init.h>
46 #include <linux/delay.h>
47
48 #include <asm/irq.h>
49 #include <asm/hardware.h>
50 #include <asm/io.h>
51
52 #include <linux/netdevice.h>
53 #include <linux/etherdevice.h>
54 #include <linux/skbuff.h>
55
56 #include "cirrus.h"
57
58 /* #define DEBUG */
59 /* #define FULL_DUPLEX */
60
61 #ifdef CONFIG_SA1100_FRODO
62 #       define CIRRUS_DEFAULT_IO FRODO_ETH_IO + 0x300
63 #       define CIRRUS_DEFAULT_IRQ FRODO_ETH_IRQ
64 #elif CONFIG_SA1100_CERF
65 #       define CIRRUS_DEFAULT_IO CERF_ETH_IO + 0x300
66 #       define CIRRUS_DEFAULT_IRQ CERF_ETH_IRQ
67 #elif CONFIG_ARCH_CDB89712
68 #       define CIRRUS_DEFAULT_IO ETHER_BASE + 0x300
69 #       define CIRRUS_DEFAULT_IRQ IRQ_EINT3
70 #else
71 #       define CIRRUS_DEFAULT_IO        0
72 #       define CIRRUS_DEFAULT_IRQ       0
73 #endif  /* #ifdef CONFIG_SA1100_CERF */
74
75 typedef struct {
76         struct net_device_stats stats;
77         u16 txlen;
78 } cirrus_t;
79
80 typedef struct {
81         u16 io_base;            /* I/O Base Address                     */
82         u16 irq;                        /* Interrupt Number                     */
83         u16 dma;                        /* DMA Channel Numbers          */
84         u32 mem_base;           /* Memory Base Address          */
85         u32 rom_base;           /* Boot PROM Base Address       */
86         u32 rom_mask;           /* Boot PROM Address Mask       */
87         u8 mac[6];                      /* Individual Address           */
88 } cirrus_eeprom_t;
89
90 /*
91  * I/O routines
92  */
93
94 static inline u16 cirrus_read (struct net_device *dev,u16 reg)
95 {
96         outw (reg,dev->base_addr + PP_Address);
97         return (inw (dev->base_addr + PP_Data));
98 }
99
100 static inline void cirrus_write (struct net_device *dev,u16 reg,u16 value)
101 {
102         outw (reg,dev->base_addr + PP_Address);
103         outw (value,dev->base_addr + PP_Data);
104 }
105
106 static inline void cirrus_set (struct net_device *dev,u16 reg,u16 value)
107 {
108         cirrus_write (dev,reg,cirrus_read (dev,reg) | value);
109 }
110
111 static inline void cirrus_clear (struct net_device *dev,u16 reg,u16 value)
112 {
113         cirrus_write (dev,reg,cirrus_read (dev,reg) & ~value);
114 }
115
116 static inline void cirrus_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length)
117 {
118         insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2);
119 }
120
121 static inline void cirrus_frame_write (struct net_device *dev,struct sk_buff *skb)
122 {
123         outsw (dev->base_addr,skb->data,(skb->len + 1) / 2);
124 }
125
126 /*
127  * Debugging functions
128  */
129
130 #ifdef DEBUG
131 static inline int printable (int c)
132 {
133         return ((c >= 32 && c <= 126) ||
134                         (c >= 174 && c <= 223) ||
135                         (c >= 242 && c <= 243) ||
136                         (c >= 252 && c <= 253));
137 }
138
139 static void dump16 (struct net_device *dev,const u8 *s,size_t len)
140 {
141         int i;
142         char str[128];
143
144         if (!len) return;
145
146         *str = '\0';
147
148         for (i = 0; i < len; i++) {
149                 if (i && !(i % 4)) strcat (str," ");
150                 sprintf (str,"%s%.2x ",str,s[i]);
151         }
152
153         for ( ; i < 16; i++) {
154                 if (i && !(i % 4)) strcat (str," ");
155                 strcat (str,"   ");
156         }
157
158         strcat (str," ");
159         for (i = 0; i < len; i++) sprintf (str,"%s%c",str,printable (s[i]) ? s[i] : '.');
160
161         printk (KERN_DEBUG "%s:     %s\n",dev->name,str);
162 }
163
164 static void hexdump (struct net_device *dev,const void *ptr,size_t size)
165 {
166         const u8 *s = (u8 *) ptr;
167         int i;
168         for (i = 0; i < size / 16; i++, s += 16) dump16 (dev,s,16);
169         dump16 (dev,s,size % 16);
170 }
171
172 static void dump_packet (struct net_device *dev,struct sk_buff *skb,const char *type)
173 {
174         printk (KERN_INFO "%s: %s %d byte frame %.2x:%.2x:%.2x:%.2x:%.2x:%.2x to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x type %.4x\n",
175                         dev->name,
176                         type,
177                         skb->len,
178                         skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5],
179                         skb->data[6],skb->data[7],skb->data[8],skb->data[9],skb->data[10],skb->data[11],
180                         (skb->data[12] << 8) | skb->data[13]);
181         if (skb->len < 0x100) hexdump (dev,skb->data,skb->len);
182 }
183 #endif  /* #ifdef DEBUG */
184
185 /*
186  * Driver functions
187  */
188
189 static void cirrus_receive (struct net_device *dev)
190 {
191         cirrus_t *priv = (cirrus_t *) dev->priv;
192         struct sk_buff *skb;
193         u16 status,length;
194
195         status = cirrus_read (dev,PP_RxStatus);
196         length = cirrus_read (dev,PP_RxLength);
197
198         if (!(status & RxOK)) {
199                 priv->stats.rx_errors++;
200                 if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++;
201                 if ((status & CRCerror)) priv->stats.rx_crc_errors++;
202                 return;
203         }
204
205         if ((skb = dev_alloc_skb (length + 4)) == NULL) {
206                 priv->stats.rx_dropped++;
207                 return;
208         }
209
210         skb->dev = dev;
211         skb_reserve (skb,2);
212
213         cirrus_frame_read (dev,skb,length);
214
215 #ifdef DEBUG
216         dump_packet (dev,skb,"recv");
217 #endif  /* #ifdef DEBUG */
218
219         skb->protocol = eth_type_trans (skb,dev);
220
221         netif_rx (skb);
222         dev->last_rx = jiffies;
223
224         priv->stats.rx_packets++;
225         priv->stats.rx_bytes += length;
226 }
227
228 static int cirrus_send_start (struct sk_buff *skb,struct net_device *dev)
229 {
230         cirrus_t *priv = (cirrus_t *) dev->priv;
231         u16 status;
232
233         netif_stop_queue (dev);
234
235         cirrus_write (dev,PP_TxCMD,TxStart (After5));
236         cirrus_write (dev,PP_TxLength,skb->len);
237
238         status = cirrus_read (dev,PP_BusST);
239
240         if ((status & TxBidErr)) {
241                 printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len);
242                 priv->stats.tx_errors++;
243                 priv->stats.tx_aborted_errors++;
244                 priv->txlen = 0;
245                 return (1);
246         }
247
248         if (!(status & Rdy4TxNOW)) {
249                 printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name);
250                 priv->stats.tx_errors++;
251                 priv->txlen = 0;
252                 /* FIXME: store skb and send it in interrupt handler */
253                 return (1);
254         }
255
256         cirrus_frame_write (dev,skb);
257
258 #ifdef DEBUG
259         dump_packet (dev,skb,"send");
260 #endif  /* #ifdef DEBUG */
261
262         dev->trans_start = jiffies;
263
264         dev_kfree_skb (skb);
265
266         priv->txlen = skb->len;
267
268         return (0);
269 }
270
271 static void cirrus_interrupt (int irq,void *id,struct pt_regs *regs)
272 {
273         struct net_device *dev = (struct net_device *) id;
274         cirrus_t *priv;
275         u16 status;
276
277         if (dev->priv == NULL) {
278                 printk (KERN_WARNING "%s: irq %d for unknown device.\n",dev->name,irq);
279                 return;
280         }
281
282         priv = (cirrus_t *) dev->priv;
283
284         while ((status = cirrus_read (dev,PP_ISQ))) {
285                 switch (RegNum (status)) {
286                 case RxEvent:
287                         cirrus_receive (dev);
288                         break;
289
290                 case TxEvent:
291                         priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL));
292                         if (!(RegContent (status) & TxOK)) {
293                                 priv->stats.tx_errors++;
294                                 if ((RegContent (status) & Out_of_window)) priv->stats.tx_window_errors++;
295                                 if ((RegContent (status) & Jabber)) priv->stats.tx_aborted_errors++;
296                                 break;
297                         } else if (priv->txlen) {
298                                 priv->stats.tx_packets++;
299                                 priv->stats.tx_bytes += priv->txlen;
300                         }
301                         priv->txlen = 0;
302                         netif_wake_queue (dev);
303                         break;
304
305                 case BufEvent:
306                         if ((RegContent (status) & RxMiss)) {
307                                 u16 missed = MissCount (cirrus_read (dev,PP_RxMISS));
308                                 priv->stats.rx_errors += missed;
309                                 priv->stats.rx_missed_errors += missed;
310                         }
311                         if ((RegContent (status) & TxUnderrun)) {
312                                 priv->stats.tx_errors++;
313                                 priv->stats.tx_fifo_errors++;
314                         }
315                         /* FIXME: if Rdy4Tx, transmit last sent packet (if any) */
316                         priv->txlen = 0;
317                         netif_wake_queue (dev);
318                         break;
319
320                 case TxCOL:
321                         priv->stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL));
322                         break;
323
324                 case RxMISS:
325                         status = MissCount (cirrus_read (dev,PP_RxMISS));
326                         priv->stats.rx_errors += status;
327                         priv->stats.rx_missed_errors += status;
328                         break;
329                 }
330         }
331 }
332
333 static void cirrus_transmit_timeout (struct net_device *dev)
334 {
335         cirrus_t *priv = (cirrus_t *) dev->priv;
336         priv->stats.tx_errors++;
337         priv->stats.tx_heartbeat_errors++;
338         priv->txlen = 0;
339         netif_wake_queue (dev);
340 }
341
342 static int cirrus_start (struct net_device *dev)
343 {
344         int result;
345
346         /* valid ethernet address? */
347         if (!is_valid_ether_addr(dev->dev_addr)) {
348                 printk(KERN_ERR "%s: invalid ethernet MAC address\n",dev->name);
349                 return (-EINVAL);
350         }
351
352         /* install interrupt handler */
353         if ((result = request_irq (dev->irq,&cirrus_interrupt,0,dev->name,dev)) < 0) {
354                 printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name,dev->irq);
355                 return (result);
356         }
357
358         /* enable the ethernet controller */
359         cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
360         cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
361         cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
362         cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
363         cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON);
364         cirrus_set (dev,PP_BusCTL,EnableRQ);
365
366 #ifdef FULL_DUPLEX
367         cirrus_set (dev,PP_TestCTL,FDX);
368 #endif  /* #ifdef FULL_DUPLEX */
369
370         /* start the queue */
371         netif_start_queue (dev);
372
373         MOD_INC_USE_COUNT;
374
375         return (0);
376 }
377
378 static int cirrus_stop (struct net_device *dev)
379 {
380         /* disable ethernet controller */
381         cirrus_write (dev,PP_BusCTL,0);
382         cirrus_write (dev,PP_TestCTL,0);
383         cirrus_write (dev,PP_SelfCTL,0);
384         cirrus_write (dev,PP_LineCTL,0);
385         cirrus_write (dev,PP_BufCFG,0);
386         cirrus_write (dev,PP_TxCFG,0);
387         cirrus_write (dev,PP_RxCTL,0);
388         cirrus_write (dev,PP_RxCFG,0);
389
390         /* uninstall interrupt handler */
391         free_irq (dev->irq,dev);
392
393         /* stop the queue */
394         netif_stop_queue (dev);
395
396         MOD_DEC_USE_COUNT;
397
398         return (0);
399 }
400
401 static int cirrus_set_mac_address (struct net_device *dev, void *p)
402 {
403         struct sockaddr *addr = (struct sockaddr *)p;
404         int i;
405
406         if (netif_running(dev))
407                 return -EBUSY;
408
409         memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
410
411         /* configure MAC address */
412         for (i = 0; i < ETH_ALEN; i += 2)
413                 cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));
414
415         return 0;
416 }
417
418 static struct net_device_stats *cirrus_get_stats (struct net_device *dev)
419 {
420         cirrus_t *priv = (cirrus_t *) dev->priv;
421         return (&priv->stats);
422 }
423
424 static void cirrus_set_receive_mode (struct net_device *dev)
425 {
426         if ((dev->flags & IFF_PROMISC))
427                 cirrus_set (dev,PP_RxCTL,PromiscuousA);
428         else
429                 cirrus_clear (dev,PP_RxCTL,PromiscuousA);
430
431         if ((dev->flags & IFF_ALLMULTI) && dev->mc_list)
432                 cirrus_set (dev,PP_RxCTL,MulticastA);
433         else
434                 cirrus_clear (dev,PP_RxCTL,MulticastA);
435 }
436
437 static int cirrus_eeprom_wait (struct net_device *dev)
438 {
439         int i;
440
441         for (i = 0; i < 200; i++) {
442                 if (!(cirrus_read (dev,PP_SelfST) & SIBUSY))
443                         return (0);
444                 udelay (1);
445         }
446
447         return (-1);
448 }
449
450 static int cirrus_eeprom_read (struct net_device *dev,u16 *value,u16 offset)
451 {
452         if (cirrus_eeprom_wait (dev) < 0)
453                 return (-1);
454
455         cirrus_write (dev,PP_EEPROMCommand,offset | EEReadRegister);
456
457         if (cirrus_eeprom_wait (dev) < 0)
458                 return (-1);
459
460         *value = cirrus_read (dev,PP_EEPROMData);
461
462         return (0);
463 }
464
465 static int cirrus_eeprom (struct net_device *dev,cirrus_eeprom_t *eeprom)
466 {
467         u16 offset,buf[16],*word;
468         u8 checksum = 0,*byte;
469
470         if (cirrus_eeprom_read (dev,buf,0) < 0) {
471                 read_timed_out:
472                 printk (KERN_DEBUG "%s: EEPROM read timed out\n",dev->name);
473                 return (-ETIMEDOUT);
474         }
475
476         if ((buf[0] >> 8) != 0xa1) {
477                 printk (KERN_DEBUG "%s: No EEPROM present\n",dev->name);
478                 return (-ENODEV);
479         }
480
481         if ((buf[0] & 0xff) < sizeof (buf)) {
482                 eeprom_too_small:
483                 printk (KERN_DEBUG "%s: EEPROM too small\n",dev->name);
484                 return (-ENODEV);
485         }
486
487         for (offset = 1; offset < (buf[0] & 0xff); offset++) {
488                 if (cirrus_eeprom_read (dev,buf + offset,offset) < 0)
489                         goto read_timed_out;
490
491                 if (buf[offset] == 0xffff)
492                         goto eeprom_too_small;
493         }
494
495         if (buf[1] != 0x2020) {
496                 printk (KERN_DEBUG "%s: Group Header #1 mismatch\n",dev->name);
497                 return (-EIO);
498         }
499
500         if (buf[5] != 0x502c) {
501                 printk (KERN_DEBUG "%s: Group Header #2 mismatch\n",dev->name);
502                 return (-EIO);
503         }
504
505         if (buf[12] != 0x2158) {
506                 printk (KERN_DEBUG "%s: Group Header #3 mismatch\n",dev->name);
507                 return (-EIO);
508         }
509
510         eeprom->io_base = buf[2];
511         eeprom->irq = buf[3];
512         eeprom->dma = buf[4];
513         eeprom->mem_base = (buf[7] << 16) | buf[6];
514         eeprom->rom_base = (buf[9] << 16) | buf[8];
515         eeprom->rom_mask = (buf[11] << 16) | buf[10];
516
517         word = (u16 *) eeprom->mac;
518         for (offset = 0; offset < 3; offset++) word[offset] = buf[13 + offset];
519
520         byte = (u8 *) buf;
521         for (offset = 0; offset < sizeof (buf); offset++) checksum += byte[offset];
522
523         if (cirrus_eeprom_read (dev,&offset,0x10) < 0)
524                 goto read_timed_out;
525
526         if ((offset >> 8) != (u8) (0x100 - checksum)) {
527                 printk (KERN_DEBUG "%s: Checksum mismatch (expected 0x%.2x, got 0x%.2x instead\n",
528                                 dev->name,
529                                 (u8) (0x100 - checksum),
530                                 offset >> 8);
531                 return (-EIO);
532         }
533
534         return (0);
535 }
536
537 /*
538  * Architecture dependant code
539  */
540
541 #ifdef CONFIG_SA1100_FRODO
542 static void frodo_reset (struct net_device *dev)
543 {
544         int i;
545         volatile u16 value;
546
547         /* reset ethernet controller */
548         FRODO_CPLD_ETHERNET |= FRODO_ETH_RESET;
549         mdelay (50);
550         FRODO_CPLD_ETHERNET &= ~FRODO_ETH_RESET;
551         mdelay (50);
552
553         /* we tied SBHE to CHIPSEL, so each memory access ensure the chip is in 16-bit mode */
554         for (i = 0; i < 3; i++) value = cirrus_read (dev,0);
555
556         /* FIXME: poll status bit */
557 }
558 #endif  /* #ifdef CONFIG_SA1100_FRODO */
559
560 /*
561  * Driver initialization routines
562  */
563
564 static int io = 0;
565 static int irq = 0;
566
567 int __init cirrus_probe (struct net_device *dev)
568 {
569         static cirrus_t priv;
570         int i,result;
571         u16 value;
572         cirrus_eeprom_t eeprom;
573
574         printk ("Cirrus Logic CS8900A driver for Linux (V0.02)\n");
575
576         memset (&priv,0,sizeof (cirrus_t));
577
578         ether_setup (dev);
579
580         dev->open               = cirrus_start;
581         dev->stop               = cirrus_stop;
582         dev->hard_start_xmit    = cirrus_send_start;
583         dev->get_stats          = cirrus_get_stats;
584         dev->set_multicast_list = cirrus_set_receive_mode;
585         dev->set_mac_address    = cirrus_set_mac_address;
586         dev->tx_timeout         = cirrus_transmit_timeout;
587         dev->watchdog_timeo     = HZ;
588
589         dev->dev_addr[0] = 0x00;
590         dev->dev_addr[1] = 0x00;
591         dev->dev_addr[2] = 0x00;
592         dev->dev_addr[3] = 0x00;
593         dev->dev_addr[4] = 0x00;
594         dev->dev_addr[5] = 0x00;
595
596         dev->if_port   = IF_PORT_10BASET;
597         dev->priv      = (void *) &priv;
598
599         SET_MODULE_OWNER (dev);
600
601         dev->base_addr = CIRRUS_DEFAULT_IO;
602         dev->irq = CIRRUS_DEFAULT_IRQ;
603
604         /* module parameters override everything */
605         if (io > 0) dev->base_addr = io;
606         if (irq > 0) dev->irq = irq;
607
608         if (!dev->base_addr) {
609                 printk (KERN_ERR
610                                 "%s: No default I/O base address defined. Use io=... or\n"
611                                 "%s: define CIRRUS_DEFAULT_IO for your platform\n",
612                                 dev->name,dev->name);
613                 return (-EINVAL);
614         }
615
616         if (!dev->irq) {
617                 printk (KERN_ERR
618                                 "%s: No default IRQ number defined. Use irq=... or\n"
619                                 "%s: define CIRRUS_DEFAULT_IRQ for your platform\n",
620                                 dev->name,dev->name);
621                 return (-EINVAL);
622         }
623
624         if ((result = check_region (dev->base_addr,16))) {
625                 printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr);
626                 return (result);
627         }
628
629         if (!request_region (dev->base_addr,16,dev->name))
630                 return -EBUSY;
631
632 #ifdef CONFIG_SA1100_FRODO
633         frodo_reset (dev);
634 #endif  /* #ifdef CONFIG_SA1100_FRODO */
635
636         /* if an EEPROM is present, use it's MAC address */
637         if (!cirrus_eeprom (dev,&eeprom))
638                 for (i = 0; i < 6; i++)
639                         dev->dev_addr[i] = eeprom.mac[i];
640
641         /* verify EISA registration number for Cirrus Logic */
642         if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) {
643                 printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value);
644                 return (-ENXIO);
645         }
646
647         /* verify chip version */
648         value = cirrus_read (dev,PP_ProductID + 2);
649         if (VERSION (value) != CS8900A) {
650                 printk (KERN_ERR "%s: unknown chip version 0x%.8x\n",dev->name,VERSION (value));
651                 return (-ENXIO);
652         }
653         printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B);
654
655         /* setup interrupt number */
656         cirrus_write (dev,PP_IntNum,0);
657
658         /* configure MAC address */
659         for (i = 0; i < ETH_ALEN; i += 2)
660                 cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));
661
662         return (0);
663 }
664
665 EXPORT_NO_SYMBOLS;
666
667 static struct net_device dev;
668
669 static int __init cirrus_init (void)
670 {
671         memset (&dev,0,sizeof (struct net_device));
672         dev.init = cirrus_probe;
673         return (register_netdev (&dev));
674 }
675
676 static void __exit cirrus_cleanup (void)
677 {
678         release_region (dev.base_addr,16);
679         unregister_netdev (&dev);
680 }
681
682 MODULE_AUTHOR ("Abraham van der Merwe <abraham@2d3d.co.za>");
683 MODULE_DESCRIPTION ("Cirrus Logic CS8900A driver for Linux (V0.02)");
684 MODULE_LICENSE ("GPL");
685 MODULE_PARM_DESC (io,"I/O Base Address");
686 MODULE_PARM (io,"i");
687 MODULE_PARM_DESC (irq,"IRQ Number");
688 MODULE_PARM (irq,"i");
689
690 module_init (cirrus_init);
691 module_exit (cirrus_cleanup);
692