diff options
Diffstat (limited to 'drivers/net/tokenring/skisa.c')
-rw-r--r-- | drivers/net/tokenring/skisa.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/drivers/net/tokenring/skisa.c b/drivers/net/tokenring/skisa.c new file mode 100644 index 0000000..3fab54a --- /dev/null +++ b/drivers/net/tokenring/skisa.c @@ -0,0 +1,442 @@ +/* + * skisa.c: A network driver for SK-NET TMS380-based ISA token ring cards. + * + * Based on tmspci written 1999 by Adam Fritzler + * + * Written 2000 by Jochen Friedrich + * Dedicated to my girlfriend Steffi Bopp + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * This driver module supports the following cards: + * - SysKonnect TR4/16(+) ISA (SK-4190) + * + * Maintainer(s): + * AF Adam Fritzler mid@auk.cx + * JF Jochen Friedrich jochen@scram.de + * + * Modification History: + * 14-Jan-01 JF Created + * 28-Oct-02 JF Fixed probe of card for static compilation. + * Fixed module init to not make hotplug go wild. + * 09-Nov-02 JF Fixed early bail out on out of memory + * situations if multiple cards are found. + * Cleaned up some unnecessary console SPAM. + * 09-Dec-02 JF Fixed module reference counting. + * 02-Jan-03 JF Renamed to skisa.c + * + */ +static const char version[] = "skisa.c: v1.03 09/12/2002 by Jochen Friedrich\n"; + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/trdevice.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/pci.h> +#include <asm/dma.h> + +#include "tms380tr.h" + +#define SK_ISA_IO_EXTENT 32 + +/* A zero-terminated list of I/O addresses to be probed. */ +static unsigned int portlist[] __initdata = { + 0x0A20, 0x1A20, 0x0B20, 0x1B20, 0x0980, 0x1980, 0x0900, 0x1900,// SK + 0 +}; + +/* A zero-terminated list of IRQs to be probed. + * Used again after initial probe for sktr_chipset_init, called from sktr_open. + */ +static const unsigned short irqlist[] = { + 3, 5, 9, 10, 11, 12, 15, + 0 +}; + +/* A zero-terminated list of DMAs to be probed. */ +static int dmalist[] __initdata = { + 5, 6, 7, + 0 +}; + +static char isa_cardname[] = "SK NET TR 4/16 ISA\0"; + +struct net_device *sk_isa_probe(int unit); +static int sk_isa_open(struct net_device *dev); +static void sk_isa_read_eeprom(struct net_device *dev); +static unsigned short sk_isa_setnselout_pins(struct net_device *dev); + +static unsigned short sk_isa_sifreadb(struct net_device *dev, unsigned short reg) +{ + return inb(dev->base_addr + reg); +} + +static unsigned short sk_isa_sifreadw(struct net_device *dev, unsigned short reg) +{ + return inw(dev->base_addr + reg); +} + +static void sk_isa_sifwriteb(struct net_device *dev, unsigned short val, unsigned short reg) +{ + outb(val, dev->base_addr + reg); +} + +static void sk_isa_sifwritew(struct net_device *dev, unsigned short val, unsigned short reg) +{ + outw(val, dev->base_addr + reg); +} + + +static int __init sk_isa_probe1(struct net_device *dev, int ioaddr) +{ + unsigned char old, chk1, chk2; + + if (!request_region(ioaddr, SK_ISA_IO_EXTENT, isa_cardname)) + return -ENODEV; + + old = inb(ioaddr + SIFADR); /* Get the old SIFADR value */ + + chk1 = 0; /* Begin with check value 0 */ + do { + /* Write new SIFADR value */ + outb(chk1, ioaddr + SIFADR); + + /* Read, invert and write */ + chk2 = inb(ioaddr + SIFADD); + chk2 ^= 0x0FE; + outb(chk2, ioaddr + SIFADR); + + /* Read, invert and compare */ + chk2 = inb(ioaddr + SIFADD); + chk2 ^= 0x0FE; + + if(chk1 != chk2) { + release_region(ioaddr, SK_ISA_IO_EXTENT); + return -ENODEV; + } + + chk1 -= 2; + } while(chk1 != 0); /* Repeat 128 times (all byte values) */ + + /* Restore the SIFADR value */ + outb(old, ioaddr + SIFADR); + + dev->base_addr = ioaddr; + return 0; +} + +static int __init setup_card(struct net_device *dev) +{ + struct net_local *tp; + static int versionprinted; + const unsigned *port; + int j, err = 0; + + if (!dev) + return -ENOMEM; + + SET_MODULE_OWNER(dev); + if (dev->base_addr) /* probe specific location */ + err = sk_isa_probe1(dev, dev->base_addr); + else { + for (port = portlist; *port; port++) { + err = sk_isa_probe1(dev, *port); + if (!err) + break; + } + } + if (err) + goto out4; + + /* At this point we have found a valid card. */ + + if (versionprinted++ == 0) + printk(KERN_DEBUG "%s", version); + + err = -EIO; + if (tmsdev_init(dev, ISA_MAX_ADDRESS, NULL)) + goto out4; + + dev->base_addr &= ~3; + + sk_isa_read_eeprom(dev); + + printk(KERN_DEBUG "%s: Ring Station Address: ", dev->name); + printk("%2.2x", dev->dev_addr[0]); + for (j = 1; j < 6; j++) + printk(":%2.2x", dev->dev_addr[j]); + printk("\n"); + + tp = netdev_priv(dev); + tp->setnselout = sk_isa_setnselout_pins; + + tp->sifreadb = sk_isa_sifreadb; + tp->sifreadw = sk_isa_sifreadw; + tp->sifwriteb = sk_isa_sifwriteb; + tp->sifwritew = sk_isa_sifwritew; + + memcpy(tp->ProductID, isa_cardname, PROD_ID_SIZE + 1); + + tp->tmspriv = NULL; + + dev->open = sk_isa_open; + dev->stop = tms380tr_close; + + if (dev->irq == 0) + { + for(j = 0; irqlist[j] != 0; j++) + { + dev->irq = irqlist[j]; + if (!request_irq(dev->irq, tms380tr_interrupt, 0, + isa_cardname, dev)) + break; + } + + if(irqlist[j] == 0) + { + printk(KERN_INFO "%s: AutoSelect no IRQ available\n", dev->name); + goto out3; + } + } + else + { + for(j = 0; irqlist[j] != 0; j++) + if (irqlist[j] == dev->irq) + break; + if (irqlist[j] == 0) + { + printk(KERN_INFO "%s: Illegal IRQ %d specified\n", + dev->name, dev->irq); + goto out3; + } + if (request_irq(dev->irq, tms380tr_interrupt, 0, + isa_cardname, dev)) + { + printk(KERN_INFO "%s: Selected IRQ %d not available\n", + dev->name, dev->irq); + goto out3; + } + } + + if (dev->dma == 0) + { + for(j = 0; dmalist[j] != 0; j++) + { + dev->dma = dmalist[j]; + if (!request_dma(dev->dma, isa_cardname)) + break; + } + + if(dmalist[j] == 0) + { + printk(KERN_INFO "%s: AutoSelect no DMA available\n", dev->name); + goto out2; + } + } + else + { + for(j = 0; dmalist[j] != 0; j++) + if (dmalist[j] == dev->dma) + break; + if (dmalist[j] == 0) + { + printk(KERN_INFO "%s: Illegal DMA %d specified\n", + dev->name, dev->dma); + goto out2; + } + if (request_dma(dev->dma, isa_cardname)) + { + printk(KERN_INFO "%s: Selected DMA %d not available\n", + dev->name, dev->dma); + goto out2; + } + } + + printk(KERN_DEBUG "%s: IO: %#4lx IRQ: %d DMA: %d\n", + dev->name, dev->base_addr, dev->irq, dev->dma); + + err = register_netdev(dev); + if (err) + goto out; + + return 0; +out: + free_dma(dev->dma); +out2: + free_irq(dev->irq, dev); +out3: + tmsdev_term(dev); +out4: + release_region(dev->base_addr, SK_ISA_IO_EXTENT); + return err; +} + +struct net_device * __init sk_isa_probe(int unit) +{ + struct net_device *dev = alloc_trdev(sizeof(struct net_local)); + int err = 0; + + if (!dev) + return ERR_PTR(-ENOMEM); + + if (unit >= 0) { + sprintf(dev->name, "tr%d", unit); + netdev_boot_setup_check(dev); + } + + err = setup_card(dev); + if (err) + goto out; + + return dev; +out: + free_netdev(dev); + return ERR_PTR(err); +} + +/* + * Reads MAC address from adapter RAM, which should've read it from + * the onboard ROM. + * + * Calling this on a board that does not support it can be a very + * dangerous thing. The Madge board, for instance, will lock your + * machine hard when this is called. Luckily, its supported in a + * separate driver. --ASF + */ +static void sk_isa_read_eeprom(struct net_device *dev) +{ + int i; + + /* Address: 0000:0000 */ + sk_isa_sifwritew(dev, 0, SIFADX); + sk_isa_sifwritew(dev, 0, SIFADR); + + /* Read six byte MAC address data */ + dev->addr_len = 6; + for(i = 0; i < 6; i++) + dev->dev_addr[i] = sk_isa_sifreadw(dev, SIFINC) >> 8; +} + +unsigned short sk_isa_setnselout_pins(struct net_device *dev) +{ + return 0; +} + +static int sk_isa_open(struct net_device *dev) +{ + struct net_local *tp = netdev_priv(dev); + unsigned short val = 0; + unsigned short oldval; + int i; + + val = 0; + for(i = 0; irqlist[i] != 0; i++) + { + if(irqlist[i] == dev->irq) + break; + } + + val |= CYCLE_TIME << 2; + val |= i << 4; + i = dev->dma - 5; + val |= i; + if(tp->DataRate == SPEED_4) + val |= LINE_SPEED_BIT; + else + val &= ~LINE_SPEED_BIT; + oldval = sk_isa_sifreadb(dev, POSREG); + /* Leave cycle bits alone */ + oldval |= 0xf3; + val &= oldval; + sk_isa_sifwriteb(dev, val, POSREG); + + return tms380tr_open(dev); +} + +#ifdef MODULE + +#define ISATR_MAX_ADAPTERS 3 + +static int io[ISATR_MAX_ADAPTERS]; +static int irq[ISATR_MAX_ADAPTERS]; +static int dma[ISATR_MAX_ADAPTERS]; + +MODULE_LICENSE("GPL"); + +module_param_array(io, int, NULL, 0); +module_param_array(irq, int, NULL, 0); +module_param_array(dma, int, NULL, 0); + +static struct net_device *sk_isa_dev[ISATR_MAX_ADAPTERS]; + +int init_module(void) +{ + struct net_device *dev; + int i, num = 0, err = 0; + + for (i = 0; i < ISATR_MAX_ADAPTERS ; i++) { + dev = alloc_trdev(sizeof(struct net_local)); + if (!dev) + continue; + + dev->base_addr = io[i]; + dev->irq = irq[i]; + dev->dma = dma[i]; + err = setup_card(dev); + + if (!err) { + sk_isa_dev[i] = dev; + ++num; + } else { + free_netdev(dev); + } + } + + printk(KERN_NOTICE "skisa.c: %d cards found.\n", num); + /* Probe for cards. */ + if (num == 0) { + printk(KERN_NOTICE "skisa.c: No cards found.\n"); + return (-ENODEV); + } + return (0); +} + +void cleanup_module(void) +{ + int i; + + for (i = 0; i < ISATR_MAX_ADAPTERS ; i++) { + struct net_device *dev = sk_isa_dev[i]; + + if (!dev) + continue; + + unregister_netdev(dev); + release_region(dev->base_addr, SK_ISA_IO_EXTENT); + free_irq(dev->irq, dev); + free_dma(dev->dma); + tmsdev_term(dev); + free_netdev(dev); + } +} +#endif /* MODULE */ + + +/* + * Local variables: + * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/tokenring/ -c skisa.c" + * alt-compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -I/usr/src/linux/drivers/net/tokenring/ -c skisa.c" + * c-set-style "K&R" + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ |