| /* |
| * 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"; |
| static u64 dma_mask = ISA_MAX_ADDRESS; |
| 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 device *pdev) |
| { |
| 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 out5; |
| |
| /* At this point we have found a valid card. */ |
| |
| if (versionprinted++ == 0) |
| printk(KERN_DEBUG "%s", version); |
| |
| err = -EIO; |
| pdev->dma_mask = &dma_mask; |
| if (tmsdev_init(dev, pdev)) |
| goto out4; |
| |
| dev->base_addr &= ~3; |
| |
| sk_isa_read_eeprom(dev); |
| |
| printk(KERN_DEBUG "skisa.c: Ring Station Address: "); |
| 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 "skisa.c: AutoSelect no IRQ available\n"); |
| goto out3; |
| } |
| } |
| else |
| { |
| for(j = 0; irqlist[j] != 0; j++) |
| if (irqlist[j] == dev->irq) |
| break; |
| if (irqlist[j] == 0) |
| { |
| printk(KERN_INFO "skisa.c: Illegal IRQ %d specified\n", |
| dev->irq); |
| goto out3; |
| } |
| if (request_irq(dev->irq, tms380tr_interrupt, 0, |
| isa_cardname, dev)) |
| { |
| printk(KERN_INFO "skisa.c: Selected IRQ %d not available\n", |
| 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 "skisa.c: AutoSelect no DMA available\n"); |
| goto out2; |
| } |
| } |
| else |
| { |
| for(j = 0; dmalist[j] != 0; j++) |
| if (dmalist[j] == dev->dma) |
| break; |
| if (dmalist[j] == 0) |
| { |
| printk(KERN_INFO "skisa.c: Illegal DMA %d specified\n", |
| dev->dma); |
| goto out2; |
| } |
| if (request_dma(dev->dma, isa_cardname)) |
| { |
| printk(KERN_INFO "skisa.c: Selected DMA %d not available\n", |
| dev->dma); |
| goto out2; |
| } |
| } |
| |
| err = register_netdev(dev); |
| if (err) |
| goto out; |
| |
| printk(KERN_DEBUG "%s: IO: %#4lx IRQ: %d DMA: %d\n", |
| dev->name, dev->base_addr, dev->irq, dev->dma); |
| |
| 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); |
| out5: |
| return 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); |
| } |
| |
| #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 platform_device *sk_isa_dev[ISATR_MAX_ADAPTERS]; |
| |
| static struct device_driver sk_isa_driver = { |
| .name = "skisa", |
| .bus = &platform_bus_type, |
| }; |
| |
| static int __init sk_isa_init(void) |
| { |
| struct net_device *dev; |
| struct platform_device *pdev; |
| int i, num = 0, err = 0; |
| |
| err = driver_register(&sk_isa_driver); |
| if (err) |
| return err; |
| |
| 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]; |
| pdev = platform_device_register_simple("skisa", |
| i, NULL, 0); |
| err = setup_card(dev, &pdev->dev); |
| if (!err) { |
| sk_isa_dev[i] = pdev; |
| dev_set_drvdata(&sk_isa_dev[i]->dev, dev); |
| ++num; |
| } else { |
| platform_device_unregister(pdev); |
| 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); |
| } |
| |
| static void __exit sk_isa_cleanup(void) |
| { |
| struct net_device *dev; |
| int i; |
| |
| for (i = 0; i < ISATR_MAX_ADAPTERS ; i++) { |
| struct platform_device *pdev = sk_isa_dev[i]; |
| |
| if (!pdev) |
| continue; |
| dev = dev_get_drvdata(&pdev->dev); |
| 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); |
| dev_set_drvdata(&pdev->dev, NULL); |
| platform_device_unregister(pdev); |
| } |
| driver_unregister(&sk_isa_driver); |
| } |
| |
| module_init(sk_isa_init); |
| module_exit(sk_isa_cleanup); |