linux-bk/drivers/net/am79c961a.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/net/am79c961.c
   3 *
   4 *  by Russell King <rmk@arm.linux.org.uk> 1995-2001.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * Derived from various things including skeleton.c
  11 *
  12 * This is a special driver for the am79c961A Lance chip used in the
  13 * Intel (formally Digital Equipment Corp) EBSA110 platform.  Please
  14 * note that this can not be built as a module (it doesn't make sense).
  15 */
  16#include <linux/kernel.h>
  17#include <linux/sched.h>
  18#include <linux/types.h>
  19#include <linux/fcntl.h>
  20#include <linux/interrupt.h>
  21#include <linux/ioport.h>
  22#include <linux/in.h>
  23#include <linux/slab.h>
  24#include <linux/string.h>
  25#include <linux/errno.h>
  26#include <linux/netdevice.h>
  27#include <linux/etherdevice.h>
  28#include <linux/skbuff.h>
  29#include <linux/delay.h>
  30#include <linux/init.h>
  31#include <linux/crc32.h>
  32
  33#include <asm/system.h>
  34#include <asm/bitops.h>
  35#include <asm/irq.h>
  36#include <asm/io.h>
  37#include <asm/dma.h>
  38
  39#define TX_BUFFERS 15
  40#define RX_BUFFERS 25
  41
  42#include "am79c961a.h"
  43
  44static void am79c961_interrupt (int irq, void *dev_id, struct pt_regs *regs);
  45
  46static unsigned int net_debug = NET_DEBUG;
  47
  48static const char version[] =
  49        "am79c961 ethernet driver (C) 1995-2001 Russell King v0.04\n";
  50
  51/* --------------------------------------------------------------------------- */
  52
  53#ifdef __arm__
  54static void write_rreg(u_long base, u_int reg, u_int val)
  55{
  56        __asm__("str%?h %1, [%2]        @ NET_RAP
  57                str%?h  %0, [%2, #-4]   @ NET_RDP
  58                " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
  59}
  60
  61static inline unsigned short read_rreg(u_long base_addr, u_int reg)
  62{
  63        unsigned short v;
  64        __asm__("str%?h %1, [%2]        @ NET_RAP
  65                ldr%?h  %0, [%2, #-4]   @ NET_RDP
  66                " : "=r" (v): "r" (reg), "r" (ISAIO_BASE + 0x0464));
  67        return v;
  68}
  69
  70static inline void write_ireg(u_long base, u_int reg, u_int val)
  71{
  72        __asm__("str%?h %1, [%2]        @ NET_RAP
  73                str%?h  %0, [%2, #8]    @ NET_IDP
  74                " : : "r" (val), "r" (reg), "r" (ISAIO_BASE + 0x0464));
  75}
  76
  77#define am_writeword(dev,off,val) __raw_writew(val, ISAMEM_BASE + ((off) << 1))
  78#define am_readword(dev,off)      __raw_readw(ISAMEM_BASE + ((off) << 1))
  79
  80static inline void
  81am_writebuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
  82{
  83        offset = ISAMEM_BASE + (offset << 1);
  84        length = (length + 1) & ~1;
  85        if ((int)buf & 2) {
  86                __asm__ __volatile__("str%?h    %2, [%0], #4"
  87                 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
  88                buf += 2;
  89                length -= 2;
  90        }
  91        while (length > 8) {
  92                unsigned int tmp, tmp2;
  93                __asm__ __volatile__("
  94                        ldm%?ia %1!, {%2, %3}
  95                        str%?h  %2, [%0], #4
  96                        mov%?   %2, %2, lsr #16
  97                        str%?h  %2, [%0], #4
  98                        str%?h  %3, [%0], #4
  99                        mov%?   %3, %3, lsr #16
 100                        str%?h  %3, [%0], #4
 101                " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2)
 102                  : "0" (offset), "1" (buf));
 103                length -= 8;
 104        }
 105        while (length > 0) {
 106                __asm__ __volatile__("str%?h    %2, [%0], #4"
 107                 : "=&r" (offset) : "0" (offset), "r" (buf[0] | (buf[1] << 8)));
 108                buf += 2;
 109                length -= 2;
 110        }
 111}
 112
 113static inline void
 114am_readbuffer(struct net_device *dev, u_int offset, unsigned char *buf, unsigned int length)
 115{
 116        offset = ISAMEM_BASE + (offset << 1);
 117        length = (length + 1) & ~1;
 118        if ((int)buf & 2) {
 119                unsigned int tmp;
 120                __asm__ __volatile__("
 121                        ldr%?h  %2, [%0], #4
 122                        str%?b  %2, [%1], #1
 123                        mov%?   %2, %2, lsr #8
 124                        str%?b  %2, [%1], #1
 125                " : "=&r" (offset), "=&r" (buf), "=r" (tmp): "0" (offset), "1" (buf));
 126                length -= 2;
 127        }
 128        while (length > 8) {
 129                unsigned int tmp, tmp2, tmp3;
 130                __asm__ __volatile__("
 131                        ldr%?h  %2, [%0], #4
 132                        ldr%?h  %3, [%0], #4
 133                        orr%?   %2, %2, %3, lsl #16
 134                        ldr%?h  %3, [%0], #4
 135                        ldr%?h  %4, [%0], #4
 136                        orr%?   %3, %3, %4, lsl #16
 137                        stm%?ia %1!, {%2, %3}
 138                " : "=&r" (offset), "=&r" (buf), "=r" (tmp), "=r" (tmp2), "=r" (tmp3)
 139                  : "0" (offset), "1" (buf));
 140                length -= 8;
 141        }
 142        while (length > 0) {
 143                unsigned int tmp;
 144                __asm__ __volatile__("
 145                        ldr%?h  %2, [%0], #4
 146                        str%?b  %2, [%1], #1
 147                        mov%?   %2, %2, lsr #8
 148                        str%?b  %2, [%1], #1
 149                " : "=&r" (offset), "=&r" (buf), "=r" (tmp) : "0" (offset), "1" (buf));
 150                length -= 2;
 151        }
 152}
 153#else
 154#error Not compatable
 155#endif
 156
 157static int
 158am79c961_ramtest(struct net_device *dev, unsigned int val)
 159{
 160        unsigned char *buffer = kmalloc (65536, GFP_KERNEL);
 161        int i, error = 0, errorcount = 0;
 162
 163        if (!buffer)
 164                return 0;
 165        memset (buffer, val, 65536);
 166        am_writebuffer(dev, 0, buffer, 65536);
 167        memset (buffer, val ^ 255, 65536);
 168        am_readbuffer(dev, 0, buffer, 65536);
 169        for (i = 0; i < 65536; i++) {
 170                if (buffer[i] != val && !error) {
 171                        printk ("%s: buffer error (%02X %02X) %05X - ", dev->name, val, buffer[i], i);
 172                        error = 1;
 173                        errorcount ++;
 174                } else if (error && buffer[i] == val) {
 175                        printk ("%05X\n", i);
 176                        error = 0;
 177                }
 178        }
 179        if (error)
 180                printk ("10000\n");
 181        kfree (buffer);
 182        return errorcount;
 183}
 184
 185static void
 186am79c961_init_for_open(struct net_device *dev)
 187{
 188        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 189        unsigned long flags;
 190        unsigned char *p;
 191        u_int hdr_addr, first_free_addr;
 192        int i;
 193
 194        /*
 195         * Stop the chip.
 196         */
 197        spin_lock_irqsave(priv->chip_lock, flags);
 198        write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP);
 199        spin_unlock_irqrestore(priv->chip_lock, flags);
 200
 201        write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */
 202        write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */
 203        write_ireg (dev->base_addr, 7, 0x0090); /* XMIT LED */
 204        write_ireg (dev->base_addr, 2, 0x0000); /* MODE register selects media */
 205
 206        for (i = LADRL; i <= LADRH; i++)
 207                write_rreg (dev->base_addr, i, 0);
 208
 209        for (i = PADRL, p = dev->dev_addr; i <= PADRH; i++, p += 2)
 210                write_rreg (dev->base_addr, i, p[0] | (p[1] << 8));
 211
 212        i = MODE_PORT_10BT;
 213        if (dev->flags & IFF_PROMISC)
 214                i |= MODE_PROMISC;
 215
 216        write_rreg (dev->base_addr, MODE, i);
 217        write_rreg (dev->base_addr, POLLINT, 0);
 218        write_rreg (dev->base_addr, SIZERXR, -RX_BUFFERS);
 219        write_rreg (dev->base_addr, SIZETXR, -TX_BUFFERS);
 220
 221        first_free_addr = RX_BUFFERS * 8 + TX_BUFFERS * 8 + 16;
 222        hdr_addr = 0;
 223
 224        priv->rxhead = 0;
 225        priv->rxtail = 0;
 226        priv->rxhdr = hdr_addr;
 227
 228        for (i = 0; i < RX_BUFFERS; i++) {
 229                priv->rxbuffer[i] = first_free_addr;
 230                am_writeword (dev, hdr_addr, first_free_addr);
 231                am_writeword (dev, hdr_addr + 2, RMD_OWN);
 232                am_writeword (dev, hdr_addr + 4, (-1600));
 233                am_writeword (dev, hdr_addr + 6, 0);
 234                first_free_addr += 1600;
 235                hdr_addr += 8;
 236        }
 237        priv->txhead = 0;
 238        priv->txtail = 0;
 239        priv->txhdr = hdr_addr;
 240        for (i = 0; i < TX_BUFFERS; i++) {
 241                priv->txbuffer[i] = first_free_addr;
 242                am_writeword (dev, hdr_addr, first_free_addr);
 243                am_writeword (dev, hdr_addr + 2, TMD_STP|TMD_ENP);
 244                am_writeword (dev, hdr_addr + 4, 0xf000);
 245                am_writeword (dev, hdr_addr + 6, 0);
 246                first_free_addr += 1600;
 247                hdr_addr += 8;
 248        }
 249
 250        write_rreg (dev->base_addr, BASERXL, priv->rxhdr);
 251        write_rreg (dev->base_addr, BASERXH, 0);
 252        write_rreg (dev->base_addr, BASETXL, priv->txhdr);
 253        write_rreg (dev->base_addr, BASERXH, 0);
 254        write_rreg (dev->base_addr, CSR0, CSR0_STOP);
 255        write_rreg (dev->base_addr, CSR3, CSR3_IDONM|CSR3_BABLM|CSR3_DXSUFLO);
 256        write_rreg (dev->base_addr, CSR0, CSR0_IENA|CSR0_STRT);
 257}
 258
 259/*
 260 * Open/initialize the board.
 261 */
 262static int
 263am79c961_open(struct net_device *dev)
 264{
 265        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 266        int ret;
 267
 268        memset (&priv->stats, 0, sizeof (priv->stats));
 269
 270        ret = request_irq(dev->irq, am79c961_interrupt, 0, dev->name, dev);
 271        if (ret)
 272                return ret;
 273
 274        am79c961_init_for_open(dev);
 275
 276        netif_start_queue(dev);
 277
 278        return 0;
 279}
 280
 281/*
 282 * The inverse routine to am79c961_open().
 283 */
 284static int
 285am79c961_close(struct net_device *dev)
 286{
 287        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 288        unsigned long flags;
 289
 290        netif_stop_queue(dev);
 291
 292        spin_lock_irqsave(priv->chip_lock, flags);
 293        write_rreg (dev->base_addr, CSR0, CSR0_STOP);
 294        write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
 295        spin_unlock_irqrestore(priv->chip_lock, flags);
 296
 297        free_irq (dev->irq, dev);
 298
 299        return 0;
 300}
 301
 302/*
 303 * Get the current statistics.
 304 */
 305static struct net_device_stats *am79c961_getstats (struct net_device *dev)
 306{
 307        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 308        return &priv->stats;
 309}
 310
 311static void am79c961_mc_hash(struct dev_mc_list *dmi, unsigned short *hash)
 312{
 313        if (dmi->dmi_addrlen == ETH_ALEN && dmi->dmi_addr[0] & 0x01) {
 314                int idx, bit;
 315                u32 crc;
 316
 317                crc = ether_crc_le(ETH_ALEN, dmi->dmi_addr);
 318
 319                idx = crc >> 30;
 320                bit = (crc >> 26) & 15;
 321
 322                hash[idx] |= 1 << bit;
 323        }
 324}
 325
 326/*
 327 * Set or clear promiscuous/multicast mode filter for this adapter.
 328 */
 329static void am79c961_setmulticastlist (struct net_device *dev)
 330{
 331        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 332        unsigned long flags;
 333        unsigned short multi_hash[4], mode;
 334        int i, stopped;
 335
 336        mode = MODE_PORT_10BT;
 337
 338        if (dev->flags & IFF_PROMISC) {
 339                mode |= MODE_PROMISC;
 340        } else if (dev->flags & IFF_ALLMULTI) {
 341                memset(multi_hash, 0xff, sizeof(multi_hash));
 342        } else {
 343                struct dev_mc_list *dmi;
 344
 345                memset(multi_hash, 0x00, sizeof(multi_hash));
 346
 347                for (dmi = dev->mc_list; dmi; dmi = dmi->next)
 348                        am79c961_mc_hash(dmi, multi_hash);
 349        }
 350
 351        spin_lock_irqsave(priv->chip_lock, flags);
 352
 353        stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP;
 354
 355        if (!stopped) {
 356                /*
 357                 * Put the chip into suspend mode
 358                 */
 359                write_rreg(dev->base_addr, CTRL1, CTRL1_SPND);
 360
 361                /*
 362                 * Spin waiting for chip to report suspend mode
 363                 */
 364                while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) {
 365                        spin_unlock_irqrestore(priv->chip_lock, flags);
 366                        nop();
 367                        spin_lock_irqsave(priv->chip_lock, flags);
 368                }
 369        }
 370
 371        /*
 372         * Update the multicast hash table
 373         */
 374        for (i = 0; i < sizeof(multi_hash) / sizeof(multi_hash[0]); i++)
 375                write_rreg(dev->base_addr, i + LADRL, multi_hash[i]);
 376
 377        /*
 378         * Write the mode register
 379         */
 380        write_rreg(dev->base_addr, MODE, mode);
 381
 382        if (!stopped) {
 383                /*
 384                 * Put the chip back into running mode
 385                 */
 386                write_rreg(dev->base_addr, CTRL1, 0);
 387        }
 388
 389        spin_unlock_irqrestore(priv->chip_lock, flags);
 390}
 391
 392static void am79c961_timeout(struct net_device *dev)
 393{
 394        printk(KERN_WARNING "%s: transmit timed out, network cable problem?\n",
 395                dev->name);
 396
 397        /*
 398         * ought to do some setup of the tx side here
 399         */
 400
 401        netif_wake_queue(dev);
 402}
 403
 404/*
 405 * Transmit a packet
 406 */
 407static int
 408am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev)
 409{
 410        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 411        unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
 412        unsigned int hdraddr, bufaddr;
 413        unsigned int head;
 414        unsigned long flags;
 415
 416        head = priv->txhead;
 417        hdraddr = priv->txhdr + (head << 3);
 418        bufaddr = priv->txbuffer[head];
 419        head += 1;
 420        if (head >= TX_BUFFERS)
 421                head = 0;
 422
 423        am_writebuffer (dev, bufaddr, skb->data, length);
 424        am_writeword (dev, hdraddr + 4, -length);
 425        am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP);
 426        priv->txhead = head;
 427
 428        spin_lock_irqsave(priv->chip_lock, flags);
 429        write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA);
 430        dev->trans_start = jiffies;
 431        spin_unlock_irqrestore(priv->chip_lock, flags);
 432
 433        /*
 434         * If the next packet is owned by the ethernet device,
 435         * then the tx ring is full and we can't add another
 436         * packet.
 437         */
 438        if (am_readword(dev, priv->txhdr + (priv->txhead << 3) + 2) & TMD_OWN)
 439                netif_stop_queue(dev);
 440
 441        dev_kfree_skb(skb);
 442
 443        return 0;
 444}
 445
 446/*
 447 * If we have a good packet(s), get it/them out of the buffers.
 448 */
 449static void
 450am79c961_rx(struct net_device *dev, struct dev_priv *priv)
 451{
 452        do {
 453                struct sk_buff *skb;
 454                u_int hdraddr;
 455                u_int pktaddr;
 456                u_int status;
 457                int len;
 458
 459                hdraddr = priv->rxhdr + (priv->rxtail << 3);
 460                pktaddr = priv->rxbuffer[priv->rxtail];
 461
 462                status = am_readword (dev, hdraddr + 2);
 463                if (status & RMD_OWN) /* do we own it? */
 464                        break;
 465
 466                priv->rxtail ++;
 467                if (priv->rxtail >= RX_BUFFERS)
 468                        priv->rxtail = 0;
 469
 470                if ((status & (RMD_ERR|RMD_STP|RMD_ENP)) != (RMD_STP|RMD_ENP)) {
 471                        am_writeword (dev, hdraddr + 2, RMD_OWN);
 472                        priv->stats.rx_errors ++;
 473                        if (status & RMD_ERR) {
 474                                if (status & RMD_FRAM)
 475                                        priv->stats.rx_frame_errors ++;
 476                                if (status & RMD_CRC)
 477                                        priv->stats.rx_crc_errors ++;
 478                        } else if (status & RMD_STP)
 479                                priv->stats.rx_length_errors ++;
 480                        continue;
 481                }
 482
 483                len = am_readword(dev, hdraddr + 6);
 484                skb = dev_alloc_skb(len + 2);
 485
 486                if (skb) {
 487                        skb->dev = dev;
 488                        skb_reserve(skb, 2);
 489
 490                        am_readbuffer(dev, pktaddr, skb_put(skb, len), len);
 491                        am_writeword(dev, hdraddr + 2, RMD_OWN);
 492                        skb->protocol = eth_type_trans(skb, dev);
 493                        netif_rx(skb);
 494                        dev->last_rx = jiffies;
 495                        priv->stats.rx_bytes += len;
 496                        priv->stats.rx_packets ++;
 497                } else {
 498                        am_writeword (dev, hdraddr + 2, RMD_OWN);
 499                        printk (KERN_WARNING "%s: memory squeeze, dropping packet.\n", dev->name);
 500                        priv->stats.rx_dropped ++;
 501                        break;
 502                }
 503        } while (1);
 504}
 505
 506/*
 507 * Update stats for the transmitted packet
 508 */
 509static void
 510am79c961_tx(struct net_device *dev, struct dev_priv *priv)
 511{
 512        do {
 513                u_int hdraddr;
 514                u_int status;
 515
 516                hdraddr = priv->txhdr + (priv->txtail << 3);
 517                status = am_readword (dev, hdraddr + 2);
 518                if (status & TMD_OWN)
 519                        break;
 520
 521                priv->txtail ++;
 522                if (priv->txtail >= TX_BUFFERS)
 523                        priv->txtail = 0;
 524
 525                if (status & TMD_ERR) {
 526                        u_int status2;
 527
 528                        priv->stats.tx_errors ++;
 529
 530                        status2 = am_readword (dev, hdraddr + 6);
 531
 532                        /*
 533                         * Clear the error byte
 534                         */
 535                        am_writeword (dev, hdraddr + 6, 0);
 536
 537                        if (status2 & TST_RTRY)
 538                                priv->stats.collisions += 16;
 539                        if (status2 & TST_LCOL)
 540                                priv->stats.tx_window_errors ++;
 541                        if (status2 & TST_LCAR)
 542                                priv->stats.tx_carrier_errors ++;
 543                        if (status2 & TST_UFLO)
 544                                priv->stats.tx_fifo_errors ++;
 545                        continue;
 546                }
 547                priv->stats.tx_packets ++;
 548        } while (priv->txtail != priv->txhead);
 549
 550        netif_wake_queue(dev);
 551}
 552
 553static void
 554am79c961_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 555{
 556        struct net_device *dev = (struct net_device *)dev_id;
 557        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 558        u_int status;
 559
 560        status = read_rreg(dev->base_addr, CSR0);
 561        write_rreg(dev->base_addr, CSR0, status & (CSR0_TINT|CSR0_RINT|CSR0_MISS|CSR0_IENA));
 562
 563        if (status & CSR0_RINT)
 564                am79c961_rx(dev, priv);
 565        if (status & CSR0_TINT)
 566                am79c961_tx(dev, priv);
 567        if (status & CSR0_MISS)
 568                priv->stats.rx_dropped ++;
 569}
 570
 571/*
 572 * Initialise the chip.  Note that we always expect
 573 * to be entered with interrupts enabled.
 574 */
 575static int
 576am79c961_hw_init(struct net_device *dev)
 577{
 578        struct dev_priv *priv = (struct dev_priv *)dev->priv;
 579
 580        spin_lock_irq(priv->chip_lock);
 581        write_rreg (dev->base_addr, CSR0, CSR0_STOP);
 582        write_rreg (dev->base_addr, CSR3, CSR3_MASKALL);
 583        spin_unlock_irq(priv->chip_lock);
 584
 585        am79c961_ramtest(dev, 0x66);
 586        am79c961_ramtest(dev, 0x99);
 587
 588        return 0;
 589}
 590
 591static void __init am79c961_banner(void)
 592{
 593        static unsigned version_printed;
 594
 595        if (net_debug && version_printed++ == 0)
 596                printk(KERN_INFO "%s", version);
 597}
 598
 599static int __init am79c961_init(void)
 600{
 601        struct net_device *dev;
 602        struct dev_priv *priv;
 603        int i, ret;
 604
 605        dev = init_etherdev(NULL, sizeof(struct dev_priv));
 606        ret = -ENOMEM;
 607        if (!dev)
 608                goto out;
 609
 610        priv = dev->priv;
 611
 612        /*
 613         * Fixed address and IRQ lines here.
 614         * The PNP initialisation should have been
 615         * done by the ether bootp loader.
 616         */
 617        dev->base_addr = 0x220;
 618        dev->irq = IRQ_EBSA110_ETHERNET;
 619
 620        /*
 621         * Reset the device.
 622         */
 623        inb(dev->base_addr + NET_RESET);
 624        udelay(5);
 625
 626        /*
 627         * Check the manufacturer part of the
 628         * ether address.
 629         */
 630        ret = -ENODEV;
 631        if (inb(dev->base_addr) != 0x08 ||
 632            inb(dev->base_addr + 2) != 0x00 ||
 633            inb(dev->base_addr + 4) != 0x2b)
 634                goto nodev;
 635
 636        if (!request_region(dev->base_addr, 0x18, dev->name))
 637                goto nodev;
 638
 639        am79c961_banner();
 640        printk(KERN_INFO "%s: ether address ", dev->name);
 641
 642        /* Retrive and print the ethernet address. */
 643        for (i = 0; i < 6; i++) {
 644                dev->dev_addr[i] = inb(dev->base_addr + i * 2) & 0xff;
 645                printk (i == 5 ? "%02x\n" : "%02x:", dev->dev_addr[i]);
 646        }
 647
 648        if (am79c961_hw_init(dev))
 649                goto release;
 650
 651        dev->open               = am79c961_open;
 652        dev->stop               = am79c961_close;
 653        dev->hard_start_xmit    = am79c961_sendpacket;
 654        dev->get_stats          = am79c961_getstats;
 655        dev->set_multicast_list = am79c961_setmulticastlist;
 656        dev->tx_timeout         = am79c961_timeout;
 657
 658        return 0;
 659
 660release:
 661        release_region(dev->base_addr, 0x18);
 662nodev:
 663        unregister_netdev(dev);
 664        kfree(dev);
 665out:
 666        return ret;
 667}
 668
 669__initcall(am79c961_init);
 670
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.