/drivers/net/pcmcia/axnet_cs.c
C | 1725 lines | 1105 code | 255 blank | 365 comment | 204 complexity | 89cc31b6910001547dec1a62d3eb03ff MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
Large files files are truncated, but you can click here to view the full file
- /*======================================================================
- A PCMCIA ethernet driver for Asix AX88190-based cards
- The Asix AX88190 is a NS8390-derived chipset with a few nasty
- idiosyncracies that make it very inconvenient to support with a
- standard 8390 driver. This driver is based on pcnet_cs, with the
- tweaked 8390 code grafted on the end. Much of what I did was to
- clean up and update a similar driver supplied by Asix, which was
- adapted by William Lee, william@asix.com.tw.
- Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net
- axnet_cs.c 1.28 2002/06/29 06:27:37
- The network driver code is based on Donald Becker's NE2000 code:
- Written 1992,1993 by Donald Becker.
- Copyright 1993 United States Government as represented by the
- Director, National Security Agency. This software may be used and
- distributed according to the terms of the GNU General Public License,
- incorporated herein by reference.
- Donald Becker may be reached at becker@scyld.com
- ======================================================================*/
- #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/ptrace.h>
- #include <linux/string.h>
- #include <linux/timer.h>
- #include <linux/delay.h>
- #include <linux/spinlock.h>
- #include <linux/netdevice.h>
- #include <linux/etherdevice.h>
- #include <linux/crc32.h>
- #include <linux/mii.h>
- #include "../8390.h"
- #include <pcmcia/cistpl.h>
- #include <pcmcia/ciscode.h>
- #include <pcmcia/ds.h>
- #include <pcmcia/cisreg.h>
- #include <asm/io.h>
- #include <asm/system.h>
- #include <asm/byteorder.h>
- #include <asm/uaccess.h>
- #define AXNET_CMD 0x00
- #define AXNET_DATAPORT 0x10 /* NatSemi-defined port window offset. */
- #define AXNET_RESET 0x1f /* Issue a read to reset, a write to clear. */
- #define AXNET_MII_EEP 0x14 /* Offset of MII access port */
- #define AXNET_TEST 0x15 /* Offset of TEST Register port */
- #define AXNET_GPIO 0x17 /* Offset of General Purpose Register Port */
- #define AXNET_START_PG 0x40 /* First page of TX buffer */
- #define AXNET_STOP_PG 0x80 /* Last page +1 of RX ring */
- #define AXNET_RDC_TIMEOUT 0x02 /* Max wait in jiffies for Tx RDC */
- #define IS_AX88190 0x0001
- #define IS_AX88790 0x0002
- /*====================================================================*/
- /* Module parameters */
- MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
- MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
- MODULE_LICENSE("GPL");
- /*====================================================================*/
- static int axnet_config(struct pcmcia_device *link);
- static void axnet_release(struct pcmcia_device *link);
- static int axnet_open(struct net_device *dev);
- static int axnet_close(struct net_device *dev);
- static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
- static netdev_tx_t axnet_start_xmit(struct sk_buff *skb,
- struct net_device *dev);
- static struct net_device_stats *get_stats(struct net_device *dev);
- static void set_multicast_list(struct net_device *dev);
- static void axnet_tx_timeout(struct net_device *dev);
- static irqreturn_t ei_irq_wrapper(int irq, void *dev_id);
- static void ei_watchdog(u_long arg);
- static void axnet_reset_8390(struct net_device *dev);
- static int mdio_read(unsigned int addr, int phy_id, int loc);
- static void mdio_write(unsigned int addr, int phy_id, int loc, int value);
- static void get_8390_hdr(struct net_device *,
- struct e8390_pkt_hdr *, int);
- static void block_input(struct net_device *dev, int count,
- struct sk_buff *skb, int ring_offset);
- static void block_output(struct net_device *dev, int count,
- const u_char *buf, const int start_page);
- static void axnet_detach(struct pcmcia_device *p_dev);
- static void AX88190_init(struct net_device *dev, int startp);
- static int ax_open(struct net_device *dev);
- static int ax_close(struct net_device *dev);
- static irqreturn_t ax_interrupt(int irq, void *dev_id);
- /*====================================================================*/
- typedef struct axnet_dev_t {
- struct pcmcia_device *p_dev;
- caddr_t base;
- struct timer_list watchdog;
- int stale, fast_poll;
- u_short link_status;
- u_char duplex_flag;
- int phy_id;
- int flags;
- int active_low;
- } axnet_dev_t;
- static inline axnet_dev_t *PRIV(struct net_device *dev)
- {
- void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device);
- return p;
- }
- static const struct net_device_ops axnet_netdev_ops = {
- .ndo_open = axnet_open,
- .ndo_stop = axnet_close,
- .ndo_do_ioctl = axnet_ioctl,
- .ndo_start_xmit = axnet_start_xmit,
- .ndo_tx_timeout = axnet_tx_timeout,
- .ndo_get_stats = get_stats,
- .ndo_set_multicast_list = set_multicast_list,
- .ndo_change_mtu = eth_change_mtu,
- .ndo_set_mac_address = eth_mac_addr,
- .ndo_validate_addr = eth_validate_addr,
- };
- static int axnet_probe(struct pcmcia_device *link)
- {
- axnet_dev_t *info;
- struct net_device *dev;
- struct ei_device *ei_local;
- dev_dbg(&link->dev, "axnet_attach()\n");
- dev = alloc_etherdev(sizeof(struct ei_device) + sizeof(axnet_dev_t));
- if (!dev)
- return -ENOMEM;
- ei_local = netdev_priv(dev);
- spin_lock_init(&ei_local->page_lock);
- info = PRIV(dev);
- info->p_dev = link;
- link->priv = dev;
- link->config_flags |= CONF_ENABLE_IRQ;
- dev->netdev_ops = &axnet_netdev_ops;
- dev->watchdog_timeo = TX_TIMEOUT;
- return axnet_config(link);
- } /* axnet_attach */
- static void axnet_detach(struct pcmcia_device *link)
- {
- struct net_device *dev = link->priv;
- dev_dbg(&link->dev, "axnet_detach(0x%p)\n", link);
- unregister_netdev(dev);
- axnet_release(link);
- free_netdev(dev);
- } /* axnet_detach */
- /*======================================================================
- This probes for a card's hardware address by reading the PROM.
- ======================================================================*/
- static int get_prom(struct pcmcia_device *link)
- {
- struct net_device *dev = link->priv;
- unsigned int ioaddr = dev->base_addr;
- int i, j;
- /* This is based on drivers/net/ne.c */
- struct {
- u_char value, offset;
- } program_seq[] = {
- {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
- {0x01, EN0_DCFG}, /* Set word-wide access. */
- {0x00, EN0_RCNTLO}, /* Clear the count regs. */
- {0x00, EN0_RCNTHI},
- {0x00, EN0_IMR}, /* Mask completion irq. */
- {0xFF, EN0_ISR},
- {E8390_RXOFF|0x40, EN0_RXCR}, /* 0x60 Set to monitor */
- {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
- {0x10, EN0_RCNTLO},
- {0x00, EN0_RCNTHI},
- {0x00, EN0_RSARLO}, /* DMA starting at 0x0400. */
- {0x04, EN0_RSARHI},
- {E8390_RREAD+E8390_START, E8390_CMD},
- };
- /* Not much of a test, but the alternatives are messy */
- if (link->config_base != 0x03c0)
- return 0;
- axnet_reset_8390(dev);
- mdelay(10);
- for (i = 0; i < ARRAY_SIZE(program_seq); i++)
- outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
- for (i = 0; i < 6; i += 2) {
- j = inw(ioaddr + AXNET_DATAPORT);
- dev->dev_addr[i] = j & 0xff;
- dev->dev_addr[i+1] = j >> 8;
- }
- return 1;
- } /* get_prom */
- static int try_io_port(struct pcmcia_device *link)
- {
- int j, ret;
- link->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
- link->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
- if (link->resource[0]->end == 32) {
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
- /* for master/slave multifunction cards */
- if (link->resource[1]->end > 0)
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
- } else {
- /* This should be two 16-port windows */
- link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
- link->resource[1]->flags |= IO_DATA_PATH_WIDTH_16;
- }
- if (link->resource[0]->start == 0) {
-