diff options
Diffstat (limited to 'drivers/net/pcmcia/pcnet_cs.c')
-rw-r--r-- | drivers/net/pcmcia/pcnet_cs.c | 287 |
1 files changed, 192 insertions, 95 deletions
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 185644307..9eff09aab 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -11,7 +11,7 @@ Copyright (C) 1999 David A. Hinds -- dhinds@pcmcia.sourceforge.org - pcnet_cs.c 1.112 2000/02/11 01:24:44 + pcnet_cs.c 1.117 2000/05/04 01:29:47 The network driver code is based on Donald Becker's NE2000 code: @@ -28,9 +28,9 @@ ======================================================================*/ +#include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/kernel.h> #include <linux/sched.h> #include <linux/ptrace.h> #include <linux/malloc.h> @@ -72,7 +72,7 @@ static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) static char *version = -"pcnet_cs.c 1.112 2000/02/11 01:24:44 (David Hinds)"; +"pcnet_cs.c 1.117 2000/05/04 01:29:47 (David Hinds)"; #else #define DEBUG(n, args...) #endif @@ -124,6 +124,7 @@ static int pcnet_event(event_t event, int priority, event_callback_args_t *args); static int pcnet_open(struct net_device *dev); static int pcnet_close(struct net_device *dev); +static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs); static void ei_watchdog(u_long arg); static void pcnet_reset_8390(struct net_device *dev); @@ -142,16 +143,17 @@ static dev_link_t *dev_list; /*====================================================================*/ typedef struct hw_info_t { - u_long offset; + u_int offset; u_char a0, a1, a2; - u_long flags; + u_int flags; } hw_info_t; #define DELAY_OUTPUT 0x01 #define HAS_MISC_REG 0x02 #define USE_BIG_BUF 0x04 #define HAS_IBM_MISC 0x08 -#define IS_DL10019A 0x10 +#define IS_DL10019 0x10 +#define IS_AX88190 0x20 #define USE_SHMEM 0x80 /* autodetected */ static hw_info_t hw_info[] = { @@ -212,25 +214,25 @@ static hw_info_t hw_info[] = { { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 }, { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 }, { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 }, - { /* PCMCIA Technology OEM */ 0x01c8, 0xa0, 0x0c, 0 } + { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 } }; #define NR_INFO (sizeof(hw_info)/sizeof(hw_info_t)) static hw_info_t default_info = { /* Unknown NE2000 Clone */ 0x00, 0x00, 0x00, 0x00, 0 }; -static hw_info_t dl_fast_info = -{ /* D-Link EtherFast */ 0x00, 0x00, 0x00, 0x00, IS_DL10019A }; +static hw_info_t dl10019_info = +{ /* D-Link DL10019 chipset */ 0x00, 0x00, 0x00, 0x00, IS_DL10019 }; typedef struct pcnet_dev_t { - struct net_device dev; /* so &dev == &pcnet_dev_t */ + struct net_device dev; /* so &dev == &pcnet_dev_t */ dev_link_t link; dev_node_t node; - u_long flags; + u_int flags; caddr_t base; struct timer_list watchdog; - int stale, state; - u_short fast_poll; + int stale, fast_poll; + u_char link_status; } pcnet_dev_t; /*====================================================================== @@ -310,14 +312,11 @@ static dev_link_t *pcnet_attach(void) link->conf.IntType = INT_MEMORY_AND_IO; ethdev_init(dev); - dev->name = info->node.dev_name; dev->init = &pcnet_init; dev->open = &pcnet_open; dev->stop = &pcnet_close; dev->set_config = &set_config; - netif_stop_queue(dev); - /* Register with Card Services */ link->next = dev_list; dev_list = link; @@ -353,7 +352,6 @@ static void pcnet_detach(dev_link_t *link) { pcnet_dev_t *info = link->priv; dev_link_t **linkp; - long flags; DEBUG(0, "pcnet_detach(0x%p)\n", link); @@ -363,14 +361,7 @@ static void pcnet_detach(dev_link_t *link) if (*linkp == NULL) return; - save_flags(flags); - cli(); - if (link->state & DEV_RELEASE_PENDING) { - del_timer(&link->release); - link->state &= ~DEV_RELEASE_PENDING; - } - restore_flags(flags); - + del_timer(&link->release); if (link->state & DEV_CONFIG) { pcnet_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { @@ -392,27 +383,6 @@ static void pcnet_detach(dev_link_t *link) /*====================================================================== - For the Linksys EtherFast 10/100 card - -======================================================================*/ - -static hw_info_t *get_dl_fast(dev_link_t *link) -{ - struct net_device *dev = link->priv; - int i; - u_char sum; - - for (sum = 0, i = 0x14; i < 0x1c; i++) - sum += inb_p(dev->base_addr + i); - if (sum != 0xff) - return NULL; - for (i = 0; i < 6; i++) - dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i); - return &dl_fast_info; -} - -/*====================================================================== - This probes for a card's hardware address, for card types that encode this information in their CIS. @@ -471,13 +441,13 @@ static hw_info_t *get_hwinfo(dev_link_t *link) static hw_info_t *get_prom(dev_link_t *link) { struct net_device *dev = link->priv; - unsigned char prom[32]; - ioaddr_t ioaddr; + ioaddr_t ioaddr = dev->base_addr; + u_char prom[32]; int i, j; /* This is lifted straight from drivers/net/ne.c */ struct { - unsigned char value, offset; + u_char value, offset; } program_seq[] = { {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */ @@ -494,8 +464,6 @@ static hw_info_t *get_prom(dev_link_t *link) {E8390_RREAD+E8390_START, E8390_CMD}, }; - ioaddr = dev->base_addr; - pcnet_reset_8390(dev); udelay(10000); @@ -520,6 +488,58 @@ static hw_info_t *get_prom(dev_link_t *link) /*====================================================================== + For DL10019 based cards, like the Linksys EtherFast + +======================================================================*/ + +static hw_info_t *get_dl10019(dev_link_t *link) +{ + struct net_device *dev = link->priv; + int i; + u_char sum; + + for (sum = 0, i = 0x14; i < 0x1c; i++) + sum += inb_p(dev->base_addr + i); + if (sum != 0xff) + return NULL; + for (i = 0; i < 6; i++) + dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i); + return &dl10019_info; +} + +/*====================================================================== + + For Asix AX88190 based cards + +======================================================================*/ + +static hw_info_t *get_ax88190(dev_link_t *link) +{ + struct net_device *dev = link->priv; + ioaddr_t ioaddr = dev->base_addr; + int i, j; + + /* Not much of a test, but the alternatives are messy */ + if (link->conf.ConfigBase != 0x03c0) + return NULL; + + outb_p(0x01, EN0_DCFG); /* Set word-wide access. */ + outb_p(0x00, EN0_RSARLO); /* DMA starting at 0x0400. */ + outb_p(0x04, EN0_RSARHI); + outb_p(E8390_RREAD+E8390_START, E8390_CMD); + + for (i = 0; i < 6; i += 2) { + j = inw(ioaddr + PCNET_DATAPORT); + dev->dev_addr[i] = j & 0xff; + dev->dev_addr[i+1] = j >> 8; + } + printk(KERN_INFO "pcnet_cs: sorry, the AX88190 chipset is not " + "supported.\n"); + return NULL; +} + +/*====================================================================== + This should be totally unnecessary... but when we can't figure out the hardware address any other way, we'll let the user hard wire it when the module is initialized. @@ -687,7 +707,6 @@ static void pcnet_config(dev_link_t *link) } else { dev->if_port = 0; } - netif_start_queue(dev); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n"); goto failed; @@ -697,7 +716,9 @@ static void pcnet_config(dev_link_t *link) if (hw_info == NULL) hw_info = get_prom(link); if (hw_info == NULL) - hw_info = get_dl_fast(link); + hw_info = get_dl10019(link); + if (hw_info == NULL) + hw_info = get_ax88190(link); if (hw_info == NULL) hw_info = get_hwired(link); @@ -733,11 +754,19 @@ static void pcnet_config(dev_link_t *link) ei_status.word16 = 1; ei_status.reset_8390 = &pcnet_reset_8390; + strcpy(info->node.dev_name, dev->name); link->dev = &info->node; link->state &= ~DEV_CONFIG_PENDING; - printk(KERN_INFO "%s: NE2000 Compatible: io %#3lx, irq %d,", - dev->name, dev->base_addr, dev->irq); + if (info->flags & IS_DL10019) { + dev->do_ioctl = &do_ioctl; + printk(KERN_INFO "%s: NE2000 (DL10019 rev %02x): ", + dev->name, inb(dev->base_addr + 0x1a)); + } else if (info->flags & IS_AX88190) { + printk(KERN_INFO "%s: NE2000 (AX88190): ", dev->name); + } else + printk(KERN_INFO "%s: NE2000 Compatible: ", dev->name); + printk("io %#3lx, irq %d,", dev->base_addr, dev->irq); if (info->flags & USE_SHMEM) printk (" mem %#5lx,", dev->mem_start); if (info->flags & HAS_MISC_REG) @@ -787,7 +816,7 @@ static void pcnet_release(u_long arg) CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIRQ, link->handle, &link->irq); - link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + link->state &= ~DEV_CONFIG; } /* pcnet_release */ @@ -813,9 +842,7 @@ static int pcnet_event(event_t event, int priority, link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { netif_device_detach(&info->dev); - link->release.expires = jiffies + HZ/20; - link->state |= DEV_RELEASE_PENDING; - add_timer(&link->release); + mod_timer(&link->release, jiffies + HZ/20); } break; case CS_EVENT_CARD_INSERTION: @@ -829,7 +856,6 @@ static int pcnet_event(event_t event, int priority, if (link->state & DEV_CONFIG) { if (link->open) netif_device_detach(&info->dev); - CardServices(ReleaseConfiguration, link->handle); } break; @@ -889,7 +915,7 @@ static int pcnet_open(struct net_device *dev) request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev); /* Start by assuming the link is bad */ - info->state = 1; + info->link_status = 1; info->watchdog.function = &ei_watchdog; info->watchdog.data = (u_long)info; info->watchdog.expires = jiffies + HZ; @@ -910,12 +936,10 @@ static int pcnet_close(struct net_device *dev) free_irq(dev->irq, dev); link->open--; + netif_stop_queue(dev); del_timer(&info->watchdog); - if (link->state & DEV_STALE_CONFIG) { - link->release.expires = jiffies + HZ/20; - link->state |= DEV_RELEASE_PENDING; - add_timer(&link->release); - } + if (link->state & DEV_STALE_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); MOD_DEC_USE_COUNT; @@ -952,7 +976,7 @@ static void pcnet_reset_8390(struct net_device *dev) } /* pcnet_reset_8390 */ -/* ======================================================================= */ +/*====================================================================*/ static int set_config(struct net_device *dev, struct ifmap *map) { @@ -970,7 +994,7 @@ static int set_config(struct net_device *dev, struct ifmap *map) return 0; } -/* ======================================================================= */ +/*====================================================================*/ static void ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs) { @@ -985,8 +1009,7 @@ static void ei_watchdog(u_long arg) struct net_device *dev = &info->dev; ioaddr_t nic_base = dev->base_addr; - if (!netif_device_present(dev)) - goto reschedule; + if (!netif_device_present(dev)) goto reschedule; /* Check for pending interrupt with expired latency timer: with this, we can limp along even if the interrupt is blocked */ @@ -1004,14 +1027,14 @@ static void ei_watchdog(u_long arg) return; } - if (info->flags & IS_DL10019A) { - int state = inb(dev->base_addr+0x1c) & 0x01; - if (state != info->state) { + if (info->flags & IS_DL10019) { + u_char link = inb(dev->base_addr+0x1c) & 0x01; + if (link != info->link_status) { printk(KERN_INFO "%s: %s link beat\n", dev->name, - (state) ? "lost" : "found"); - if (!state) + (link) ? "lost" : "found"); + if (!link) NS8390_init(dev, 1); - info->state = state; + info->link_status = link; } } @@ -1020,7 +1043,89 @@ reschedule: add_timer(&info->watchdog); } -/* ======================================================================= */ +/*====================================================================== + + MII interface support for DL10019 based cards + + There are two types of DL10019 based cards. Some have the MII IO + direction bit as 0x10, others as 0x20; setting both bits seems to + work on all cards. + +======================================================================*/ + +#define MDIO_SHIFT_CLK 0x80 +#define MDIO_DATA_OUT 0x40 +#define MDIO_DIR_WRITE 0x30 +#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE) +#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT) +#define MDIO_DATA_READ 0x10 +#define MDIO_MASK 0x0f + +static void mdio_sync(ioaddr_t addr) +{ + int bits, mask = inb(addr) & MDIO_MASK; + for (bits = 0; bits < 32; bits++) { + outb(mask | MDIO_DATA_WRITE1, addr); + outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); + } +} + +static int mdio_read(ioaddr_t addr, int phy_id, int loc) +{ + u_int cmd = (0x06<<10)|(phy_id<<5)|loc; + int i, retval = 0, mask = inb(addr) & MDIO_MASK; + + mdio_sync(addr); + for (i = 13; i >= 0; i--) { + int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; + outb(mask | dat, addr); + outb(mask | dat | MDIO_SHIFT_CLK, addr); + } + for (i = 19; i > 0; i--) { + outb(mask, addr); + retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0); + outb(mask | MDIO_SHIFT_CLK, addr); + } + return (retval>>1) & 0xffff; +} + +static void mdio_write(ioaddr_t addr, int phy_id, int loc, int value) +{ + u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; + int i, mask = inb(addr) & MDIO_MASK; + + mdio_sync(addr); + for (i = 31; i >= 0; i--) { + int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; + outb(mask | dat, addr); + outb(mask | dat | MDIO_SHIFT_CLK, addr); + } + for (i = 1; i >= 0; i--) { + outb(mask, addr); + outb(mask | MDIO_SHIFT_CLK, addr); + } +} + +static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + u16 *data = (u16 *)&rq->ifr_data; + ioaddr_t addr = dev->base_addr + 0x1c; + switch (cmd) { + case SIOCDEVPRIVATE: + data[0] = 0; + case SIOCDEVPRIVATE+1: + data[3] = mdio_read(addr, 0, data[1] & 0x1f); + return 0; + case SIOCDEVPRIVATE+2: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + mdio_write(addr, 0, data[1] & 0x1f, data[2]); + return 0; + } + return -EOPNOTSUPP; +} + +/*====================================================================*/ static void dma_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, @@ -1052,7 +1157,7 @@ static void dma_get_8390_hdr(struct net_device *dev, ei_status.dmaing &= ~0x01; } -/* ======================================================================= */ +/*====================================================================*/ static void dma_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) @@ -1110,8 +1215,7 @@ static void dma_block_input(struct net_device *dev, int count, /*====================================================================*/ static void dma_block_output(struct net_device *dev, int count, - const unsigned char *buf, - const int start_page) + const u_char *buf, const int start_page) { ioaddr_t nic_base = dev->base_addr; pcnet_dev_t *info = (pcnet_dev_t *)dev; @@ -1215,33 +1319,31 @@ static int setup_dma_config(dev_link_t *link, int start_pg, /*====================================================================*/ -static void copyin(unsigned char *dest, unsigned char *src, int c) +static void copyin(u_char *dest, u_char *src, int c) { - unsigned short *d = (unsigned short *) dest; - unsigned short *s = (unsigned short *) src; + u_short *d = (u_short *)dest, *s = (u_short *)src; int odd; if (c <= 0) return; - odd = (c & 01); c >>= 1; + odd = (c & 1); c >>= 1; if (c) { do { *d++ = __raw_readw(s++); } while (--c); } /* get last byte by fetching a word and masking */ if (odd) - *((unsigned char *)d) = readw(s) & 0xff; + *((u_char *)d) = readw(s) & 0xff; } -static void copyout(unsigned char *dest, const unsigned char *src, int c) +static void copyout(u_char *dest, const u_char *src, int c) { - volatile unsigned short *d = (unsigned short *) dest; - unsigned short *s = (unsigned short *) src; + u_short *d = (u_short *)dest, *s = (u_short *)src; int odd; if (c <= 0) return; - odd = (c & 01); c >>= 1; + odd = (c & 1); c >>= 1; if (c) { do { __raw_writew(*s++, d++); } while (--c); @@ -1289,15 +1391,10 @@ static void shmem_block_input(struct net_device *dev, int count, /*====================================================================*/ static void shmem_block_output(struct net_device *dev, int count, - const unsigned char *buf, - const int start_page) + const u_char *buf, const int start_page) { void *shmem = (void *)dev->mem_start + (start_page << 8); shmem -= ei_status.tx_start_page << 8; - - if (ei_debug > 4) - printk(KERN_DEBUG "[bo=%d @ %x]\n", count, start_page); - copyout(shmem, buf, count); } |