~ubuntu-branches/ubuntu/trusty/linux-armadaxp/trusty

« back to all changes in this revision

Viewing changes to drivers/net/ethernet/dlink/de600.c

  • Committer: Package Import Robot
  • Author(s): Michael Casadevall, Bryan Wu, Dann Frazier, Michael Casadeall
  • Date: 2012-03-10 15:00:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20120310150054-flugb39zon8vvgwe
Tags: 3.2.0-1600.1
[ Bryan Wu ]
* UBUNTU: import debian/debian.env and debian.armadaxp

[ Dann Frazier ]
* ARM: Armada XP: remove trailing '/' in dirnames in mvRules.mk

[ Michael Casadeall ]
* tools: add some tools for Marvell Armada XP processor
* kernel: timer tick hacking from Marvell
* kernel: Sheeva Errata: add delay on Sheeva when powering down
* net: add Marvell NFP netfilter
* net: socket and skb modifications made by Marvell
* miscdevice: add minor IDs for some Marvell Armada drivers
* fs: introduce memory pool for splice()
* video: EDID detection updates from Marvell Armada XP patchset
* video: backlight: add Marvell Dove LCD backlight driver
* video: display: add THS8200 display driver
* video: framebuffer: add Marvell Dove and Armada XP processor onchip LCD controller driver
* usbtest: add Interrupt transfer testing by Marvell Armada XP code
* usb: ehci: add support for Marvell EHCI controler
* tty/serial: 8250: add support for Marvell Armada XP processor and DeviceTree work
* rtc: add support for Marvell Armada XP onchip RTC controller
* net: pppoe: add Marvell ethernet NFP hook in PPPoE networking driver
* mtd: nand: add support for Marvell Armada XP Nand Flash Controller
* mtd: maps: add Marvell Armada XP specific map driver
* mmc: add support for Marvell Armada XP MMC/SD host controller
* i2c: add support for Marvell Armada XP onchip i2c bus controller
* hwmon: add Kconfig option for Armada XP onchip thermal sensor driver
* dmaengine: add Net DMA support for splice and update Marvell XOR DMA engine driver
* ata: add support for Marvell Armada XP SATA controller and update some quirks
* ARM: add Marvell Armada XP machine to mach-types
* ARM: oprofile: add support for Marvell PJ4B core
* ARM: mm: more ARMv6 switches for Marvell Armada XP
* ARM: remove static declaration to allow compilation
* ARM: alignment access fault trick
* ARM: mm: skip some fault fixing when run on NONE SMP ARMv6 mode during early abort event
* ARM: mm: add Marvell Sheeva CPU Architecture for PJ4B
* ARM: introduce optimized copy operation for Marvell Armada XP
* ARM: SAUCE: hardware breakpoint trick for Marvell Armada XP
* ARM: big endian and little endian tricks for Marvell Armada XP
* ARM: SAUCE: Add Marvell Armada XP build rules to arch/arm/kernel/Makefile
* ARM: vfp: add special handling for Marvell Armada XP
* ARM: add support for Marvell U-Boot
* ARM: add mv_controller_num for ARM PCI drivers
* ARM: add support for local PMUs, general SMP tweaks and cache flushing
* ARM: add Marvell device identifies in glue-proc.h
* ARM: add IPC driver support for Marvell platforms
* ARM: add DMA mapping for Marvell platforms
* ARM: add Sheeva errata and PJ4B code for booting
* ARM: update Kconfig and Makefile to include Marvell Armada XP platforms
* ARM: Armada XP: import LSP from Marvell for Armada XP 3.2 kernel enablement

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
static const char version[] = "de600.c: $Revision: 1.41-2.5 $,  Bjorn Ekwall (bj0rn@blox.se)\n";
 
2
/*
 
3
 *      de600.c
 
4
 *
 
5
 *      Linux driver for the D-Link DE-600 Ethernet pocket adapter.
 
6
 *
 
7
 *      Portions (C) Copyright 1993, 1994 by Bjorn Ekwall
 
8
 *      The Author may be reached as bj0rn@blox.se
 
9
 *
 
10
 *      Based on adapter information gathered from DE600.ASM by D-Link Inc.,
 
11
 *      as included on disk C in the v.2.11 of PC/TCP from FTP Software.
 
12
 *      For DE600.asm:
 
13
 *              Portions (C) Copyright 1990 D-Link, Inc.
 
14
 *              Copyright, 1988-1992, Russell Nelson, Crynwr Software
 
15
 *
 
16
 *      Adapted to the sample network driver core for linux,
 
17
 *      written by: Donald Becker <becker@super.org>
 
18
 *              (Now at <becker@scyld.com>)
 
19
 *
 
20
 **************************************************************/
 
21
/*
 
22
 *      This program is free software; you can redistribute it and/or modify
 
23
 *      it under the terms of the GNU General Public License as published by
 
24
 *      the Free Software Foundation; either version 2, or (at your option)
 
25
 *      any later version.
 
26
 *
 
27
 *      This program is distributed in the hope that it will be useful,
 
28
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 
29
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
30
 *      GNU General Public License for more details.
 
31
 *
 
32
 *      You should have received a copy of the GNU General Public License
 
33
 *      along with this program; if not, write to the Free Software
 
34
 *      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
35
 *
 
36
 **************************************************************/
 
37
 
 
38
/* Add more time here if your adapter won't work OK: */
 
39
#define DE600_SLOW_DOWN udelay(delay_time)
 
40
 
 
41
#include <linux/module.h>
 
42
#include <linux/kernel.h>
 
43
#include <linux/types.h>
 
44
#include <linux/fcntl.h>
 
45
#include <linux/string.h>
 
46
#include <linux/interrupt.h>
 
47
#include <linux/ioport.h>
 
48
#include <linux/in.h>
 
49
#include <asm/system.h>
 
50
#include <linux/errno.h>
 
51
#include <linux/init.h>
 
52
#include <linux/delay.h>
 
53
#include <linux/inet.h>
 
54
#include <linux/netdevice.h>
 
55
#include <linux/etherdevice.h>
 
56
#include <linux/skbuff.h>
 
57
 
 
58
#include <asm/io.h>
 
59
 
 
60
#include "de600.h"
 
61
 
 
62
static unsigned int check_lost = 1;
 
63
module_param(check_lost, bool, 0);
 
64
MODULE_PARM_DESC(check_lost, "If set then check for unplugged de600");
 
65
 
 
66
static unsigned int delay_time = 10;
 
67
module_param(delay_time, int, 0);
 
68
MODULE_PARM_DESC(delay_time, "DE-600 deley on I/O in microseconds");
 
69
 
 
70
 
 
71
/*
 
72
 * D-Link driver variables:
 
73
 */
 
74
 
 
75
static volatile int             rx_page;
 
76
 
 
77
#define TX_PAGES 2
 
78
static volatile int             tx_fifo[TX_PAGES];
 
79
static volatile int             tx_fifo_in;
 
80
static volatile int             tx_fifo_out;
 
81
static volatile int             free_tx_pages = TX_PAGES;
 
82
static int                      was_down;
 
83
static DEFINE_SPINLOCK(de600_lock);
 
84
 
 
85
static inline u8 de600_read_status(struct net_device *dev)
 
86
{
 
87
        u8 status;
 
88
 
 
89
        outb_p(STATUS, DATA_PORT);
 
90
        status = inb(STATUS_PORT);
 
91
        outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT);
 
92
 
 
93
        return status;
 
94
}
 
95
 
 
96
static inline u8 de600_read_byte(unsigned char type, struct net_device *dev)
 
97
{
 
98
        /* dev used by macros */
 
99
        u8 lo;
 
100
        outb_p((type), DATA_PORT);
 
101
        lo = ((unsigned char)inb(STATUS_PORT)) >> 4;
 
102
        outb_p((type) | HI_NIBBLE, DATA_PORT);
 
103
        return ((unsigned char)inb(STATUS_PORT) & (unsigned char)0xf0) | lo;
 
104
}
 
105
 
 
106
/*
 
107
 * Open/initialize the board.  This is called (in the current kernel)
 
108
 * after booting when 'ifconfig <dev->name> $IP_ADDR' is run (in rc.inet1).
 
109
 *
 
110
 * This routine should set everything up anew at each open, even
 
111
 * registers that "should" only need to be set once at boot, so that
 
112
 * there is a non-reboot way to recover if something goes wrong.
 
113
 */
 
114
 
 
115
static int de600_open(struct net_device *dev)
 
116
{
 
117
        unsigned long flags;
 
118
        int ret = request_irq(DE600_IRQ, de600_interrupt, 0, dev->name, dev);
 
119
        if (ret) {
 
120
                printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name, DE600_IRQ);
 
121
                return ret;
 
122
        }
 
123
        spin_lock_irqsave(&de600_lock, flags);
 
124
        ret = adapter_init(dev);
 
125
        spin_unlock_irqrestore(&de600_lock, flags);
 
126
        return ret;
 
127
}
 
128
 
 
129
/*
 
130
 * The inverse routine to de600_open().
 
131
 */
 
132
 
 
133
static int de600_close(struct net_device *dev)
 
134
{
 
135
        select_nic();
 
136
        rx_page = 0;
 
137
        de600_put_command(RESET);
 
138
        de600_put_command(STOP_RESET);
 
139
        de600_put_command(0);
 
140
        select_prn();
 
141
        free_irq(DE600_IRQ, dev);
 
142
        return 0;
 
143
}
 
144
 
 
145
static inline void trigger_interrupt(struct net_device *dev)
 
146
{
 
147
        de600_put_command(FLIP_IRQ);
 
148
        select_prn();
 
149
        DE600_SLOW_DOWN;
 
150
        select_nic();
 
151
        de600_put_command(0);
 
152
}
 
153
 
 
154
/*
 
155
 * Copy a buffer to the adapter transmit page memory.
 
156
 * Start sending.
 
157
 */
 
158
 
 
159
static int de600_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
160
{
 
161
        unsigned long flags;
 
162
        int     transmit_from;
 
163
        int     len;
 
164
        int     tickssofar;
 
165
        u8      *buffer = skb->data;
 
166
        int     i;
 
167
 
 
168
        if (free_tx_pages <= 0) {       /* Do timeouts, to avoid hangs. */
 
169
                tickssofar = jiffies - dev_trans_start(dev);
 
170
                if (tickssofar < HZ/20)
 
171
                        return NETDEV_TX_BUSY;
 
172
                /* else */
 
173
                printk(KERN_WARNING "%s: transmit timed out (%d), %s?\n", dev->name, tickssofar, "network cable problem");
 
174
                /* Restart the adapter. */
 
175
                spin_lock_irqsave(&de600_lock, flags);
 
176
                if (adapter_init(dev)) {
 
177
                        spin_unlock_irqrestore(&de600_lock, flags);
 
178
                        return NETDEV_TX_BUSY;
 
179
                }
 
180
                spin_unlock_irqrestore(&de600_lock, flags);
 
181
        }
 
182
 
 
183
        /* Start real output */
 
184
        pr_debug("de600_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages);
 
185
 
 
186
        if ((len = skb->len) < RUNT)
 
187
                len = RUNT;
 
188
 
 
189
        spin_lock_irqsave(&de600_lock, flags);
 
190
        select_nic();
 
191
        tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len;
 
192
        tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */
 
193
 
 
194
        if(check_lost)
 
195
        {
 
196
                /* This costs about 40 instructions per packet... */
 
197
                de600_setup_address(NODE_ADDRESS, RW_ADDR);
 
198
                de600_read_byte(READ_DATA, dev);
 
199
                if (was_down || (de600_read_byte(READ_DATA, dev) != 0xde)) {
 
200
                        if (adapter_init(dev)) {
 
201
                                spin_unlock_irqrestore(&de600_lock, flags);
 
202
                                return NETDEV_TX_BUSY;
 
203
                        }
 
204
                }
 
205
        }
 
206
 
 
207
        de600_setup_address(transmit_from, RW_ADDR);
 
208
        for (i = 0;  i < skb->len ; ++i, ++buffer)
 
209
                de600_put_byte(*buffer);
 
210
        for (; i < len; ++i)
 
211
                de600_put_byte(0);
 
212
 
 
213
        if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */
 
214
                dev->trans_start = jiffies;
 
215
                netif_start_queue(dev); /* allow more packets into adapter */
 
216
                /* Send page and generate a faked interrupt */
 
217
                de600_setup_address(transmit_from, TX_ADDR);
 
218
                de600_put_command(TX_ENABLE);
 
219
        }
 
220
        else {
 
221
                if (free_tx_pages)
 
222
                        netif_start_queue(dev);
 
223
                else
 
224
                        netif_stop_queue(dev);
 
225
                select_prn();
 
226
        }
 
227
        spin_unlock_irqrestore(&de600_lock, flags);
 
228
        dev_kfree_skb(skb);
 
229
        return NETDEV_TX_OK;
 
230
}
 
231
 
 
232
/*
 
233
 * The typical workload of the driver:
 
234
 * Handle the network interface interrupts.
 
235
 */
 
236
 
 
237
static irqreturn_t de600_interrupt(int irq, void *dev_id)
 
238
{
 
239
        struct net_device       *dev = dev_id;
 
240
        u8              irq_status;
 
241
        int             retrig = 0;
 
242
        int             boguscount = 0;
 
243
 
 
244
        spin_lock(&de600_lock);
 
245
 
 
246
        select_nic();
 
247
        irq_status = de600_read_status(dev);
 
248
 
 
249
        do {
 
250
                pr_debug("de600_interrupt (%02X)\n", irq_status);
 
251
 
 
252
                if (irq_status & RX_GOOD)
 
253
                        de600_rx_intr(dev);
 
254
                else if (!(irq_status & RX_BUSY))
 
255
                        de600_put_command(RX_ENABLE);
 
256
 
 
257
                /* Any transmission in progress? */
 
258
                if (free_tx_pages < TX_PAGES)
 
259
                        retrig = de600_tx_intr(dev, irq_status);
 
260
                else
 
261
                        retrig = 0;
 
262
 
 
263
                irq_status = de600_read_status(dev);
 
264
        } while ( (irq_status & RX_GOOD) || ((++boguscount < 100) && retrig) );
 
265
        /*
 
266
         * Yeah, it _looks_ like busy waiting, smells like busy waiting
 
267
         * and I know it's not PC, but please, it will only occur once
 
268
         * in a while and then only for a loop or so (< 1ms for sure!)
 
269
         */
 
270
 
 
271
        /* Enable adapter interrupts */
 
272
        select_prn();
 
273
        if (retrig)
 
274
                trigger_interrupt(dev);
 
275
        spin_unlock(&de600_lock);
 
276
        return IRQ_HANDLED;
 
277
}
 
278
 
 
279
static int de600_tx_intr(struct net_device *dev, int irq_status)
 
280
{
 
281
        /*
 
282
         * Returns 1 if tx still not done
 
283
         */
 
284
 
 
285
        /* Check if current transmission is done yet */
 
286
        if (irq_status & TX_BUSY)
 
287
                return 1; /* tx not done, try again */
 
288
 
 
289
        /* else */
 
290
        /* If last transmission OK then bump fifo index */
 
291
        if (!(irq_status & TX_FAILED16)) {
 
292
                tx_fifo_out = (tx_fifo_out + 1) % TX_PAGES;
 
293
                ++free_tx_pages;
 
294
                dev->stats.tx_packets++;
 
295
                netif_wake_queue(dev);
 
296
        }
 
297
 
 
298
        /* More to send, or resend last packet? */
 
299
        if ((free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) {
 
300
                dev->trans_start = jiffies;
 
301
                de600_setup_address(tx_fifo[tx_fifo_out], TX_ADDR);
 
302
                de600_put_command(TX_ENABLE);
 
303
                return 1;
 
304
        }
 
305
        /* else */
 
306
 
 
307
        return 0;
 
308
}
 
309
 
 
310
/*
 
311
 * We have a good packet, get it out of the adapter.
 
312
 */
 
313
static void de600_rx_intr(struct net_device *dev)
 
314
{
 
315
        struct sk_buff  *skb;
 
316
        int             i;
 
317
        int             read_from;
 
318
        int             size;
 
319
        unsigned char   *buffer;
 
320
 
 
321
        /* Get size of received packet */
 
322
        size = de600_read_byte(RX_LEN, dev);    /* low byte */
 
323
        size += (de600_read_byte(RX_LEN, dev) << 8);    /* high byte */
 
324
        size -= 4;      /* Ignore trailing 4 CRC-bytes */
 
325
 
 
326
        /* Tell adapter where to store next incoming packet, enable receiver */
 
327
        read_from = rx_page_adr();
 
328
        next_rx_page();
 
329
        de600_put_command(RX_ENABLE);
 
330
 
 
331
        if ((size < 32)  ||  (size > 1535)) {
 
332
                printk(KERN_WARNING "%s: Bogus packet size %d.\n", dev->name, size);
 
333
                if (size > 10000)
 
334
                        adapter_init(dev);
 
335
                return;
 
336
        }
 
337
 
 
338
        skb = dev_alloc_skb(size+2);
 
339
        if (skb == NULL) {
 
340
                printk("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, size);
 
341
                return;
 
342
        }
 
343
        /* else */
 
344
 
 
345
        skb_reserve(skb,2);     /* Align */
 
346
 
 
347
        /* 'skb->data' points to the start of sk_buff data area. */
 
348
        buffer = skb_put(skb,size);
 
349
 
 
350
        /* copy the packet into the buffer */
 
351
        de600_setup_address(read_from, RW_ADDR);
 
352
        for (i = size; i > 0; --i, ++buffer)
 
353
                *buffer = de600_read_byte(READ_DATA, dev);
 
354
 
 
355
        skb->protocol=eth_type_trans(skb,dev);
 
356
 
 
357
        netif_rx(skb);
 
358
 
 
359
        /* update stats */
 
360
        dev->stats.rx_packets++; /* count all receives */
 
361
        dev->stats.rx_bytes += size; /* count all received bytes */
 
362
 
 
363
        /*
 
364
         * If any worth-while packets have been received, netif_rx()
 
365
         * will work on them when we get to the tasklets.
 
366
         */
 
367
}
 
368
 
 
369
static const struct net_device_ops de600_netdev_ops = {
 
370
        .ndo_open               = de600_open,
 
371
        .ndo_stop               = de600_close,
 
372
        .ndo_start_xmit         = de600_start_xmit,
 
373
        .ndo_change_mtu         = eth_change_mtu,
 
374
        .ndo_set_mac_address    = eth_mac_addr,
 
375
        .ndo_validate_addr      = eth_validate_addr,
 
376
};
 
377
 
 
378
 
 
379
static struct net_device * __init de600_probe(void)
 
380
{
 
381
        int     i;
 
382
        struct net_device *dev;
 
383
        int err;
 
384
 
 
385
        dev = alloc_etherdev(0);
 
386
        if (!dev)
 
387
                return ERR_PTR(-ENOMEM);
 
388
 
 
389
 
 
390
        if (!request_region(DE600_IO, 3, "de600")) {
 
391
                printk(KERN_WARNING "DE600: port 0x%x busy\n", DE600_IO);
 
392
                err = -EBUSY;
 
393
                goto out;
 
394
        }
 
395
 
 
396
        printk(KERN_INFO "%s: D-Link DE-600 pocket adapter", dev->name);
 
397
        /* Alpha testers must have the version number to report bugs. */
 
398
        pr_debug("%s", version);
 
399
 
 
400
        /* probe for adapter */
 
401
        err = -ENODEV;
 
402
        rx_page = 0;
 
403
        select_nic();
 
404
        (void)de600_read_status(dev);
 
405
        de600_put_command(RESET);
 
406
        de600_put_command(STOP_RESET);
 
407
        if (de600_read_status(dev) & 0xf0) {
 
408
                printk(": not at I/O %#3x.\n", DATA_PORT);
 
409
                goto out1;
 
410
        }
 
411
 
 
412
        /*
 
413
         * Maybe we found one,
 
414
         * have to check if it is a D-Link DE-600 adapter...
 
415
         */
 
416
 
 
417
        /* Get the adapter ethernet address from the ROM */
 
418
        de600_setup_address(NODE_ADDRESS, RW_ADDR);
 
419
        for (i = 0; i < ETH_ALEN; i++) {
 
420
                dev->dev_addr[i] = de600_read_byte(READ_DATA, dev);
 
421
                dev->broadcast[i] = 0xff;
 
422
        }
 
423
 
 
424
        /* Check magic code */
 
425
        if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) {
 
426
                /* OK, install real address */
 
427
                dev->dev_addr[0] = 0x00;
 
428
                dev->dev_addr[1] = 0x80;
 
429
                dev->dev_addr[2] = 0xc8;
 
430
                dev->dev_addr[3] &= 0x0f;
 
431
                dev->dev_addr[3] |= 0x70;
 
432
        } else {
 
433
                printk(" not identified in the printer port\n");
 
434
                goto out1;
 
435
        }
 
436
 
 
437
        printk(", Ethernet Address: %pM\n", dev->dev_addr);
 
438
 
 
439
        dev->netdev_ops = &de600_netdev_ops;
 
440
 
 
441
        dev->flags&=~IFF_MULTICAST;
 
442
 
 
443
        select_prn();
 
444
 
 
445
        err = register_netdev(dev);
 
446
        if (err)
 
447
                goto out1;
 
448
 
 
449
        return dev;
 
450
 
 
451
out1:
 
452
        release_region(DE600_IO, 3);
 
453
out:
 
454
        free_netdev(dev);
 
455
        return ERR_PTR(err);
 
456
}
 
457
 
 
458
static int adapter_init(struct net_device *dev)
 
459
{
 
460
        int     i;
 
461
 
 
462
        select_nic();
 
463
        rx_page = 0; /* used by RESET */
 
464
        de600_put_command(RESET);
 
465
        de600_put_command(STOP_RESET);
 
466
 
 
467
        /* Check if it is still there... */
 
468
        /* Get the some bytes of the adapter ethernet address from the ROM */
 
469
        de600_setup_address(NODE_ADDRESS, RW_ADDR);
 
470
        de600_read_byte(READ_DATA, dev);
 
471
        if ((de600_read_byte(READ_DATA, dev) != 0xde) ||
 
472
            (de600_read_byte(READ_DATA, dev) != 0x15)) {
 
473
        /* was: if (de600_read_status(dev) & 0xf0) { */
 
474
                printk("Something has happened to the DE-600!  Please check it and do a new ifconfig!\n");
 
475
                /* Goodbye, cruel world... */
 
476
                dev->flags &= ~IFF_UP;
 
477
                de600_close(dev);
 
478
                was_down = 1;
 
479
                netif_stop_queue(dev); /* Transmit busy...  */
 
480
                return 1; /* failed */
 
481
        }
 
482
 
 
483
        if (was_down) {
 
484
                printk(KERN_INFO "%s: Thanks, I feel much better now!\n", dev->name);
 
485
                was_down = 0;
 
486
        }
 
487
 
 
488
        tx_fifo_in = 0;
 
489
        tx_fifo_out = 0;
 
490
        free_tx_pages = TX_PAGES;
 
491
 
 
492
 
 
493
        /* set the ether address. */
 
494
        de600_setup_address(NODE_ADDRESS, RW_ADDR);
 
495
        for (i = 0; i < ETH_ALEN; i++)
 
496
                de600_put_byte(dev->dev_addr[i]);
 
497
 
 
498
        /* where to start saving incoming packets */
 
499
        rx_page = RX_BP | RX_BASE_PAGE;
 
500
        de600_setup_address(MEM_4K, RW_ADDR);
 
501
        /* Enable receiver */
 
502
        de600_put_command(RX_ENABLE);
 
503
        select_prn();
 
504
 
 
505
        netif_start_queue(dev);
 
506
 
 
507
        return 0; /* OK */
 
508
}
 
509
 
 
510
static struct net_device *de600_dev;
 
511
 
 
512
static int __init de600_init(void)
 
513
{
 
514
        de600_dev = de600_probe();
 
515
        if (IS_ERR(de600_dev))
 
516
                return PTR_ERR(de600_dev);
 
517
        return 0;
 
518
}
 
519
 
 
520
static void __exit de600_exit(void)
 
521
{
 
522
        unregister_netdev(de600_dev);
 
523
        release_region(DE600_IO, 3);
 
524
        free_netdev(de600_dev);
 
525
}
 
526
 
 
527
module_init(de600_init);
 
528
module_exit(de600_exit);
 
529
 
 
530
MODULE_LICENSE("GPL");