?? axnet_cs.c
字號:
/*====================================================================== 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======================================================================*/#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/ptrace.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/spinlock.h>#include <linux/ethtool.h>#include <linux/netdevice.h>#include <linux/crc32.h>#include "../8390.h"#include <pcmcia/cs_types.h>#include <pcmcia/cs.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");#ifdef PCMCIA_DEBUG#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)static char *version ="axnet_cs.c 1.28 2002/06/29 06:27:37 (David Hinds)";#else#define DEBUG(n, args...)#endif/*====================================================================*/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 const struct ethtool_ops netdev_ethtool_ops;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(kio_addr_t addr, int phy_id, int loc);static void mdio_write(kio_addr_t 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 axdev_setup(struct net_device *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; dev_node_t node; caddr_t base; struct timer_list watchdog; int stale, fast_poll; u_short link_status; u_char duplex_flag; int phy_id; int flags;} 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;}/*====================================================================== axnet_attach() creates an "instance" of the driver, allocating local data structures for one device. The device is registered with Card Services.======================================================================*/static int axnet_probe(struct pcmcia_device *link){ axnet_dev_t *info; struct net_device *dev; DEBUG(0, "axnet_attach()\n"); dev = alloc_netdev(sizeof(struct ei_device) + sizeof(axnet_dev_t), "eth%d", axdev_setup); if (!dev) return -ENOMEM; info = PRIV(dev); info->p_dev = link; link->priv = dev; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; dev->open = &axnet_open; dev->stop = &axnet_close; dev->do_ioctl = &axnet_ioctl; SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); return axnet_config(link);} /* axnet_attach *//*====================================================================== This deletes a driver "instance". The device is de-registered with Card Services. If it has been released, all local data structures are freed. Otherwise, the structures will be freed when the device is released.======================================================================*/static void axnet_detach(struct pcmcia_device *link){ struct net_device *dev = link->priv; DEBUG(0, "axnet_detach(0x%p)\n", link); if (link->dev_node) 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; kio_addr_t 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->conf.ConfigBase != 0x03c0) return 0; axnet_reset_8390(dev); mdelay(10); for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); 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 *//*====================================================================== axnet_config() is scheduled to run after a CARD_INSERTION event is received, to configure the PCMCIA socket, and to make the ethernet device available to the system.======================================================================*/#define CS_CHECK(fn, ret) \do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)static int try_io_port(struct pcmcia_device *link){ int j, ret; if (link->io.NumPorts1 == 32) { link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; if (link->io.NumPorts2 > 0) { /* for master/slave multifunction cards */ link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; } } else { /* This should be two 16-port windows */ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; } if (link->io.BasePort1 == 0) { link->io.IOAddrLines = 16; for (j = 0; j < 0x400; j += 0x20) { link->io.BasePort1 = j ^ 0x300; link->io.BasePort2 = (j ^ 0x300) + 0x10; ret = pcmcia_request_io(link, &link->io); if (ret == CS_SUCCESS) return ret; } return ret; } else { return pcmcia_request_io(link, &link->io); }}static int axnet_config(struct pcmcia_device *link){ struct net_device *dev = link->priv; axnet_dev_t *info = PRIV(dev); tuple_t tuple; cisparse_t parse; int i, j, last_ret, last_fn; u_short buf[64]; DEBUG(0, "axnet_config(0x%p)\n", link); tuple.Attributes = 0; tuple.TupleData = (cisdata_t *)buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; /* don't trust the CIS on this; Linksys got it wrong */ link->conf.Present = 0x63; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple.Attributes = 0; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (last_ret == CS_SUCCESS) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); cistpl_io_t *io = &(parse.cftable_entry.io); if (pcmcia_get_tuple_data(link, &tuple) != 0 || pcmcia_parse_tuple(link, &tuple, &parse) != 0 || cfg->index == 0 || cfg->io.nwin == 0) goto next_entry; link->conf.ConfigIndex = 0x05; /* For multifunction cards, by convention, we configure the network function with window 0, and serial with window 1 */ if (io->nwin > 1) { i = (io->win[1].len > io->win[0].len); link->io.BasePort2 = io->win[1-i].base; link->io.NumPorts2 = io->win[1-i].len; } else { i = link->io.NumPorts2 = 0; } link->io.BasePort1 = io->win[i].base; link->io.NumPorts1 = io->win[i].len; link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; if (link->io.NumPorts1 + link->io.NumPorts2 >= 32) { last_ret = try_io_port(link); if (last_ret == CS_SUCCESS) break; } next_entry: last_ret = pcmcia_get_next_tuple(link, &tuple); } if (last_ret != CS_SUCCESS) { cs_error(link, RequestIO, last_ret); goto failed; } CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); if (link->io.NumPorts2 == 8) { link->conf.Attributes |= CONF_ENABLE_SPKR; link->conf.Status = CCSR_AUDIO_ENA; } CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; if (!get_prom(link)) { printk(KERN_NOTICE "axnet_cs: this is not an AX88190 card!\n"); printk(KERN_NOTICE "axnet_cs: use pcnet_cs instead.\n"); goto failed; } ei_status.name = "AX88190"; ei_status.word16 = 1; ei_status.tx_start_page = AXNET_START_PG; ei_status.rx_start_page = AXNET_START_PG + TX_PAGES; ei_status.stop_page = AXNET_STOP_PG; ei_status.reset_8390 = &axnet_reset_8390; ei_status.get_8390_hdr = &get_8390_hdr; ei_status.block_input = &block_input; ei_status.block_output = &block_output; if (inb(dev->base_addr + AXNET_TEST) != 0) info->flags |= IS_AX88790; else info->flags |= IS_AX88190; if (info->flags & IS_AX88790) outb(0x10, dev->base_addr + AXNET_GPIO); /* select Internal PHY */ for (i = 0; i < 32; i++) { j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); if ((j != 0) && (j != 0xffff)) break; } /* Maybe PHY is in power down mode. (PPD_SET = 1) Bit 2 of CCSR is active low. */ if (i == 32) { conf_reg_t reg = { 0, CS_WRITE, CISREG_CCSR, 0x04 }; pcmcia_access_configuration_register(link, ®); for (i = 0; i < 32; i++) { j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); if ((j != 0) && (j != 0xffff)) break; } } info->phy_id = (i < 32) ? i : -1; link->dev_node = &info->node; SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n"); link->dev_node = NULL; goto failed; } strcpy(info->node.dev_name, dev->name); printk(KERN_INFO "%s: Asix AX88%d90: io %#3lx, irq %d, hw_addr ", dev->name, ((info->flags & IS_AX88790) ? 7 : 1), dev->base_addr, dev->irq); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); if (info->phy_id != -1) { DEBUG(0, " MII transceiver at index %d, status %x.\n", info->phy_id, j); } else { printk(KERN_NOTICE " No MII transceivers found!\n"); } return 0;cs_failed: cs_error(link, last_fn, last_ret);failed: axnet_release(link); return -ENODEV;} /* axnet_config *//*====================================================================== After a card is removed, axnet_release() will unregister the net device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed.======================================================================*/static void axnet_release(struct pcmcia_device *link){ pcmcia_disable_device(link);}static int axnet_suspend(struct pcmcia_device *link){ struct net_device *dev = link->priv; if (link->open) netif_device_detach(dev); return 0;}static int axnet_resume(struct pcmcia_device *link){ struct net_device *dev = link->priv; if (link->open) { axnet_reset_8390(dev); AX88190_init(dev, 1); netif_device_attach(dev); } return 0;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -