11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  proteon.c: A network driver for Proteon ISA token ring cards.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Based on tmspci written 1999 by Adam Fritzler
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Written 2003 by Jochen Friedrich
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  This software may be used and distributed according to the terms
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  of the GNU General Public License, incorporated herein by reference.
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  This driver module supports the following cards:
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	- Proteon 1392, 1392+
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Maintainer(s):
15726a645913694619876dd20645f88aad25cfbcd5Joe Perches *    AF        Adam Fritzler
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    JF	Jochen Friedrich	jochen@scram.de
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *  Modification History:
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	02-Jan-03	JF	Created
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char version[] = "proteon.c: v1.00 02/01/2003 by Jochen Friedrich\n";
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/trdevice.h>
32d052d1beff706920e82c5d55006b08e256b5df09Russell King#include <linux/platform_device.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/irq.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/pci.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dma.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "tms380tr.h"
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define PROTEON_IO_EXTENT 32
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A zero-terminated list of I/O addresses to be probed. */
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int portlist[] __initdata = {
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x0A20, 0x0E20, 0x1A20, 0x1E20, 0x2A20, 0x2E20, 0x3A20, 0x3E20,// Prot.
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x4A20, 0x4E20, 0x5A20, 0x5E20, 0x6A20, 0x6E20, 0x7A20, 0x7E20,// Prot.
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0x8A20, 0x8E20, 0x9A20, 0x9E20, 0xAA20, 0xAE20, 0xBA20, 0xBE20,// Prot.
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0xCA20, 0xCE20, 0xDA20, 0xDE20, 0xEA20, 0xEE20, 0xFA20, 0xFE20,// Prot.
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A zero-terminated list of IRQs to be probed. */
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short irqlist[] = {
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	7, 6, 5, 4, 3, 12, 11, 10, 9,
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* A zero-terminated list of DMAs to be probed. */
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dmalist[] __initdata = {
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	5, 6, 7,
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char cardname[] = "Proteon 1392\0";
65504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichstatic u64 dma_mask = ISA_MAX_ADDRESS;
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int proteon_open(struct net_device *dev);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void proteon_read_eeprom(struct net_device *dev);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short proteon_setnselout_pins(struct net_device *dev);
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short proteon_sifreadb(struct net_device *dev, unsigned short reg)
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return inb(dev->base_addr + reg);
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short proteon_sifreadw(struct net_device *dev, unsigned short reg)
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return inw(dev->base_addr + reg);
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void proteon_sifwriteb(struct net_device *dev, unsigned short val, unsigned short reg)
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(val, dev->base_addr + reg);
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void proteon_sifwritew(struct net_device *dev, unsigned short val, unsigned short reg)
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outw(val, dev->base_addr + reg);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init proteon_probe1(struct net_device *dev, int ioaddr)
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char chk1, chk2;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(ioaddr, PROTEON_IO_EXTENT, cardname))
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chk1 = inb(ioaddr + 0x1f);      /* Get Proteon ID reg 1 */
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (chk1 != 0x1f)
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto nodev;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chk1 = inb(ioaddr + 0x1e) & 0x07;       /* Get Proteon ID reg 0 */
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i=0; i<16; i++) {
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		chk2 = inb(ioaddr + 0x1e) & 0x07;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (((chk1 + 1) & 0x07) != chk2)
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto nodev;
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		chk1 = chk2;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->base_addr = ioaddr;
112807540baae406c84dcb9c1c8ef07a56d2d2ae84aEric Dumazet	return 0;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsnodev:
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(ioaddr, PROTEON_IO_EXTENT);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ENODEV;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11879f8ae3aa27f2f17a50ad1deee1e16ff02be01bbStephen Hemmingerstatic struct net_device_ops proteon_netdev_ops __read_mostly;
11979f8ae3aa27f2f17a50ad1deee1e16ff02be01bbStephen Hemminger
120504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichstatic int __init setup_card(struct net_device *dev, struct device *pdev)
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_local *tp;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        static int versionprinted;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const unsigned *port;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int j,err = 0;
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->base_addr)	/* probe specific location */
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = proteon_probe1(dev, dev->base_addr);
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else {
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (port = portlist; *port; port++) {
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			err = proteon_probe1(dev, *port);
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!err)
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
140504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich		goto out5;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* At this point we have found a valid card. */
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (versionprinted++ == 0)
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_DEBUG "%s", version);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = -EIO;
148504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich	pdev->dma_mask = &dma_mask;
14984c3ea01d163a24323d827e1d280dc3346905972Jochen Friedrich	if (tmsdev_init(dev, pdev))
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out4;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->base_addr &= ~3;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	proteon_read_eeprom(dev);
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
156e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	printk(KERN_DEBUG "proteon.c:    Ring Station Address: %pM\n",
157e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       dev->dev_addr);
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp = netdev_priv(dev);
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->setnselout = proteon_setnselout_pins;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->sifreadb = proteon_sifreadb;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->sifreadw = proteon_sifreadw;
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->sifwriteb = proteon_sifwriteb;
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->sifwritew = proteon_sifwritew;
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(tp->ProductID, cardname, PROD_ID_SIZE + 1);
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->tmspriv = NULL;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17179f8ae3aa27f2f17a50ad1deee1e16ff02be01bbStephen Hemminger	dev->netdev_ops = &proteon_netdev_ops;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->irq == 0)
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for(j = 0; irqlist[j] != 0; j++)
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->irq = irqlist[j];
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (!request_irq(dev->irq, tms380tr_interrupt, 0,
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				cardname, dev))
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                }
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if(irqlist[j] == 0)
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                {
185504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich                        printk(KERN_INFO "proteon.c: AutoSelect no IRQ available\n");
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out3;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for(j = 0; irqlist[j] != 0; j++)
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (irqlist[j] == dev->irq)
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (irqlist[j] == 0)
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
196504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich			printk(KERN_INFO "proteon.c: Illegal IRQ %d specified\n",
197504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich				dev->irq);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out3;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (request_irq(dev->irq, tms380tr_interrupt, 0,
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cardname, dev))
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
203504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich                        printk(KERN_INFO "proteon.c: Selected IRQ %d not available\n",
204504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich				dev->irq);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out3;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dev->dma == 0)
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for(j = 0; dmalist[j] != 0; j++)
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->dma = dmalist[j];
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        if (!request_dma(dev->dma, cardname))
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if(dmalist[j] == 0)
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
220504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich			printk(KERN_INFO "proteon.c: AutoSelect no DMA available\n");
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out2;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for(j = 0; dmalist[j] != 0; j++)
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (dmalist[j] == dev->dma)
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dmalist[j] == 0)
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
231504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich                        printk(KERN_INFO "proteon.c: Illegal DMA %d specified\n",
232504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich				dev->dma);
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out2;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (request_dma(dev->dma, cardname))
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
237504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich                        printk(KERN_INFO "proteon.c: Selected DMA %d not available\n",
238504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich				dev->dma);
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto out2;
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = register_netdev(dev);
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
247504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich	printk(KERN_DEBUG "%s:    IO: %#4lx  IRQ: %d  DMA: %d\n",
248504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich	       dev->name, dev->base_addr, dev->irq, dev->dma);
249504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_dma(dev->dma);
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout2:
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(dev->irq, dev);
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout3:
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tmsdev_term(dev);
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout4:
258504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich	release_region(dev->base_addr, PROTEON_IO_EXTENT);
259504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichout5:
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Reads MAC address from adapter RAM, which should've read it from
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the onboard ROM.
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Calling this on a board that does not support it can be a very
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * dangerous thing.  The Madge board, for instance, will lock your
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * machine hard when this is called.  Luckily, its supported in a
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * separate driver.  --ASF
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void proteon_read_eeprom(struct net_device *dev)
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Address: 0000:0000 */
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	proteon_sifwritew(dev, 0, SIFADX);
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	proteon_sifwritew(dev, 0, SIFADR);
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Read six byte MAC address data */
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->addr_len = 6;
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for(i = 0; i < 6; i++)
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->dev_addr[i] = proteon_sifreadw(dev, SIFINC) >> 8;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28627cd6ae5619084f363630683e77d70be38075afeHannes Ederstatic unsigned short proteon_setnselout_pins(struct net_device *dev)
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int proteon_open(struct net_device *dev)
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_local *tp = netdev_priv(dev);
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned short val = 0;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Proteon reset sequence */
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0, dev->base_addr + 0x11);
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mdelay(20);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x04, dev->base_addr + 0x11);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mdelay(20);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0, dev->base_addr + 0x11);
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mdelay(100);
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* set control/status reg */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = inb(dev->base_addr + 0x11);
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val |= 0x78;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val &= 0xf9;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(tp->DataRate == SPEED_4)
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		val |= 0x20;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		val &= ~0x20;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(val, dev->base_addr + 0x11);
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0xff, dev->base_addr + 0x12);
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for(i = 0; irqlist[i] != 0; i++)
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if(irqlist[i] == dev->irq)
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = i;
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	i = (7 - dev->dma) << 4;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val |= i;
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(val, dev->base_addr + 0x13);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return tms380tr_open(dev);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ISATR_MAX_ADAPTERS 3
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int io[ISATR_MAX_ADAPTERS];
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int irq[ISATR_MAX_ADAPTERS];
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dma[ISATR_MAX_ADAPTERS];
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(io, int, NULL, 0);
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(irq, int, NULL, 0);
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(dma, int, NULL, 0);
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichstatic struct platform_device *proteon_dev[ISATR_MAX_ADAPTERS];
342504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich
3433ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell Kingstatic struct platform_driver proteon_driver = {
3443ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	.driver		= {
3453ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King		.name	= "proteon",
3463ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	},
347504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich};
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
349504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichstatic int __init proteon_init(void)
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
352504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich	struct platform_device *pdev;
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, num = 0, err = 0;
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35579f8ae3aa27f2f17a50ad1deee1e16ff02be01bbStephen Hemminger	proteon_netdev_ops = tms380tr_netdev_ops;
35679f8ae3aa27f2f17a50ad1deee1e16ff02be01bbStephen Hemminger	proteon_netdev_ops.ndo_open = proteon_open;
35779f8ae3aa27f2f17a50ad1deee1e16ff02be01bbStephen Hemminger	proteon_netdev_ops.ndo_stop = tms380tr_close;
35879f8ae3aa27f2f17a50ad1deee1e16ff02be01bbStephen Hemminger
3593ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	err = platform_driver_register(&proteon_driver);
360504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich	if (err)
361504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich		return err;
362504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ISATR_MAX_ADAPTERS ; i++) {
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev = alloc_trdev(sizeof(struct net_local));
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!dev)
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->base_addr = io[i];
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->irq = irq[i];
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->dma = dma[i];
371504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich		pdev = platform_device_register_simple("proteon",
372504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich			i, NULL, 0);
3739d4df9e0fadfc84cd826e0f7e946691b4d7baee5Akinobu Mita		if (IS_ERR(pdev)) {
3749d4df9e0fadfc84cd826e0f7e946691b4d7baee5Akinobu Mita			free_netdev(dev);
3759d4df9e0fadfc84cd826e0f7e946691b4d7baee5Akinobu Mita			continue;
3769d4df9e0fadfc84cd826e0f7e946691b4d7baee5Akinobu Mita		}
377504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich		err = setup_card(dev, &pdev->dev);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!err) {
379504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich			proteon_dev[i] = pdev;
3803ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King			platform_set_drvdata(pdev, dev);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			++num;
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
383504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich			platform_device_unregister(pdev);
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_netdev(dev);
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_NOTICE "proteon.c: %d cards found.\n", num);
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Probe for cards. */
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (num == 0) {
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_NOTICE "proteon.c: No cards found.\n");
3929d4df9e0fadfc84cd826e0f7e946691b4d7baee5Akinobu Mita		platform_driver_unregister(&proteon_driver);
3939d4df9e0fadfc84cd826e0f7e946691b4d7baee5Akinobu Mita		return -ENODEV;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3959d4df9e0fadfc84cd826e0f7e946691b4d7baee5Akinobu Mita	return 0;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
398504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichstatic void __exit proteon_cleanup(void)
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
400504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich	struct net_device *dev;
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ISATR_MAX_ADAPTERS ; i++) {
404504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich		struct platform_device *pdev = proteon_dev[i];
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
406504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich		if (!pdev)
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
4083ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King		dev = platform_get_drvdata(pdev);
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unregister_netdev(dev);
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_region(dev->base_addr, PROTEON_IO_EXTENT);
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_irq(dev->irq, dev);
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_dma(dev->dma);
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tmsdev_term(dev);
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
4153ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King		platform_set_drvdata(pdev, NULL);
416504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrich		platform_device_unregister(pdev);
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4183ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	platform_driver_unregister(&proteon_driver);
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichmodule_init(proteon_init);
422504ff16cecf2a788181eddc9d6e47d94ce50a9f6Jochen Friedrichmodule_exit(proteon_cleanup);
423