summaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/smsc
diff options
context:
space:
mode:
authorJeff Kirsher <jeffrey.t.kirsher@intel.com>2011-05-12 20:21:07 -0700
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2011-08-10 20:04:03 -0700
commitae150435b59e68de00546330241727f2fec54517 (patch)
tree29a1cb71053306e8a8e0dff1f927d16a9a396917 /drivers/net/ethernet/smsc
parentaa43c2158d5ae1dc76cccb08cd57a3ffd32c3825 (diff)
downloadblackbird-op-linux-ae150435b59e68de00546330241727f2fec54517.tar.gz
blackbird-op-linux-ae150435b59e68de00546330241727f2fec54517.zip
smsc: Move the SMC (SMSC) drivers
Moves the SMC (SMSC) drivers into drivers/net/ethernet/smsc/ and the necessary Kconfig and Makefile changes. Also did some cleanup of NET_VENDOR_SMC Kconfig tag for the 8390 based drivers. CC: Nicolas Pitre <nico@fluxnic.net> CC: Donald Becker <becker@scyld.com> CC: Erik Stahlman <erik@vt.edu> CC: Dustin McIntire <dustin@sensoria.com> CC: Steve Glendinning <steve.glendinning@smsc.com> CC: David Hinds <dahinds@users.sourceforge.net> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/smsc')
-rw-r--r--drivers/net/ethernet/smsc/Kconfig131
-rw-r--r--drivers/net/ethernet/smsc/Makefile11
-rw-r--r--drivers/net/ethernet/smsc/epic100.c1609
-rw-r--r--drivers/net/ethernet/smsc/smc911x.c2210
-rw-r--r--drivers/net/ethernet/smsc/smc911x.h924
-rw-r--r--drivers/net/ethernet/smsc/smc9194.c1589
-rw-r--r--drivers/net/ethernet/smsc/smc9194.h241
-rw-r--r--drivers/net/ethernet/smsc/smc91c92_cs.c2070
-rw-r--r--drivers/net/ethernet/smsc/smc91x.c2431
-rw-r--r--drivers/net/ethernet/smsc/smc91x.h1180
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c2404
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.h404
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.c1763
-rw-r--r--drivers/net/ethernet/smsc/smsc9420.h276
14 files changed, 17243 insertions, 0 deletions
diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig
new file mode 100644
index 000000000000..702efe686c48
--- /dev/null
+++ b/drivers/net/ethernet/smsc/Kconfig
@@ -0,0 +1,131 @@
+#
+# Western Digital/SMC network device configuration
+#
+
+config NET_VENDOR_SMSC
+ bool "SMC (SMSC)/Western Digital devices"
+ depends on ARM || ISA || MAC || ARM || MIPS || M32R || SUPERH || \
+ BLACKFIN || MN10300 || COLDFIRE || PCI || PCMCIA
+ ---help---
+ If you have a network (Ethernet) card belonging to this class, say Y
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about SMC/Western Digital cards. If you say Y, you will
+ be asked for your specific card in the following questions.
+
+if NET_VENDOR_SMSC
+
+config SMC9194
+ tristate "SMC 9194 support"
+ depends on (ISA || MAC && BROKEN)
+ select CRC32
+ ---help---
+ This is support for the SMC9xxx based Ethernet cards. Choose this
+ option if you have a DELL laptop with the docking station, or
+ another SMC9192/9194 based chipset. Say Y if you want it compiled
+ into the kernel, and read the file
+ <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called smc9194.
+
+config SMC91X
+ tristate "SMC 91C9x/91C1xxx support"
+ select CRC32
+ select MII
+ depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \
+ MN10300 || COLDFIRE)
+ ---help---
+ This is a driver for SMC's 91x series of Ethernet chipsets,
+ including the SMC91C94 and the SMC91C111. Say Y if you want it
+ compiled into the kernel, and read the file
+ <file:Documentation/networking/smc9.txt> and the Ethernet-HOWTO,
+ available from <http://www.tldp.org/docs.html#howto>.
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called smc91x. If you want to compile it as a
+ module, say M here and read <file:Documentation/kbuild/modules.txt>.
+
+config PCMCIA_SMC91C92
+ tristate "SMC 91Cxx PCMCIA support"
+ depends on PCMCIA
+ select CRC32
+ select MII
+ ---help---
+ Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
+ (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+ To compile this driver as a module, choose M here: the module will be
+ called smc91c92_cs. If unsure, say N.
+
+config EPIC100
+ tristate "SMC EtherPower II"
+ depends on PCI
+ select CRC32
+ select MII
+ ---help---
+ This driver is for the SMC EtherPower II 9432 PCI Ethernet NIC,
+ which is based on the SMC83c17x (EPIC/100).
+ More specific information and updates are available from
+ <http://www.scyld.com/network/epic100.html>.
+
+config SMC911X
+ tristate "SMSC LAN911[5678] support"
+ select CRC32
+ select MII
+ depends on (ARM || SUPERH || MN10300)
+ ---help---
+ This is a driver for SMSC's LAN911x series of Ethernet chipsets
+ including the new LAN9115, LAN9116, LAN9117, and LAN9118.
+ Say Y if you want it compiled into the kernel,
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ This driver is also available as a module. The module will be
+ called smc911x. If you want to compile it as a module, say M
+ here and read <file:Documentation/kbuild/modules.txt>
+
+config SMSC911X
+ tristate "SMSC LAN911x/LAN921x families embedded ethernet support"
+ depends on (ARM || SUPERH || BLACKFIN || MIPS || MN10300)
+ select CRC32
+ select MII
+ select PHYLIB
+ ---help---
+ Say Y here if you want support for SMSC LAN911x and LAN921x families
+ of ethernet controllers.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module
+ will be called smsc911x.
+
+config SMSC911X_ARCH_HOOKS
+ def_bool n
+ depends on SMSC911X
+ ---help---
+ If the arch enables this, it allows the arch to implement various
+ hooks for more comprehensive interrupt control and also to override
+ the source of the MAC address.
+
+config SMSC9420
+ tristate "SMSC LAN9420 PCI ethernet adapter support"
+ depends on PCI
+ select CRC32
+ select PHYLIB
+ select SMSC_PHY
+ ---help---
+ This is a driver for SMSC's LAN9420 PCI ethernet adapter.
+ Say Y if you want it compiled into the kernel,
+ and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ This driver is also available as a module. The module will be
+ called smsc9420. If you want to compile it as a module, say M
+ here and read <file:Documentation/kbuild/modules.txt>
+
+endif # NET_VENDOR_SMSC
diff --git a/drivers/net/ethernet/smsc/Makefile b/drivers/net/ethernet/smsc/Makefile
new file mode 100644
index 000000000000..f3438dec9d90
--- /dev/null
+++ b/drivers/net/ethernet/smsc/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the SMSC network device drivers.
+#
+
+obj-$(CONFIG_SMC9194) += smc9194.o
+obj-$(CONFIG_SMC91X) += smc91x.o
+obj-$(CONFIG_PCMCIA_SMC91C92) += smc91c92_cs.o
+obj-$(CONFIG_EPIC100) += epic100.o
+obj-$(CONFIG_SMSC9420) += smsc9420.o
+obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_SMSC911X) += smsc911x.o
diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c
new file mode 100644
index 000000000000..814c187d5f95
--- /dev/null
+++ b/drivers/net/ethernet/smsc/epic100.c
@@ -0,0 +1,1609 @@
+/* epic100.c: A SMC 83c170 EPIC/100 Fast Ethernet driver for Linux. */
+/*
+ Written/copyright 1997-2001 by Donald Becker.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License (GPL), incorporated herein by reference.
+ Drivers based on or derived from this code fall under the GPL and must
+ retain the authorship, copyright and license notice. This file is not
+ a complete program and may only be used when the entire operating
+ system is licensed under the GPL.
+
+ This driver is for the SMC83c170/175 "EPIC" series, as used on the
+ SMC EtherPower II 9432 PCI adapter, and several CardBus cards.
+
+ The author may be reached as becker@scyld.com, or C/O
+ Scyld Computing Corporation
+ 410 Severn Ave., Suite 210
+ Annapolis MD 21403
+
+ Information and updates available at
+ http://www.scyld.com/network/epic100.html
+ [this link no longer provides anything useful -jgarzik]
+
+ ---------------------------------------------------------------------
+
+*/
+
+#define DRV_NAME "epic100"
+#define DRV_VERSION "2.1"
+#define DRV_RELDATE "Sept 11, 2006"
+
+/* The user-configurable values.
+ These may be modified when a driver module is loaded.*/
+
+static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+
+/* Used to pass the full-duplex flag, etc. */
+#define MAX_UNITS 8 /* More are supported, limit only on options */
+static int options[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1};
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
+ Setting to > 1518 effectively disables this feature. */
+static int rx_copybreak;
+
+/* Operational parameters that are set at compile time. */
+
+/* Keep the ring sizes a power of two for operational efficiency.
+ The compiler will convert <unsigned>'%'<2^N> into a bit mask.
+ Making the Tx ring too large decreases the effectiveness of channel
+ bonding and packet priority.
+ There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE 256
+#define TX_QUEUE_LEN 240 /* Limit ring entries actually used. */
+#define RX_RING_SIZE 256
+#define TX_TOTAL_SIZE TX_RING_SIZE*sizeof(struct epic_tx_desc)
+#define RX_TOTAL_SIZE RX_RING_SIZE*sizeof(struct epic_rx_desc)
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT (2*HZ)
+
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+/* Bytes transferred to chip before transmission starts. */
+/* Initial threshold, increased on underflow, rounded down to 4 byte units. */
+#define TX_FIFO_THRESH 256
+#define RX_FIFO_THRESH 1 /* 0-3, 0==32, 64,96, or 3==128 bytes */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+
+/* These identify the driver base version and may not be removed. */
+static char version[] __devinitdata =
+DRV_NAME ".c:v1.11 1/7/2001 Written by Donald Becker <becker@scyld.com>\n";
+static char version2[] __devinitdata =
+" (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n";
+
+MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
+MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver");
+MODULE_LICENSE("GPL");
+
+module_param(debug, int, 0);
+module_param(rx_copybreak, int, 0);
+module_param_array(options, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(debug, "EPIC/100 debug level (0-5)");
+MODULE_PARM_DESC(options, "EPIC/100: Bits 0-3: media type, bit 4: full duplex");
+MODULE_PARM_DESC(rx_copybreak, "EPIC/100 copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(full_duplex, "EPIC/100 full duplex setting(s) (1)");
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the SMC "EPIC/100", the SMC
+single-chip Ethernet controllers for PCI. This chip is used on
+the SMC EtherPower II boards.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS will assign the
+PCI INTA signal to a (preferably otherwise unused) system IRQ line.
+Note: Kernel versions earlier than 1.3.73 do not support shared PCI
+interrupt lines.
+
+III. Driver operation
+
+IIIa. Ring buffers
+
+IVb. References
+
+http://www.smsc.com/media/Downloads_Public/discontinued/83c171.pdf
+http://www.smsc.com/media/Downloads_Public/discontinued/83c175.pdf
+http://scyld.com/expert/NWay.html
+http://www.national.com/pf/DP/DP83840A.html
+
+IVc. Errata
+
+*/
+
+
+enum chip_capability_flags { MII_PWRDWN=1, TYPE2_INTR=2, NO_MII=4 };
+
+#define EPIC_TOTAL_SIZE 0x100
+#define USE_IO_OPS 1
+
+typedef enum {
+ SMSC_83C170_0,
+ SMSC_83C170,
+ SMSC_83C175,
+} chip_t;
+
+
+struct epic_chip_info {
+ const char *name;
+ int drv_flags; /* Driver use, intended as capability flags. */
+};
+
+
+/* indexed by chip_t */
+static const struct epic_chip_info pci_id_tbl[] = {
+ { "SMSC EPIC/100 83c170", TYPE2_INTR | NO_MII | MII_PWRDWN },
+ { "SMSC EPIC/100 83c170", TYPE2_INTR },
+ { "SMSC EPIC/C 83c175", TYPE2_INTR | MII_PWRDWN },
+};
+
+
+static DEFINE_PCI_DEVICE_TABLE(epic_pci_tbl) = {
+ { 0x10B8, 0x0005, 0x1092, 0x0AB4, 0, 0, SMSC_83C170_0 },
+ { 0x10B8, 0x0005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SMSC_83C170 },
+ { 0x10B8, 0x0006, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, SMSC_83C175 },
+ { 0,}
+};
+MODULE_DEVICE_TABLE (pci, epic_pci_tbl);
+
+
+#ifndef USE_IO_OPS
+#undef inb
+#undef inw
+#undef inl
+#undef outb
+#undef outw
+#undef outl
+#define inb readb
+#define inw readw
+#define inl readl
+#define outb writeb
+#define outw writew
+#define outl writel
+#endif
+
+/* Offsets to registers, using the (ugh) SMC names. */
+enum epic_registers {
+ COMMAND=0, INTSTAT=4, INTMASK=8, GENCTL=0x0C, NVCTL=0x10, EECTL=0x14,
+ PCIBurstCnt=0x18,
+ TEST1=0x1C, CRCCNT=0x20, ALICNT=0x24, MPCNT=0x28, /* Rx error counters. */
+ MIICtrl=0x30, MIIData=0x34, MIICfg=0x38,
+ LAN0=64, /* MAC address. */
+ MC0=80, /* Multicast filter table. */
+ RxCtrl=96, TxCtrl=112, TxSTAT=0x74,
+ PRxCDAR=0x84, RxSTAT=0xA4, EarlyRx=0xB0, PTxCDAR=0xC4, TxThresh=0xDC,
+};
+
+/* Interrupt register bits, using my own meaningful names. */
+enum IntrStatus {
+ TxIdle=0x40000, RxIdle=0x20000, IntrSummary=0x010000,
+ PCIBusErr170=0x7000, PCIBusErr175=0x1000, PhyEvent175=0x8000,
+ RxStarted=0x0800, RxEarlyWarn=0x0400, CntFull=0x0200, TxUnderrun=0x0100,
+ TxEmpty=0x0080, TxDone=0x0020, RxError=0x0010,
+ RxOverflow=0x0008, RxFull=0x0004, RxHeader=0x0002, RxDone=0x0001,
+};
+enum CommandBits {
+ StopRx=1, StartRx=2, TxQueued=4, RxQueued=8,
+ StopTxDMA=0x20, StopRxDMA=0x40, RestartTx=0x80,
+};
+
+#define EpicRemoved 0xffffffff /* Chip failed or removed (CardBus) */
+
+#define EpicNapiEvent (TxEmpty | TxDone | \
+ RxDone | RxStarted | RxEarlyWarn | RxOverflow | RxFull)
+#define EpicNormalEvent (0x0000ffff & ~EpicNapiEvent)
+
+static const u16 media2miictl[16] = {
+ 0, 0x0C00, 0x0C00, 0x2000, 0x0100, 0x2100, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 };
+
+/*
+ * The EPIC100 Rx and Tx buffer descriptors. Note that these
+ * really ARE host-endian; it's not a misannotation. We tell
+ * the card to byteswap them internally on big-endian hosts -
+ * look for #ifdef __BIG_ENDIAN in epic_open().
+ */
+
+struct epic_tx_desc {
+ u32 txstatus;
+ u32 bufaddr;
+ u32 buflength;
+ u32 next;
+};
+
+struct epic_rx_desc {
+ u32 rxstatus;
+ u32 bufaddr;
+ u32 buflength;
+ u32 next;
+};
+
+enum desc_status_bits {
+ DescOwn=0x8000,
+};
+
+#define PRIV_ALIGN 15 /* Required alignment mask */
+struct epic_private {
+ struct epic_rx_desc *rx_ring;
+ struct epic_tx_desc *tx_ring;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ /* The addresses of receive-in-place skbuffs. */
+ struct sk_buff* rx_skbuff[RX_RING_SIZE];
+
+ dma_addr_t tx_ring_dma;
+ dma_addr_t rx_ring_dma;
+
+ /* Ring pointers. */
+ spinlock_t lock; /* Group with Tx control cache line. */
+ spinlock_t napi_lock;
+ struct napi_struct napi;
+ unsigned int reschedule_in_poll;
+ unsigned int cur_tx, dirty_tx;
+
+ unsigned int cur_rx, dirty_rx;
+ u32 irq_mask;
+ unsigned int rx_buf_sz; /* Based on MTU+slack. */
+
+ struct pci_dev *pci_dev; /* PCI bus location. */
+ int chip_id, chip_flags;
+
+ struct timer_list timer; /* Media selection timer. */
+ int tx_threshold;
+ unsigned char mc_filter[8];
+ signed char phys[4]; /* MII device addresses. */
+ u16 advertising; /* NWay media advertisement */
+ int mii_phy_cnt;
+ struct mii_if_info mii;
+ unsigned int tx_full:1; /* The Tx queue is full. */
+ unsigned int default_port:4; /* Last dev->if_port value. */
+};
+
+static int epic_open(struct net_device *dev);
+static int read_eeprom(long ioaddr, int location);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int val);
+static void epic_restart(struct net_device *dev);
+static void epic_timer(unsigned long data);
+static void epic_tx_timeout(struct net_device *dev);
+static void epic_init_ring(struct net_device *dev);
+static netdev_tx_t epic_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static int epic_rx(struct net_device *dev, int budget);
+static int epic_poll(struct napi_struct *napi, int budget);
+static irqreturn_t epic_interrupt(int irq, void *dev_instance);
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static const struct ethtool_ops netdev_ethtool_ops;
+static int epic_close(struct net_device *dev);
+static struct net_device_stats *epic_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+
+static const struct net_device_ops epic_netdev_ops = {
+ .ndo_open = epic_open,
+ .ndo_stop = epic_close,
+ .ndo_start_xmit = epic_start_xmit,
+ .ndo_tx_timeout = epic_tx_timeout,
+ .ndo_get_stats = epic_get_stats,
+ .ndo_set_multicast_list = set_rx_mode,
+ .ndo_do_ioctl = netdev_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int __devinit epic_init_one (struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ static int card_idx = -1;
+ long ioaddr;
+ int chip_idx = (int) ent->driver_data;
+ int irq;
+ struct net_device *dev;
+ struct epic_private *ep;
+ int i, ret, option = 0, duplex = 0;
+ void *ring_space;
+ dma_addr_t ring_dma;
+
+/* when built into the kernel, we only print version if device is found */
+#ifndef MODULE
+ static int printed_version;
+ if (!printed_version++)
+ printk(KERN_INFO "%s%s", version, version2);
+#endif
+
+ card_idx++;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto out;
+ irq = pdev->irq;
+
+ if (pci_resource_len(pdev, 0) < EPIC_TOTAL_SIZE) {
+ dev_err(&pdev->dev, "no PCI region space\n");
+ ret = -ENODEV;
+ goto err_out_disable;
+ }
+
+ pci_set_master(pdev);
+
+ ret = pci_request_regions(pdev, DRV_NAME);
+ if (ret < 0)
+ goto err_out_disable;
+
+ ret = -ENOMEM;
+
+ dev = alloc_etherdev(sizeof (*ep));
+ if (!dev) {
+ dev_err(&pdev->dev, "no memory for eth device\n");
+ goto err_out_free_res;
+ }
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+#ifdef USE_IO_OPS
+ ioaddr = pci_resource_start (pdev, 0);
+#else
+ ioaddr = pci_resource_start (pdev, 1);
+ ioaddr = (long) pci_ioremap_bar(pdev, 1);
+ if (!ioaddr) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ goto err_out_free_netdev;
+ }
+#endif
+
+ pci_set_drvdata(pdev, dev);
+ ep = netdev_priv(dev);
+ ep->mii.dev = dev;
+ ep->mii.mdio_read = mdio_read;
+ ep->mii.mdio_write = mdio_write;
+ ep->mii.phy_id_mask = 0x1f;
+ ep->mii.reg_num_mask = 0x1f;
+
+ ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
+ if (!ring_space)
+ goto err_out_iounmap;
+ ep->tx_ring = ring_space;
+ ep->tx_ring_dma = ring_dma;
+
+ ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma);
+ if (!ring_space)
+ goto err_out_unmap_tx;
+ ep->rx_ring = ring_space;
+ ep->rx_ring_dma = ring_dma;
+
+ if (dev->mem_start) {
+ option = dev->mem_start;
+ duplex = (dev->mem_start & 16) ? 1 : 0;
+ } else if (card_idx >= 0 && card_idx < MAX_UNITS) {
+ if (options[card_idx] >= 0)
+ option = options[card_idx];
+ if (full_duplex[card_idx] >= 0)
+ duplex = full_duplex[card_idx];
+ }
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ spin_lock_init(&ep->lock);
+ spin_lock_init(&ep->napi_lock);
+ ep->reschedule_in_poll = 0;
+
+ /* Bring the chip out of low-power mode. */
+ outl(0x4200, ioaddr + GENCTL);
+ /* Magic?! If we don't set this bit the MII interface won't work. */
+ /* This magic is documented in SMSC app note 7.15 */
+ for (i = 16; i > 0; i--)
+ outl(0x0008, ioaddr + TEST1);
+
+ /* Turn on the MII transceiver. */
+ outl(0x12, ioaddr + MIICfg);
+ if (chip_idx == 1)
+ outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+ outl(0x0200, ioaddr + GENCTL);
+
+ /* Note: the '175 does not have a serial EEPROM. */
+ for (i = 0; i < 3; i++)
+ ((__le16 *)dev->dev_addr)[i] = cpu_to_le16(inw(ioaddr + LAN0 + i*4));
+
+ if (debug > 2) {
+ dev_printk(KERN_DEBUG, &pdev->dev, "EEPROM contents:\n");
+ for (i = 0; i < 64; i++)
+ printk(" %4.4x%s", read_eeprom(ioaddr, i),
+ i % 16 == 15 ? "\n" : "");
+ }
+
+ ep->pci_dev = pdev;
+ ep->chip_id = chip_idx;
+ ep->chip_flags = pci_id_tbl[chip_idx].drv_flags;
+ ep->irq_mask =
+ (ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170)
+ | CntFull | TxUnderrun | EpicNapiEvent;
+
+ /* Find the connected MII xcvrs.
+ Doing this in open() would allow detecting external xcvrs later, but
+ takes much time and no cards have external MII. */
+ {
+ int phy, phy_idx = 0;
+ for (phy = 1; phy < 32 && phy_idx < sizeof(ep->phys); phy++) {
+ int mii_status = mdio_read(dev, phy, MII_BMSR);
+ if (mii_status != 0xffff && mii_status != 0x0000) {
+ ep->phys[phy_idx++] = phy;
+ dev_info(&pdev->dev,
+ "MII transceiver #%d control "
+ "%4.4x status %4.4x.\n",
+ phy, mdio_read(dev, phy, 0), mii_status);
+ }
+ }
+ ep->mii_phy_cnt = phy_idx;
+ if (phy_idx != 0) {
+ phy = ep->phys[0];
+ ep->mii.advertising = mdio_read(dev, phy, MII_ADVERTISE);
+ dev_info(&pdev->dev,
+ "Autonegotiation advertising %4.4x link "
+ "partner %4.4x.\n",
+ ep->mii.advertising, mdio_read(dev, phy, 5));
+ } else if ( ! (ep->chip_flags & NO_MII)) {
+ dev_warn(&pdev->dev,
+ "***WARNING***: No MII transceiver found!\n");
+ /* Use the known PHY address of the EPII. */
+ ep->phys[0] = 3;
+ }
+ ep->mii.phy_id = ep->phys[0];
+ }
+
+ /* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */
+ if (ep->chip_flags & MII_PWRDWN)
+ outl(inl(ioaddr + NVCTL) & ~0x483C, ioaddr + NVCTL);
+ outl(0x0008, ioaddr + GENCTL);
+
+ /* The lower four bits are the media type. */
+ if (duplex) {
+ ep->mii.force_media = ep->mii.full_duplex = 1;
+ dev_info(&pdev->dev, "Forced full duplex requested.\n");
+ }
+ dev->if_port = ep->default_port = option;
+
+ /* The Epic-specific entries in the device structure. */
+ dev->netdev_ops = &epic_netdev_ops;
+ dev->ethtool_ops = &netdev_ethtool_ops;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ netif_napi_add(dev, &ep->napi, epic_poll, 64);
+
+ ret = register_netdev(dev);
+ if (ret < 0)
+ goto err_out_unmap_rx;
+
+ printk(KERN_INFO "%s: %s at %#lx, IRQ %d, %pM\n",
+ dev->name, pci_id_tbl[chip_idx].name, ioaddr, dev->irq,
+ dev->dev_addr);
+
+out:
+ return ret;
+
+err_out_unmap_rx:
+ pci_free_consistent(pdev, RX_TOTAL_SIZE, ep->rx_ring, ep->rx_ring_dma);
+err_out_unmap_tx:
+ pci_free_consistent(pdev, TX_TOTAL_SIZE, ep->tx_ring, ep->tx_ring_dma);
+err_out_iounmap:
+#ifndef USE_IO_OPS
+ iounmap(ioaddr);
+err_out_free_netdev:
+#endif
+ free_netdev(dev);
+err_out_free_res:
+ pci_release_regions(pdev);
+err_out_disable:
+ pci_disable_device(pdev);
+ goto out;
+}
+
+/* Serial EEPROM section. */
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x08 /* EEPROM chip data in. */
+#define EE_WRITE_0 0x01
+#define EE_WRITE_1 0x09
+#define EE_DATA_READ 0x10 /* EEPROM chip data out. */
+#define EE_ENB (0x0001 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+ This serves to flush the operation to the PCI bus.
+ */
+
+#define eeprom_delay() inl(ee_addr)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ64_CMD (6 << 6)
+#define EE_READ256_CMD (6 << 8)
+#define EE_ERASE_CMD (7 << 6)
+
+static void epic_disable_int(struct net_device *dev, struct epic_private *ep)
+{
+ long ioaddr = dev->base_addr;
+
+ outl(0x00000000, ioaddr + INTMASK);
+}
+
+static inline void __epic_pci_commit(long ioaddr)
+{
+#ifndef USE_IO_OPS
+ inl(ioaddr + INTMASK);
+#endif
+}
+
+static inline void epic_napi_irq_off(struct net_device *dev,
+ struct epic_private *ep)
+{
+ long ioaddr = dev->base_addr;
+
+ outl(ep->irq_mask & ~EpicNapiEvent, ioaddr + INTMASK);
+ __epic_pci_commit(ioaddr);
+}
+
+static inline void epic_napi_irq_on(struct net_device *dev,
+ struct epic_private *ep)
+{
+ long ioaddr = dev->base_addr;
+
+ /* No need to commit possible posted write */
+ outl(ep->irq_mask | EpicNapiEvent, ioaddr + INTMASK);
+}
+
+static int __devinit read_eeprom(long ioaddr, int location)
+{
+ int i;
+ int retval = 0;
+ long ee_addr = ioaddr + EECTL;
+ int read_cmd = location |
+ (inl(ee_addr) & 0x40 ? EE_READ64_CMD : EE_READ256_CMD);
+
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ outl(EE_ENB, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 12; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_WRITE_1 : EE_WRITE_0;
+ outl(EE_ENB | dataval, ee_addr);
+ eeprom_delay();
+ outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ }
+ outl(EE_ENB, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outl(EE_ENB, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ outl(EE_ENB & ~EE_CS, ee_addr);
+ return retval;
+}
+
+#define MII_READOP 1
+#define MII_WRITEOP 2
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+ long ioaddr = dev->base_addr;
+ int read_cmd = (phy_id << 9) | (location << 4) | MII_READOP;
+ int i;
+
+ outl(read_cmd, ioaddr + MIICtrl);
+ /* Typical operation takes 25 loops. */
+ for (i = 400; i > 0; i--) {
+ barrier();
+ if ((inl(ioaddr + MIICtrl) & MII_READOP) == 0) {
+ /* Work around read failure bug. */
+ if (phy_id == 1 && location < 6 &&
+ inw(ioaddr + MIIData) == 0xffff) {
+ outl(read_cmd, ioaddr + MIICtrl);
+ continue;
+ }
+ return inw(ioaddr + MIIData);
+ }
+ }
+ return 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
+{
+ long ioaddr = dev->base_addr;
+ int i;
+
+ outw(value, ioaddr + MIIData);
+ outl((phy_id << 9) | (loc << 4) | MII_WRITEOP, ioaddr + MIICtrl);
+ for (i = 10000; i > 0; i--) {
+ barrier();
+ if ((inl(ioaddr + MIICtrl) & MII_WRITEOP) == 0)
+ break;
+ }
+}
+
+
+static int epic_open(struct net_device *dev)
+{
+ struct epic_private *ep = netdev_priv(dev);
+ long ioaddr = dev->base_addr;
+ int i;
+ int retval;
+
+ /* Soft reset the chip. */
+ outl(0x4001, ioaddr + GENCTL);
+
+ napi_enable(&ep->napi);
+ if ((retval = request_irq(dev->irq, epic_interrupt, IRQF_SHARED, dev->name, dev))) {
+ napi_disable(&ep->napi);
+ return retval;
+ }
+
+ epic_init_ring(dev);
+
+ outl(0x4000, ioaddr + GENCTL);
+ /* This magic is documented in SMSC app note 7.15 */
+ for (i = 16; i > 0; i--)
+ outl(0x0008, ioaddr + TEST1);
+
+ /* Pull the chip out of low-power mode, enable interrupts, and set for
+ PCI read multiple. The MIIcfg setting and strange write order are
+ required by the details of which bits are reset and the transceiver
+ wiring on the Ositech CardBus card.
+ */
+#if 0
+ outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg);
+#endif
+ if (ep->chip_flags & MII_PWRDWN)
+ outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+
+ /* Tell the chip to byteswap descriptors on big-endian hosts */
+#ifdef __BIG_ENDIAN
+ outl(0x4432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+ inl(ioaddr + GENCTL);
+ outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#else
+ outl(0x4412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+ inl(ioaddr + GENCTL);
+ outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#endif
+
+ udelay(20); /* Looks like EPII needs that if you want reliable RX init. FIXME: pci posting bug? */
+
+ for (i = 0; i < 3; i++)
+ outl(le16_to_cpu(((__le16*)dev->dev_addr)[i]), ioaddr + LAN0 + i*4);
+
+ ep->tx_threshold = TX_FIFO_THRESH;
+ outl(ep->tx_threshold, ioaddr + TxThresh);
+
+ if (media2miictl[dev->if_port & 15]) {
+ if (ep->mii_phy_cnt)
+ mdio_write(dev, ep->phys[0], MII_BMCR, media2miictl[dev->if_port&15]);
+ if (dev->if_port == 1) {
+ if (debug > 1)
+ printk(KERN_INFO "%s: Using the 10base2 transceiver, MII "
+ "status %4.4x.\n",
+ dev->name, mdio_read(dev, ep->phys[0], MII_BMSR));
+ }
+ } else {
+ int mii_lpa = mdio_read(dev, ep->phys[0], MII_LPA);
+ if (mii_lpa != 0xffff) {
+ if ((mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == LPA_10FULL)
+ ep->mii.full_duplex = 1;
+ else if (! (mii_lpa & LPA_LPACK))
+ mdio_write(dev, ep->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
+ if (debug > 1)
+ printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d"
+ " register read of %4.4x.\n", dev->name,
+ ep->mii.full_duplex ? "full" : "half",
+ ep->phys[0], mii_lpa);
+ }
+ }
+
+ outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+ outl(ep->rx_ring_dma, ioaddr + PRxCDAR);
+ outl(ep->tx_ring_dma, ioaddr + PTxCDAR);
+
+ /* Start the chip's Rx process. */
+ set_rx_mode(dev);
+ outl(StartRx | RxQueued, ioaddr + COMMAND);
+
+ netif_start_queue(dev);
+
+ /* Enable interrupts by setting the interrupt mask. */
+ outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170)
+ | CntFull | TxUnderrun
+ | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK);
+
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x "
+ "%s-duplex.\n",
+ dev->name, ioaddr, dev->irq, (int)inl(ioaddr + GENCTL),
+ ep->mii.full_duplex ? "full" : "half");
+
+ /* Set the timer to switch to check for link beat and perhaps switch
+ to an alternate media type. */
+ init_timer(&ep->timer);
+ ep->timer.expires = jiffies + 3*HZ;
+ ep->timer.data = (unsigned long)dev;
+ ep->timer.function = epic_timer; /* timer handler */
+ add_timer(&ep->timer);
+
+ return 0;
+}
+
+/* Reset the chip to recover from a PCI transaction error.
+ This may occur at interrupt time. */
+static void epic_pause(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+
+ netif_stop_queue (dev);
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0x00000000, ioaddr + INTMASK);
+ /* Stop the chip's Tx and Rx DMA processes. */
+ outw(StopRx | StopTxDMA | StopRxDMA, ioaddr + COMMAND);
+
+ /* Update the error counts. */
+ if (inw(ioaddr + COMMAND) != 0xffff) {
+ dev->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+ dev->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+ dev->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+ }
+
+ /* Remove the packets on the Rx queue. */
+ epic_rx(dev, RX_RING_SIZE);
+}
+
+static void epic_restart(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct epic_private *ep = netdev_priv(dev);
+ int i;
+
+ /* Soft reset the chip. */
+ outl(0x4001, ioaddr + GENCTL);
+
+ printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n",
+ dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx);
+ udelay(1);
+
+ /* This magic is documented in SMSC app note 7.15 */
+ for (i = 16; i > 0; i--)
+ outl(0x0008, ioaddr + TEST1);
+
+#ifdef __BIG_ENDIAN
+ outl(0x0432 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#else
+ outl(0x0412 | (RX_FIFO_THRESH<<8), ioaddr + GENCTL);
+#endif
+ outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg);
+ if (ep->chip_flags & MII_PWRDWN)
+ outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+
+ for (i = 0; i < 3; i++)
+ outl(le16_to_cpu(((__le16*)dev->dev_addr)[i]), ioaddr + LAN0 + i*4);
+
+ ep->tx_threshold = TX_FIFO_THRESH;
+ outl(ep->tx_threshold, ioaddr + TxThresh);
+ outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+ outl(ep->rx_ring_dma + (ep->cur_rx%RX_RING_SIZE)*
+ sizeof(struct epic_rx_desc), ioaddr + PRxCDAR);
+ outl(ep->tx_ring_dma + (ep->dirty_tx%TX_RING_SIZE)*
+ sizeof(struct epic_tx_desc), ioaddr + PTxCDAR);
+
+ /* Start the chip's Rx process. */
+ set_rx_mode(dev);
+ outl(StartRx | RxQueued, ioaddr + COMMAND);
+
+ /* Enable interrupts by setting the interrupt mask. */
+ outl((ep->chip_flags & TYPE2_INTR ? PCIBusErr175 : PCIBusErr170)
+ | CntFull | TxUnderrun
+ | RxError | RxHeader | EpicNapiEvent, ioaddr + INTMASK);
+
+ printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x"
+ " interrupt %4.4x.\n",
+ dev->name, (int)inl(ioaddr + COMMAND), (int)inl(ioaddr + GENCTL),
+ (int)inl(ioaddr + INTSTAT));
+}
+
+static void check_media(struct net_device *dev)
+{
+ struct epic_private *ep = netdev_priv(dev);
+ long ioaddr = dev->base_addr;
+ int mii_lpa = ep->mii_phy_cnt ? mdio_read(dev, ep->phys[0], MII_LPA) : 0;
+ int negotiated = mii_lpa & ep->mii.advertising;
+ int duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
+
+ if (ep->mii.force_media)
+ return;
+ if (mii_lpa == 0xffff) /* Bogus read */
+ return;
+ if (ep->mii.full_duplex != duplex) {
+ ep->mii.full_duplex = duplex;
+ printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
+ " partner capability of %4.4x.\n", dev->name,
+ ep->mii.full_duplex ? "full" : "half", ep->phys[0], mii_lpa);
+ outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+ }
+}
+
+static void epic_timer(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct epic_private *ep = netdev_priv(dev);
+ long ioaddr = dev->base_addr;
+ int next_tick = 5*HZ;
+
+ if (debug > 3) {
+ printk(KERN_DEBUG "%s: Media monitor tick, Tx status %8.8x.\n",
+ dev->name, (int)inl(ioaddr + TxSTAT));
+ printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x "
+ "IntStatus %4.4x RxStatus %4.4x.\n",
+ dev->name, (int)inl(ioaddr + INTMASK),
+ (int)inl(ioaddr + INTSTAT), (int)inl(ioaddr + RxSTAT));
+ }
+
+ check_media(dev);
+
+ ep->timer.expires = jiffies + next_tick;
+ add_timer(&ep->timer);
+}
+
+static void epic_tx_timeout(struct net_device *dev)
+{
+ struct epic_private *ep = netdev_priv(dev);
+ long ioaddr = dev->base_addr;
+
+ if (debug > 0) {
+ printk(KERN_WARNING "%s: Transmit timeout using MII device, "
+ "Tx status %4.4x.\n",
+ dev->name, (int)inw(ioaddr + TxSTAT));
+ if (debug > 1) {
+ printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n",
+ dev->name, ep->dirty_tx, ep->cur_tx);
+ }
+ }
+ if (inw(ioaddr + TxSTAT) & 0x10) { /* Tx FIFO underflow. */
+ dev->stats.tx_fifo_errors++;
+ outl(RestartTx, ioaddr + COMMAND);
+ } else {
+ epic_restart(dev);
+ outl(TxQueued, dev->base_addr + COMMAND);
+ }
+
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ dev->stats.tx_errors++;
+ if (!ep->tx_full)
+ netif_wake_queue(dev);
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void epic_init_ring(struct net_device *dev)
+{
+ struct epic_private *ep = netdev_priv(dev);
+ int i;
+
+ ep->tx_full = 0;
+ ep->dirty_tx = ep->cur_tx = 0;
+ ep->cur_rx = ep->dirty_rx = 0;
+ ep->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+
+ /* Initialize all Rx descriptors. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ ep->rx_ring[i].rxstatus = 0;
+ ep->rx_ring[i].buflength = ep->rx_buf_sz;
+ ep->rx_ring[i].next = ep->rx_ring_dma +
+ (i+1)*sizeof(struct epic_rx_desc);
+ ep->rx_skbuff[i] = NULL;
+ }
+ /* Mark the last entry as wrapping the ring. */
+ ep->rx_ring[i-1].next = ep->rx_ring_dma;
+
+ /* Fill in the Rx buffers. Handle allocation failure gracefully. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ struct sk_buff *skb = dev_alloc_skb(ep->rx_buf_sz + 2);
+ ep->rx_skbuff[i] = skb;
+ if (skb == NULL)
+ break;
+ skb_reserve(skb, 2); /* 16 byte align the IP header. */
+ ep->rx_ring[i].bufaddr = pci_map_single(ep->pci_dev,
+ skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ ep->rx_ring[i].rxstatus = DescOwn;
+ }
+ ep->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+
+ /* The Tx buffer descriptor is filled in as needed, but we
+ do need to clear the ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ ep->tx_skbuff[i] = NULL;
+ ep->tx_ring[i].txstatus = 0x0000;
+ ep->tx_ring[i].next = ep->tx_ring_dma +
+ (i+1)*sizeof(struct epic_tx_desc);
+ }
+ ep->tx_ring[i-1].next = ep->tx_ring_dma;
+}
+
+static netdev_tx_t epic_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct epic_private *ep = netdev_priv(dev);
+ int entry, free_count;
+ u32 ctrl_word;
+ unsigned long flags;
+
+ if (skb_padto(skb, ETH_ZLEN))
+ return NETDEV_TX_OK;
+
+ /* Caution: the write order is important here, set the field with the
+ "ownership" bit last. */
+
+ /* Calculate the next Tx descriptor entry. */
+ spin_lock_irqsave(&ep->lock, flags);
+ free_count = ep->cur_tx - ep->dirty_tx;
+ entry = ep->cur_tx % TX_RING_SIZE;
+
+ ep->tx_skbuff[entry] = skb;
+ ep->tx_ring[entry].bufaddr = pci_map_single(ep->pci_dev, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
+ if (free_count < TX_QUEUE_LEN/2) {/* Typical path */
+ ctrl_word = 0x100000; /* No interrupt */
+ } else if (free_count == TX_QUEUE_LEN/2) {
+ ctrl_word = 0x140000; /* Tx-done intr. */
+ } else if (free_count < TX_QUEUE_LEN - 1) {
+ ctrl_word = 0x100000; /* No Tx-done intr. */
+ } else {
+ /* Leave room for an additional entry. */
+ ctrl_word = 0x140000; /* Tx-done intr. */
+ ep->tx_full = 1;
+ }
+ ep->tx_ring[entry].buflength = ctrl_word | skb->len;
+ ep->tx_ring[entry].txstatus =
+ ((skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN) << 16)
+ | DescOwn;
+
+ ep->cur_tx++;
+ if (ep->tx_full)
+ netif_stop_queue(dev);
+
+ spin_unlock_irqrestore(&ep->lock, flags);
+ /* Trigger an immediate transmit demand. */
+ outl(TxQueued, dev->base_addr + COMMAND);
+
+ if (debug > 4)
+ printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, "
+ "flag %2.2x Tx status %8.8x.\n",
+ dev->name, (int)skb->len, entry, ctrl_word,
+ (int)inl(dev->base_addr + TxSTAT));
+
+ return NETDEV_TX_OK;
+}
+
+static void epic_tx_error(struct net_device *dev, struct epic_private *ep,
+ int status)
+{
+ struct net_device_stats *stats = &dev->stats;
+
+#ifndef final_version
+ /* There was an major error, log it. */
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n",
+ dev->name, status);
+#endif
+ stats->tx_errors++;
+ if (status & 0x1050)
+ stats->tx_aborted_errors++;
+ if (status & 0x0008)
+ stats->tx_carrier_errors++;
+ if (status & 0x0040)
+ stats->tx_window_errors++;
+ if (status & 0x0010)
+ stats->tx_fifo_errors++;
+}
+
+static void epic_tx(struct net_device *dev, struct epic_private *ep)
+{
+ unsigned int dirty_tx, cur_tx;
+
+ /*
+ * Note: if this lock becomes a problem we can narrow the locked
+ * region at the cost of occasionally grabbing the lock more times.
+ */
+ cur_tx = ep->cur_tx;
+ for (dirty_tx = ep->dirty_tx; cur_tx - dirty_tx > 0; dirty_tx++) {
+ struct sk_buff *skb;
+ int entry = dirty_tx % TX_RING_SIZE;
+ int txstatus = ep->tx_ring[entry].txstatus;
+
+ if (txstatus & DescOwn)
+ break; /* It still hasn't been Txed */
+
+ if (likely(txstatus & 0x0001)) {
+ dev->stats.collisions += (txstatus >> 8) & 15;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += ep->tx_skbuff[entry]->len;
+ } else
+ epic_tx_error(dev, ep, txstatus);
+
+ /* Free the original skb. */
+ skb = ep->tx_skbuff[entry];
+ pci_unmap_single(ep->pci_dev, ep->tx_ring[entry].bufaddr,
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_irq(skb);
+ ep->tx_skbuff[entry] = NULL;
+ }
+
+#ifndef final_version
+ if (cur_tx - dirty_tx > TX_RING_SIZE) {
+ printk(KERN_WARNING
+ "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dev->name, dirty_tx, cur_tx, ep->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+ ep->dirty_tx = dirty_tx;
+ if (ep->tx_full && cur_tx - dirty_tx < TX_QUEUE_LEN - 4) {
+ /* The ring is no longer full, allow new TX entries. */
+ ep->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static irqreturn_t epic_interrupt(int irq, void *dev_instance)
+{
+ struct net_device *dev = dev_instance;
+ struct epic_private *ep = netdev_priv(dev);
+ long ioaddr = dev->base_addr;
+ unsigned int handled = 0;
+ int status;
+
+ status = inl(ioaddr + INTSTAT);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outl(status & EpicNormalEvent, ioaddr + INTSTAT);
+
+ if (debug > 4) {
+ printk(KERN_DEBUG "%s: Interrupt, status=%#8.8x new "
+ "intstat=%#8.8x.\n", dev->name, status,
+ (int)inl(ioaddr + INTSTAT));
+ }
+
+ if ((status & IntrSummary) == 0)
+ goto out;
+
+ handled = 1;
+
+ if ((status & EpicNapiEvent) && !ep->reschedule_in_poll) {
+ spin_lock(&ep->napi_lock);
+ if (napi_schedule_prep(&ep->napi)) {
+ epic_napi_irq_off(dev, ep);
+ __napi_schedule(&ep->napi);
+ } else
+ ep->reschedule_in_poll++;
+ spin_unlock(&ep->napi_lock);
+ }
+ status &= ~EpicNapiEvent;
+
+ /* Check uncommon events all at once. */
+ if (status & (CntFull | TxUnderrun | PCIBusErr170 | PCIBusErr175)) {
+ if (status == EpicRemoved)
+ goto out;
+
+ /* Always update the error counts to avoid overhead later. */
+ dev->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+ dev->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+ dev->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+
+ if (status & TxUnderrun) { /* Tx FIFO underflow. */
+ dev->stats.tx_fifo_errors++;
+ outl(ep->tx_threshold += 128, ioaddr + TxThresh);
+ /* Restart the transmit process. */
+ outl(RestartTx, ioaddr + COMMAND);
+ }
+ if (status & PCIBusErr170) {
+ printk(KERN_ERR "%s: PCI Bus Error! status %4.4x.\n",
+ dev->name, status);
+ epic_pause(dev);
+ epic_restart(dev);
+ }
+ /* Clear all error sources. */
+ outl(status & 0x7f18, ioaddr + INTSTAT);
+ }
+
+out:
+ if (debug > 3) {
+ printk(KERN_DEBUG "%s: exit interrupt, intr_status=%#4.4x.\n",
+ dev->name, status);
+ }
+
+ return IRQ_RETVAL(handled);
+}
+
+static int epic_rx(struct net_device *dev, int budget)
+{
+ struct epic_private *ep = netdev_priv(dev);
+ int entry = ep->cur_rx % RX_RING_SIZE;
+ int rx_work_limit = ep->dirty_rx + RX_RING_SIZE - ep->cur_rx;
+ int work_done = 0;
+
+ if (debug > 4)
+ printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry,
+ ep->rx_ring[entry].rxstatus);
+
+ if (rx_work_limit > budget)
+ rx_work_limit = budget;
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while ((ep->rx_ring[entry].rxstatus & DescOwn) == 0) {
+ int status = ep->rx_ring[entry].rxstatus;
+
+ if (debug > 4)
+ printk(KERN_DEBUG " epic_rx() status was %8.8x.\n", status);
+ if (--rx_work_limit < 0)
+ break;
+ if (status & 0x2006) {
+ if (debug > 2)
+ printk(KERN_DEBUG "%s: epic_rx() error status was %8.8x.\n",
+ dev->name, status);
+ if (status & 0x2000) {
+ printk(KERN_WARNING "%s: Oversized Ethernet frame spanned "
+ "multiple buffers, status %4.4x!\n", dev->name, status);
+ dev->stats.rx_length_errors++;
+ } else if (status & 0x0006)
+ /* Rx Frame errors are counted in hardware. */
+ dev->stats.rx_errors++;
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ /* Omit the four octet CRC from the length. */
+ short pkt_len = (status >> 16) - 4;
+ struct sk_buff *skb;
+
+ if (pkt_len > PKT_BUF_SZ - 4) {
+ printk(KERN_ERR "%s: Oversized Ethernet frame, status %x "
+ "%d bytes.\n",
+ dev->name, status, pkt_len);
+ pkt_len = 1514;
+ }
+ /* Check if the packet is long enough to accept without copying
+ to a minimally-sized skbuff. */
+ if (pkt_len < rx_copybreak &&
+ (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+ skb_reserve(skb, 2); /* 16 byte align the IP header */
+ pci_dma_sync_single_for_cpu(ep->pci_dev,
+ ep->rx_ring[entry].bufaddr,
+ ep->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ skb_copy_to_linear_data(skb, ep->rx_skbuff[entry]->data, pkt_len);
+ skb_put(skb, pkt_len);
+ pci_dma_sync_single_for_device(ep->pci_dev,
+ ep->rx_ring[entry].bufaddr,
+ ep->rx_buf_sz,
+ PCI_DMA_FROMDEVICE);
+ } else {
+ pci_unmap_single(ep->pci_dev,
+ ep->rx_ring[entry].bufaddr,
+ ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ skb_put(skb = ep->rx_skbuff[entry], pkt_len);
+ ep->rx_skbuff[entry] = NULL;
+ }
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_receive_skb(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len;
+ }
+ work_done++;
+ entry = (++ep->cur_rx) % RX_RING_SIZE;
+ }
+
+ /* Refill the Rx ring buffers. */
+ for (; ep->cur_rx - ep->dirty_rx > 0; ep->dirty_rx++) {
+ entry = ep->dirty_rx % RX_RING_SIZE;
+ if (ep->rx_skbuff[entry] == NULL) {
+ struct sk_buff *skb;
+ skb = ep->rx_skbuff[entry] = dev_alloc_skb(ep->rx_buf_sz + 2);
+ if (skb == NULL)
+ break;
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ ep->rx_ring[entry].bufaddr = pci_map_single(ep->pci_dev,
+ skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ work_done++;
+ }
+ /* AV: shouldn't we add a barrier here? */
+ ep->rx_ring[entry].rxstatus = DescOwn;
+ }
+ return work_done;
+}
+
+static void epic_rx_err(struct net_device *dev, struct epic_private *ep)
+{
+ long ioaddr = dev->base_addr;
+ int status;
+
+ status = inl(ioaddr + INTSTAT);
+
+ if (status == EpicRemoved)
+ return;
+ if (status & RxOverflow) /* Missed a Rx frame. */
+ dev->stats.rx_errors++;
+ if (status & (RxOverflow | RxFull))
+ outw(RxQueued, ioaddr + COMMAND);
+}
+
+static int epic_poll(struct napi_struct *napi, int budget)
+{
+ struct epic_private *ep = container_of(napi, struct epic_private, napi);
+ struct net_device *dev = ep->mii.dev;
+ int work_done = 0;
+ long ioaddr = dev->base_addr;
+
+rx_action:
+
+ epic_tx(dev, ep);
+
+ work_done += epic_rx(dev, budget);
+
+ epic_rx_err(dev, ep);
+
+ if (work_done < budget) {
+ unsigned long flags;
+ int more;
+
+ /* A bit baroque but it avoids a (space hungry) spin_unlock */
+
+ spin_lock_irqsave(&ep->napi_lock, flags);
+
+ more = ep->reschedule_in_poll;
+ if (!more) {
+ __napi_complete(napi);
+ outl(EpicNapiEvent, ioaddr + INTSTAT);
+ epic_napi_irq_on(dev, ep);
+ } else
+ ep->reschedule_in_poll--;
+
+ spin_unlock_irqrestore(&ep->napi_lock, flags);
+
+ if (more)
+ goto rx_action;
+ }
+
+ return work_done;
+}
+
+static int epic_close(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct epic_private *ep = netdev_priv(dev);
+ struct sk_buff *skb;
+ int i;
+
+ netif_stop_queue(dev);
+ napi_disable(&ep->napi);
+
+ if (debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, (int)inl(ioaddr + INTSTAT));
+
+ del_timer_sync(&ep->timer);
+
+ epic_disable_int(dev, ep);
+
+ free_irq(dev->irq, dev);
+
+ epic_pause(dev);
+
+ /* Free all the skbuffs in the Rx queue. */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ skb = ep->rx_skbuff[i];
+ ep->rx_skbuff[i] = NULL;
+ ep->rx_ring[i].rxstatus = 0; /* Not owned by Epic chip. */
+ ep->rx_ring[i].buflength = 0;
+ if (skb) {
+ pci_unmap_single(ep->pci_dev, ep->rx_ring[i].bufaddr,
+ ep->rx_buf_sz, PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(skb);
+ }
+ ep->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */
+ }
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ skb = ep->tx_skbuff[i];
+ ep->tx_skbuff[i] = NULL;
+ if (!skb)
+ continue;
+ pci_unmap_single(ep->pci_dev, ep->tx_ring[i].bufaddr,
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb(skb);
+ }
+
+ /* Green! Leave the chip in low-power mode. */
+ outl(0x0008, ioaddr + GENCTL);
+
+ return 0;
+}
+
+static struct net_device_stats *epic_get_stats(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+
+ if (netif_running(dev)) {
+ /* Update the error counts. */
+ dev->stats.rx_missed_errors += inb(ioaddr + MPCNT);
+ dev->stats.rx_frame_errors += inb(ioaddr + ALICNT);
+ dev->stats.rx_crc_errors += inb(ioaddr + CRCCNT);
+ }
+
+ return &dev->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ Note that we only use exclusion around actually queueing the
+ new frame, not around filling ep->setup_frame. This is non-deterministic
+ when re-entered but still correct. */
+
+static void set_rx_mode(struct net_device *dev)
+{
+ long ioaddr = dev->base_addr;
+ struct epic_private *ep = netdev_priv(dev);
+ unsigned char mc_filter[8]; /* Multicast hash filter */
+ int i;
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+ outl(0x002C, ioaddr + RxCtrl);
+ /* Unconditionally log net taps. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ } else if ((!netdev_mc_empty(dev)) || (dev->flags & IFF_ALLMULTI)) {
+ /* There is apparently a chip bug, so the multicast filter
+ is never enabled. */
+ /* Too many to filter perfectly -- accept all multicasts. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ outl(0x000C, ioaddr + RxCtrl);
+ } else if (netdev_mc_empty(dev)) {
+ outl(0x0004, ioaddr + RxCtrl);
+ return;
+ } else { /* Never executed, for now. */
+ struct netdev_hw_addr *ha;
+
+ memset(mc_filter, 0, sizeof(mc_filter));
+ netdev_for_each_mc_addr(ha, dev) {
+ unsigned int bit_nr =
+ ether_crc_le(ETH_ALEN, ha->addr) & 0x3f;
+ mc_filter[bit_nr >> 3] |= (1 << bit_nr);
+ }
+ }
+ /* ToDo: perhaps we need to stop the Tx and Rx process here? */
+ if (memcmp(mc_filter, ep->mc_filter, sizeof(mc_filter))) {
+ for (i = 0; i < 4; i++)
+ outw(((u16 *)mc_filter)[i], ioaddr + MC0 + i*4);
+ memcpy(ep->mc_filter, mc_filter, sizeof(mc_filter));
+ }
+}
+
+static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct epic_private *np = netdev_priv(dev);
+
+ strcpy (info->driver, DRV_NAME);
+ strcpy (info->version, DRV_VERSION);
+ strcpy (info->bus_info, pci_name(np->pci_dev));
+}
+
+static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct epic_private *np = netdev_priv(dev);
+ int rc;
+
+ spin_lock_irq(&np->lock);
+ rc = mii_ethtool_gset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+
+ return rc;
+}
+
+static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct epic_private *np = netdev_priv(dev);
+ int rc;
+
+ spin_lock_irq(&np->lock);
+ rc = mii_ethtool_sset(&np->mii, cmd);
+ spin_unlock_irq(&np->lock);
+
+ return rc;
+}
+
+static int netdev_nway_reset(struct net_device *dev)
+{
+ struct epic_private *np = netdev_priv(dev);
+ return mii_nway_restart(&np->mii);
+}
+
+static u32 netdev_get_link(struct net_device *dev)
+{
+ struct epic_private *np = netdev_priv(dev);
+ return mii_link_ok(&np->mii);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+ return debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 value)
+{
+ debug = value;
+}
+
+static int ethtool_begin(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ /* power-up, if interface is down */
+ if (! netif_running(dev)) {
+ outl(0x0200, ioaddr + GENCTL);
+ outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+ }
+ return 0;
+}
+
+static void ethtool_complete(struct net_device *dev)
+{
+ unsigned long ioaddr = dev->base_addr;
+ /* power-down, if interface is down */
+ if (! netif_running(dev)) {
+ outl(0x0008, ioaddr + GENCTL);
+ outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
+ }
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+ .get_drvinfo = netdev_get_drvinfo,
+ .get_settings = netdev_get_settings,
+ .set_settings = netdev_set_settings,
+ .nway_reset = netdev_nway_reset,
+ .get_link = netdev_get_link,
+ .get_msglevel = netdev_get_msglevel,
+ .set_msglevel = netdev_set_msglevel,
+ .begin = ethtool_begin,
+ .complete = ethtool_complete
+};
+
+static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct epic_private *np = netdev_priv(dev);
+ long ioaddr = dev->base_addr;
+ struct mii_ioctl_data *data = if_mii(rq);
+ int rc;
+
+ /* power-up, if interface is down */
+ if (! netif_running(dev)) {
+ outl(0x0200, ioaddr + GENCTL);
+ outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
+ }
+
+ /* all non-ethtool ioctls (the SIOC[GS]MIIxxx ioctls) */
+ spin_lock_irq(&np->lock);
+ rc = generic_mii_ioctl(&np->mii, data, cmd, NULL);
+ spin_unlock_irq(&np->lock);
+
+ /* power-down, if interface is down */
+ if (! netif_running(dev)) {
+ outl(0x0008, ioaddr + GENCTL);
+ outl((inl(ioaddr + NVCTL) & ~0x483C) | 0x0000, ioaddr + NVCTL);
+ }
+ return rc;
+}
+
+
+static void __devexit epic_remove_one (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct epic_private *ep = netdev_priv(dev);
+
+ pci_free_consistent(pdev, TX_TOTAL_SIZE, ep->tx_ring, ep->tx_ring_dma);
+ pci_free_consistent(pdev, RX_TOTAL_SIZE, ep->rx_ring, ep->rx_ring_dma);
+ unregister_netdev(dev);
+#ifndef USE_IO_OPS
+ iounmap((void*) dev->base_addr);
+#endif
+ pci_release_regions(pdev);
+ free_netdev(dev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ /* pci_power_off(pdev, -1); */
+}
+
+
+#ifdef CONFIG_PM
+
+static int epic_suspend (struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ long ioaddr = dev->base_addr;
+
+ if (!netif_running(dev))
+ return 0;
+ epic_pause(dev);
+ /* Put the chip into low-power mode. */
+ outl(0x0008, ioaddr + GENCTL);
+ /* pci_power_off(pdev, -1); */
+ return 0;
+}
+
+
+static int epic_resume (struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+
+ if (!netif_running(dev))
+ return 0;
+ epic_restart(dev);
+ /* pci_power_on(pdev); */
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+static struct pci_driver epic_driver = {
+ .name = DRV_NAME,
+ .id_table = epic_pci_tbl,
+ .probe = epic_init_one,
+ .remove = __devexit_p(epic_remove_one),
+#ifdef CONFIG_PM
+ .suspend = epic_suspend,
+ .resume = epic_resume,
+#endif /* CONFIG_PM */
+};
+
+
+static int __init epic_init (void)
+{
+/* when a module, this is printed whether or not devices are found in probe */
+#ifdef MODULE
+ printk (KERN_INFO "%s%s",
+ version, version2);
+#endif
+
+ return pci_register_driver(&epic_driver);
+}
+
+
+static void __exit epic_cleanup (void)
+{
+ pci_unregister_driver (&epic_driver);
+}
+
+
+module_init(epic_init);
+module_exit(epic_cleanup);
diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c
new file mode 100644
index 000000000000..a91fe1723020
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc911x.c
@@ -0,0 +1,2210 @@
+/*
+ * smc911x.c
+ * This is a driver for SMSC's LAN911{5,6,7,8} single-chip Ethernet devices.
+ *
+ * Copyright (C) 2005 Sensoria Corp
+ * Derived from the unified SMC91x driver by Nicolas Pitre
+ * and the smsc911x.c reference driver by SMSC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Arguments:
+ * watchdog = TX watchdog timeout
+ * tx_fifo_kb = Size of TX FIFO in KB
+ *
+ * History:
+ * 04/16/05 Dustin McIntire Initial version
+ */
+static const char version[] =
+ "smc911x.c: v1.0 04-16-2005 by Dustin McIntire <dustin@sensoria.com>\n";
+
+/* Debugging options */
+#define ENABLE_SMC_DEBUG_RX 0
+#define ENABLE_SMC_DEBUG_TX 0
+#define ENABLE_SMC_DEBUG_DMA 0
+#define ENABLE_SMC_DEBUG_PKTS 0
+#define ENABLE_SMC_DEBUG_MISC 0
+#define ENABLE_SMC_DEBUG_FUNC 0
+
+#define SMC_DEBUG_RX ((ENABLE_SMC_DEBUG_RX ? 1 : 0) << 0)
+#define SMC_DEBUG_TX ((ENABLE_SMC_DEBUG_TX ? 1 : 0) << 1)
+#define SMC_DEBUG_DMA ((ENABLE_SMC_DEBUG_DMA ? 1 : 0) << 2)
+#define SMC_DEBUG_PKTS ((ENABLE_SMC_DEBUG_PKTS ? 1 : 0) << 3)
+#define SMC_DEBUG_MISC ((ENABLE_SMC_DEBUG_MISC ? 1 : 0) << 4)
+#define SMC_DEBUG_FUNC ((ENABLE_SMC_DEBUG_FUNC ? 1 : 0) << 5)
+
+#ifndef SMC_DEBUG
+#define SMC_DEBUG ( SMC_DEBUG_RX | \
+ SMC_DEBUG_TX | \
+ SMC_DEBUG_DMA | \
+ SMC_DEBUG_PKTS | \
+ SMC_DEBUG_MISC | \
+ SMC_DEBUG_FUNC \
+ )
+#endif
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include "smc911x.h"
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+static int tx_fifo_kb=8;
+module_param(tx_fifo_kb, int, 0400);
+MODULE_PARM_DESC(tx_fifo_kb,"transmit FIFO size in KB (1<x<15)(default=8)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:smc911x");
+
+/*
+ * The internal workings of the driver. If you are changing anything
+ * here with the SMC stuff, you should have the datasheet and know
+ * what you are doing.
+ */
+#define CARDNAME "smc911x"
+
+/*
+ * Use power-down feature of the chip
+ */
+#define POWER_DOWN 1
+
+#if SMC_DEBUG > 0
+#define DBG(n, args...) \
+ do { \
+ if (SMC_DEBUG & (n)) \
+ printk(args); \
+ } while (0)
+
+#define PRINTK(args...) printk(args)
+#else
+#define DBG(n, args...) do { } while (0)
+#define PRINTK(args...) printk(KERN_DEBUG args)
+#endif
+
+#if SMC_DEBUG_PKTS > 0
+static void PRINT_PKT(u_char *buf, int length)
+{
+ int i;
+ int remainder;
+ int lines;
+
+ lines = length / 16;
+ remainder = length % 16;
+
+ for (i = 0; i < lines ; i ++) {
+ int cur;
+ for (cur = 0; cur < 8; cur++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+ }
+ for (i = 0; i < remainder/2 ; i++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+}
+#else
+#define PRINT_PKT(x...) do { } while (0)
+#endif
+
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(lp, x) do { \
+ unsigned int __mask; \
+ __mask = SMC_GET_INT_EN((lp)); \
+ __mask |= (x); \
+ SMC_SET_INT_EN((lp), __mask); \
+} while (0)
+
+/* this disables an interrupt from the interrupt mask register */
+#define SMC_DISABLE_INT(lp, x) do { \
+ unsigned int __mask; \
+ __mask = SMC_GET_INT_EN((lp)); \
+ __mask &= ~(x); \
+ SMC_SET_INT_EN((lp), __mask); \
+} while (0)
+
+/*
+ * this does a soft reset on the device
+ */
+static void smc911x_reset(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int reg, timeout=0, resets=1, irq_cfg;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ /* Take out of PM setting first */
+ if ((SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_) == 0) {
+ /* Write to the bytetest will take out of powerdown */
+ SMC_SET_BYTE_TEST(lp, 0);
+ timeout=10;
+ do {
+ udelay(10);
+ reg = SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_;
+ } while (--timeout && !reg);
+ if (timeout == 0) {
+ PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name);
+ return;
+ }
+ }
+
+ /* Disable all interrupts */
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_SET_INT_EN(lp, 0);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ while (resets--) {
+ SMC_SET_HW_CFG(lp, HW_CFG_SRST_);
+ timeout=10;
+ do {
+ udelay(10);
+ reg = SMC_GET_HW_CFG(lp);
+ /* If chip indicates reset timeout then try again */
+ if (reg & HW_CFG_SRST_TO_) {
+ PRINTK("%s: chip reset timeout, retrying...\n", dev->name);
+ resets++;
+ break;
+ }
+ } while (--timeout && (reg & HW_CFG_SRST_));
+ }
+ if (timeout == 0) {
+ PRINTK("%s: smc911x_reset timeout waiting for reset\n", dev->name);
+ return;
+ }
+
+ /* make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout=1000;
+ while (--timeout && (SMC_GET_E2P_CMD(lp) & E2P_CMD_EPC_BUSY_))
+ udelay(10);
+
+ if (timeout == 0){
+ PRINTK("%s: smc911x_reset timeout waiting for EEPROM busy\n", dev->name);
+ return;
+ }
+
+ /* Initialize interrupts */
+ SMC_SET_INT_EN(lp, 0);
+ SMC_ACK_INT(lp, -1);
+
+ /* Reset the FIFO level and flow control settings */
+ SMC_SET_HW_CFG(lp, (lp->tx_fifo_kb & 0xF) << 16);
+//TODO: Figure out what appropriate pause time is
+ SMC_SET_FLOW(lp, FLOW_FCPT_ | FLOW_FCEN_);
+ SMC_SET_AFC_CFG(lp, lp->afc_cfg);
+
+
+ /* Set to LED outputs */
+ SMC_SET_GPIO_CFG(lp, 0x70070000);
+
+ /*
+ * Deassert IRQ for 1*10us for edge type interrupts
+ * and drive IRQ pin push-pull
+ */
+ irq_cfg = (1 << 24) | INT_CFG_IRQ_EN_ | INT_CFG_IRQ_TYPE_;
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+ if (lp->cfg.irq_polarity)
+ irq_cfg |= INT_CFG_IRQ_POL_;
+#endif
+ SMC_SET_IRQ_CFG(lp, irq_cfg);
+
+ /* clear anything saved */
+ if (lp->pending_tx_skb != NULL) {
+ dev_kfree_skb (lp->pending_tx_skb);
+ lp->pending_tx_skb = NULL;
+ dev->stats.tx_errors++;
+ dev->stats.tx_aborted_errors++;
+ }
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static void smc911x_enable(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned mask, cfg, cr;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ SMC_SET_MAC_ADDR(lp, dev->dev_addr);
+
+ /* Enable TX */
+ cfg = SMC_GET_HW_CFG(lp);
+ cfg &= HW_CFG_TX_FIF_SZ_ | 0xFFF;
+ cfg |= HW_CFG_SF_;
+ SMC_SET_HW_CFG(lp, cfg);
+ SMC_SET_FIFO_TDA(lp, 0xFF);
+ /* Update TX stats on every 64 packets received or every 1 sec */
+ SMC_SET_FIFO_TSL(lp, 64);
+ SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
+
+ SMC_GET_MAC_CR(lp, cr);
+ cr |= MAC_CR_TXEN_ | MAC_CR_HBDIS_;
+ SMC_SET_MAC_CR(lp, cr);
+ SMC_SET_TX_CFG(lp, TX_CFG_TX_ON_);
+
+ /* Add 2 byte padding to start of packets */
+ SMC_SET_RX_CFG(lp, (2<<8) & RX_CFG_RXDOFF_);
+
+ /* Turn on receiver and enable RX */
+ if (cr & MAC_CR_RXEN_)
+ DBG(SMC_DEBUG_RX, "%s: Receiver already enabled\n", dev->name);
+
+ SMC_SET_MAC_CR(lp, cr | MAC_CR_RXEN_);
+
+ /* Interrupt on every received packet */
+ SMC_SET_FIFO_RSA(lp, 0x01);
+ SMC_SET_FIFO_RSL(lp, 0x00);
+
+ /* now, enable interrupts */
+ mask = INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_ | INT_EN_RSFL_EN_ |
+ INT_EN_GPT_INT_EN_ | INT_EN_RXDFH_INT_EN_ | INT_EN_RXE_EN_ |
+ INT_EN_PHY_INT_EN_;
+ if (IS_REV_A(lp->revision))
+ mask|=INT_EN_RDFL_EN_;
+ else {
+ mask|=INT_EN_RDFO_EN_;
+ }
+ SMC_ENABLE_INT(lp, mask);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void smc911x_shutdown(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned cr;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", CARDNAME, __func__);
+
+ /* Disable IRQ's */
+ SMC_SET_INT_EN(lp, 0);
+
+ /* Turn of Rx and TX */
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MAC_CR(lp, cr);
+ cr &= ~(MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+ SMC_SET_MAC_CR(lp, cr);
+ SMC_SET_TX_CFG(lp, TX_CFG_STOP_TX_);
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static inline void smc911x_drop_pkt(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int fifo_count, timeout, reg;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n", CARDNAME, __func__);
+ fifo_count = SMC_GET_RX_FIFO_INF(lp) & 0xFFFF;
+ if (fifo_count <= 4) {
+ /* Manually dump the packet data */
+ while (fifo_count--)
+ SMC_GET_RX_FIFO(lp);
+ } else {
+ /* Fast forward through the bad packet */
+ SMC_SET_RX_DP_CTRL(lp, RX_DP_CTRL_FFWD_BUSY_);
+ timeout=50;
+ do {
+ udelay(10);
+ reg = SMC_GET_RX_DP_CTRL(lp) & RX_DP_CTRL_FFWD_BUSY_;
+ } while (--timeout && reg);
+ if (timeout == 0) {
+ PRINTK("%s: timeout waiting for RX fast forward\n", dev->name);
+ }
+ }
+}
+
+/*
+ * This is the procedure to handle the receipt of a packet.
+ * It should be called after checking for packet presence in
+ * the RX status FIFO. It must be called with the spin lock
+ * already held.
+ */
+static inline void smc911x_rcv(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int pkt_len, status;
+ struct sk_buff *skb;
+ unsigned char *data;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n",
+ dev->name, __func__);
+ status = SMC_GET_RX_STS_FIFO(lp);
+ DBG(SMC_DEBUG_RX, "%s: Rx pkt len %d status 0x%08x\n",
+ dev->name, (status & 0x3fff0000) >> 16, status & 0xc000ffff);
+ pkt_len = (status & RX_STS_PKT_LEN_) >> 16;
+ if (status & RX_STS_ES_) {
+ /* Deal with a bad packet */
+ dev->stats.rx_errors++;
+ if (status & RX_STS_CRC_ERR_)
+ dev->stats.rx_crc_errors++;
+ else {
+ if (status & RX_STS_LEN_ERR_)
+ dev->stats.rx_length_errors++;
+ if (status & RX_STS_MCAST_)
+ dev->stats.multicast++;
+ }
+ /* Remove the bad packet data from the RX FIFO */
+ smc911x_drop_pkt(dev);
+ } else {
+ /* Receive a valid packet */
+ /* Alloc a buffer with extra room for DMA alignment */
+ skb=dev_alloc_skb(pkt_len+32);
+ if (unlikely(skb == NULL)) {
+ PRINTK( "%s: Low memory, rcvd packet dropped.\n",
+ dev->name);
+ dev->stats.rx_dropped++;
+ smc911x_drop_pkt(dev);
+ return;
+ }
+ /* Align IP header to 32 bits
+ * Note that the device is configured to add a 2
+ * byte padding to the packet start, so we really
+ * want to write to the orignal data pointer */
+ data = skb->data;
+ skb_reserve(skb, 2);
+ skb_put(skb,pkt_len-4);
+#ifdef SMC_USE_DMA
+ {
+ unsigned int fifo;
+ /* Lower the FIFO threshold if possible */
+ fifo = SMC_GET_FIFO_INT(lp);
+ if (fifo & 0xFF) fifo--;
+ DBG(SMC_DEBUG_RX, "%s: Setting RX stat FIFO threshold to %d\n",
+ dev->name, fifo & 0xff);
+ SMC_SET_FIFO_INT(lp, fifo);
+ /* Setup RX DMA */
+ SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN16_ | ((2<<8) & RX_CFG_RXDOFF_));
+ lp->rxdma_active = 1;
+ lp->current_rx_skb = skb;
+ SMC_PULL_DATA(lp, data, (pkt_len+2+15) & ~15);
+ /* Packet processing deferred to DMA RX interrupt */
+ }
+#else
+ SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_));
+ SMC_PULL_DATA(lp, data, pkt_len+2+3);
+
+ DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name);
+ PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += pkt_len-4;
+#endif
+ }
+}
+
+/*
+ * This is called to actually send a packet to the chip.
+ */
+static void smc911x_hardware_send_pkt(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ struct sk_buff *skb;
+ unsigned int cmdA, cmdB, len;
+ unsigned char *buf;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __func__);
+ BUG_ON(lp->pending_tx_skb == NULL);
+
+ skb = lp->pending_tx_skb;
+ lp->pending_tx_skb = NULL;
+
+ /* cmdA {25:24] data alignment [20:16] start offset [10:0] buffer length */
+ /* cmdB {31:16] pkt tag [10:0] length */
+#ifdef SMC_USE_DMA
+ /* 16 byte buffer alignment mode */
+ buf = (char*)((u32)(skb->data) & ~0xF);
+ len = (skb->len + 0xF + ((u32)skb->data & 0xF)) & ~0xF;
+ cmdA = (1<<24) | (((u32)skb->data & 0xF)<<16) |
+ TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+ skb->len;
+#else
+ buf = (char*)((u32)skb->data & ~0x3);
+ len = (skb->len + 3 + ((u32)skb->data & 3)) & ~0x3;
+ cmdA = (((u32)skb->data & 0x3) << 16) |
+ TX_CMD_A_INT_FIRST_SEG_ | TX_CMD_A_INT_LAST_SEG_ |
+ skb->len;
+#endif
+ /* tag is packet length so we can use this in stats update later */
+ cmdB = (skb->len << 16) | (skb->len & 0x7FF);
+
+ DBG(SMC_DEBUG_TX, "%s: TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n",
+ dev->name, len, len, buf, cmdA, cmdB);
+ SMC_SET_TX_FIFO(lp, cmdA);
+ SMC_SET_TX_FIFO(lp, cmdB);
+
+ DBG(SMC_DEBUG_PKTS, "%s: Transmitted packet\n", dev->name);
+ PRINT_PKT(buf, len <= 64 ? len : 64);
+
+ /* Send pkt via PIO or DMA */
+#ifdef SMC_USE_DMA
+ lp->current_tx_skb = skb;
+ SMC_PUSH_DATA(lp, buf, len);
+ /* DMA complete IRQ will free buffer and set jiffies */
+#else
+ SMC_PUSH_DATA(lp, buf, len);
+ dev->trans_start = jiffies;
+ dev_kfree_skb_irq(skb);
+#endif
+ if (!lp->tx_throttle) {
+ netif_wake_queue(dev);
+ }
+ SMC_ENABLE_INT(lp, INT_EN_TDFA_EN_ | INT_EN_TSFL_EN_);
+}
+
+/*
+ * Since I am not sure if I will have enough room in the chip's ram
+ * to store the packet, I call this routine which either sends it
+ * now, or set the card to generates an interrupt when ready
+ * for the packet.
+ */
+static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int free;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
+ dev->name, __func__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ BUG_ON(lp->pending_tx_skb != NULL);
+
+ free = SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TDFREE_;
+ DBG(SMC_DEBUG_TX, "%s: TX free space %d\n", dev->name, free);
+
+ /* Turn off the flow when running out of space in FIFO */
+ if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) {
+ DBG(SMC_DEBUG_TX, "%s: Disabling data flow due to low FIFO space (%d)\n",
+ dev->name, free);
+ /* Reenable when at least 1 packet of size MTU present */
+ SMC_SET_FIFO_TDA(lp, (SMC911X_TX_FIFO_LOW_THRESHOLD)/64);
+ lp->tx_throttle = 1;
+ netif_stop_queue(dev);
+ }
+
+ /* Drop packets when we run out of space in TX FIFO
+ * Account for overhead required for:
+ *
+ * Tx command words 8 bytes
+ * Start offset 15 bytes
+ * End padding 15 bytes
+ */
+ if (unlikely(free < (skb->len + 8 + 15 + 15))) {
+ printk("%s: No Tx free space %d < %d\n",
+ dev->name, free, skb->len);
+ lp->pending_tx_skb = NULL;
+ dev->stats.tx_errors++;
+ dev->stats.tx_dropped++;
+ spin_unlock_irqrestore(&lp->lock, flags);
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+#ifdef SMC_USE_DMA
+ {
+ /* If the DMA is already running then defer this packet Tx until
+ * the DMA IRQ starts it
+ */
+ if (lp->txdma_active) {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Tx DMA running, deferring packet\n", dev->name);
+ lp->pending_tx_skb = skb;
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return NETDEV_TX_OK;
+ } else {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Activating Tx DMA\n", dev->name);
+ lp->txdma_active = 1;
+ }
+ }
+#endif
+ lp->pending_tx_skb = skb;
+ smc911x_hardware_send_pkt(dev);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+/*
+ * This handles a TX status interrupt, which is only called when:
+ * - a TX error occurred, or
+ * - TX of a packet completed.
+ */
+static void smc911x_tx(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int tx_status;
+
+ DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n",
+ dev->name, __func__);
+
+ /* Collect the TX status */
+ while (((SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16) != 0) {
+ DBG(SMC_DEBUG_TX, "%s: Tx stat FIFO used 0x%04x\n",
+ dev->name,
+ (SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16);
+ tx_status = SMC_GET_TX_STS_FIFO(lp);
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes+=tx_status>>16;
+ DBG(SMC_DEBUG_TX, "%s: Tx FIFO tag 0x%04x status 0x%04x\n",
+ dev->name, (tx_status & 0xffff0000) >> 16,
+ tx_status & 0x0000ffff);
+ /* count Tx errors, but ignore lost carrier errors when in
+ * full-duplex mode */
+ if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx &&
+ !(tx_status & 0x00000306))) {
+ dev->stats.tx_errors++;
+ }
+ if (tx_status & TX_STS_MANY_COLL_) {
+ dev->stats.collisions+=16;
+ dev->stats.tx_aborted_errors++;
+ } else {
+ dev->stats.collisions+=(tx_status & TX_STS_COLL_CNT_) >> 3;
+ }
+ /* carrier error only has meaning for half-duplex communication */
+ if ((tx_status & (TX_STS_LOC_ | TX_STS_NO_CARR_)) &&
+ !lp->ctl_rfduplx) {
+ dev->stats.tx_carrier_errors++;
+ }
+ if (tx_status & TX_STS_LATE_COLL_) {
+ dev->stats.collisions++;
+ dev->stats.tx_aborted_errors++;
+ }
+ }
+}
+
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+/*
+ * Reads a register from the MII Management serial interface
+ */
+
+static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int phydata;
+
+ SMC_GET_MII(lp, phyreg, phyaddr, phydata);
+
+ DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n",
+ __func__, phyaddr, phyreg, phydata);
+ return phydata;
+}
+
+
+/*
+ * Writes a register to the MII Management serial interface
+ */
+static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg,
+ int phydata)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+
+ DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+ __func__, phyaddr, phyreg, phydata);
+
+ SMC_SET_MII(lp, phyreg, phyaddr, phydata);
+}
+
+/*
+ * Finds and reports the PHY address (115 and 117 have external
+ * PHY interface 118 has internal only
+ */
+static void smc911x_phy_detect(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int phyaddr;
+ unsigned int cfg, id1, id2;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ lp->phy_type = 0;
+
+ /*
+ * Scan all 32 PHY addresses if necessary, starting at
+ * PHY#1 to PHY#31, and then PHY#0 last.
+ */
+ switch(lp->version) {
+ case CHIP_9115:
+ case CHIP_9117:
+ case CHIP_9215:
+ case CHIP_9217:
+ cfg = SMC_GET_HW_CFG(lp);
+ if (cfg & HW_CFG_EXT_PHY_DET_) {
+ cfg &= ~HW_CFG_PHY_CLK_SEL_;
+ cfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ SMC_SET_HW_CFG(lp, cfg);
+ udelay(10); /* Wait for clocks to stop */
+
+ cfg |= HW_CFG_EXT_PHY_EN_;
+ SMC_SET_HW_CFG(lp, cfg);
+ udelay(10); /* Wait for clocks to stop */
+
+ cfg &= ~HW_CFG_PHY_CLK_SEL_;
+ cfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ SMC_SET_HW_CFG(lp, cfg);
+ udelay(10); /* Wait for clocks to stop */
+
+ cfg |= HW_CFG_SMI_SEL_;
+ SMC_SET_HW_CFG(lp, cfg);
+
+ for (phyaddr = 1; phyaddr < 32; ++phyaddr) {
+
+ /* Read the PHY identifiers */
+ SMC_GET_PHY_ID1(lp, phyaddr & 31, id1);
+ SMC_GET_PHY_ID2(lp, phyaddr & 31, id2);
+
+ /* Make sure it is a valid identifier */
+ if (id1 != 0x0000 && id1 != 0xffff &&
+ id1 != 0x8000 && id2 != 0x0000 &&
+ id2 != 0xffff && id2 != 0x8000) {
+ /* Save the PHY's address */
+ lp->mii.phy_id = phyaddr & 31;
+ lp->phy_type = id1 << 16 | id2;
+ break;
+ }
+ }
+ if (phyaddr < 32)
+ /* Found an external PHY */
+ break;
+ }
+ default:
+ /* Internal media only */
+ SMC_GET_PHY_ID1(lp, 1, id1);
+ SMC_GET_PHY_ID2(lp, 1, id2);
+ /* Save the PHY's address */
+ lp->mii.phy_id = 1;
+ lp->phy_type = id1 << 16 | id2;
+ }
+
+ DBG(SMC_DEBUG_MISC, "%s: phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n",
+ dev->name, id1, id2, lp->mii.phy_id);
+}
+
+/*
+ * Sets the PHY to a configuration as determined by the user.
+ * Called with spin_lock held.
+ */
+static int smc911x_phy_fixed(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int phyaddr = lp->mii.phy_id;
+ int bmcr;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ /* Enter Link Disable state */
+ SMC_GET_PHY_BMCR(lp, phyaddr, bmcr);
+ bmcr |= BMCR_PDOWN;
+ SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+
+ /*
+ * Set our fixed capabilities
+ * Disable auto-negotiation
+ */
+ bmcr &= ~BMCR_ANENABLE;
+ if (lp->ctl_rfduplx)
+ bmcr |= BMCR_FULLDPLX;
+
+ if (lp->ctl_rspeed == 100)
+ bmcr |= BMCR_SPEED100;
+
+ /* Write our capabilities to the phy control register */
+ SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+
+ /* Re-Configure the Receive/Phy Control register */
+ bmcr &= ~BMCR_PDOWN;
+ SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+
+ return 1;
+}
+
+/*
+ * smc911x_phy_reset - reset the phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Issue a software reset for the specified PHY and
+ * wait up to 100ms for the reset to complete. We should
+ * not access the PHY for 50ms after issuing the reset.
+ *
+ * The time to wait appears to be dependent on the PHY.
+ *
+ */
+static int smc911x_phy_reset(struct net_device *dev, int phy)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int timeout;
+ unsigned long flags;
+ unsigned int reg;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ reg = SMC_GET_PMT_CTRL(lp);
+ reg &= ~0xfffff030;
+ reg |= PMT_CTRL_PHY_RST_;
+ SMC_SET_PMT_CTRL(lp, reg);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ for (timeout = 2; timeout; timeout--) {
+ msleep(50);
+ spin_lock_irqsave(&lp->lock, flags);
+ reg = SMC_GET_PMT_CTRL(lp);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ if (!(reg & PMT_CTRL_PHY_RST_)) {
+ /* extra delay required because the phy may
+ * not be completed with its reset
+ * when PHY_BCR_RESET_ is cleared. 256us
+ * should suffice, but use 500us to be safe
+ */
+ udelay(500);
+ break;
+ }
+ }
+
+ return reg & PMT_CTRL_PHY_RST_;
+}
+
+/*
+ * smc911x_phy_powerdown - powerdown phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Power down the specified PHY
+ */
+static void smc911x_phy_powerdown(struct net_device *dev, int phy)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int bmcr;
+
+ /* Enter Link Disable state */
+ SMC_GET_PHY_BMCR(lp, phy, bmcr);
+ bmcr |= BMCR_PDOWN;
+ SMC_SET_PHY_BMCR(lp, phy, bmcr);
+}
+
+/*
+ * smc911x_phy_check_media - check the media status and adjust BMCR
+ * @dev: net device
+ * @init: set true for initialisation
+ *
+ * Select duplex mode depending on negotiation state. This
+ * also updates our carrier state.
+ */
+static void smc911x_phy_check_media(struct net_device *dev, int init)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int phyaddr = lp->mii.phy_id;
+ unsigned int bmcr, cr;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
+ /* duplex state has changed */
+ SMC_GET_PHY_BMCR(lp, phyaddr, bmcr);
+ SMC_GET_MAC_CR(lp, cr);
+ if (lp->mii.full_duplex) {
+ DBG(SMC_DEBUG_MISC, "%s: Configuring for full-duplex mode\n", dev->name);
+ bmcr |= BMCR_FULLDPLX;
+ cr |= MAC_CR_RCVOWN_;
+ } else {
+ DBG(SMC_DEBUG_MISC, "%s: Configuring for half-duplex mode\n", dev->name);
+ bmcr &= ~BMCR_FULLDPLX;
+ cr &= ~MAC_CR_RCVOWN_;
+ }
+ SMC_SET_PHY_BMCR(lp, phyaddr, bmcr);
+ SMC_SET_MAC_CR(lp, cr);
+ }
+}
+
+/*
+ * Configures the specified PHY through the MII management interface
+ * using Autonegotiation.
+ * Calls smc911x_phy_fixed() if the user has requested a certain config.
+ * If RPC ANEG bit is set, the media selection is dependent purely on
+ * the selection by the MII (either in the MII BMCR reg or the result
+ * of autonegotiation.) If the RPC ANEG bit is cleared, the selection
+ * is controlled by the RPC SPEED and RPC DPLX bits.
+ */
+static void smc911x_phy_configure(struct work_struct *work)
+{
+ struct smc911x_local *lp = container_of(work, struct smc911x_local,
+ phy_configure);
+ struct net_device *dev = lp->netdev;
+ int phyaddr = lp->mii.phy_id;
+ int my_phy_caps; /* My PHY capabilities */
+ int my_ad_caps; /* My Advertised capabilities */
+ int status;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__);
+
+ /*
+ * We should not be called if phy_type is zero.
+ */
+ if (lp->phy_type == 0)
+ return;
+
+ if (smc911x_phy_reset(dev, phyaddr)) {
+ printk("%s: PHY reset timed out\n", dev->name);
+ return;
+ }
+ spin_lock_irqsave(&lp->lock, flags);
+
+ /*
+ * Enable PHY Interrupts (for register 18)
+ * Interrupts listed here are enabled
+ */
+ SMC_SET_PHY_INT_MASK(lp, phyaddr, PHY_INT_MASK_ENERGY_ON_ |
+ PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_REMOTE_FAULT_ |
+ PHY_INT_MASK_LINK_DOWN_);
+
+ /* If the user requested no auto neg, then go set his request */
+ if (lp->mii.force_media) {
+ smc911x_phy_fixed(dev);
+ goto smc911x_phy_configure_exit;
+ }
+
+ /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
+ SMC_GET_PHY_BMSR(lp, phyaddr, my_phy_caps);
+ if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
+ printk(KERN_INFO "Auto negotiation NOT supported\n");
+ smc911x_phy_fixed(dev);
+ goto smc911x_phy_configure_exit;
+ }
+
+ /* CSMA capable w/ both pauses */
+ my_ad_caps = ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+
+ if (my_phy_caps & BMSR_100BASE4)
+ my_ad_caps |= ADVERTISE_100BASE4;
+ if (my_phy_caps & BMSR_100FULL)
+ my_ad_caps |= ADVERTISE_100FULL;
+ if (my_phy_caps & BMSR_100HALF)
+ my_ad_caps |= ADVERTISE_100HALF;
+ if (my_phy_caps & BMSR_10FULL)
+ my_ad_caps |= ADVERTISE_10FULL;
+ if (my_phy_caps & BMSR_10HALF)
+ my_ad_caps |= ADVERTISE_10HALF;
+
+ /* Disable capabilities not selected by our user */
+ if (lp->ctl_rspeed != 100)
+ my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF);
+
+ if (!lp->ctl_rfduplx)
+ my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL);
+
+ /* Update our Auto-Neg Advertisement Register */
+ SMC_SET_PHY_MII_ADV(lp, phyaddr, my_ad_caps);
+ lp->mii.advertising = my_ad_caps;
+
+ /*
+ * Read the register back. Without this, it appears that when
+ * auto-negotiation is restarted, sometimes it isn't ready and
+ * the link does not come up.
+ */
+ udelay(10);
+ SMC_GET_PHY_MII_ADV(lp, phyaddr, status);
+
+ DBG(SMC_DEBUG_MISC, "%s: phy caps=0x%04x\n", dev->name, my_phy_caps);
+ DBG(SMC_DEBUG_MISC, "%s: phy advertised caps=0x%04x\n", dev->name, my_ad_caps);
+
+ /* Restart auto-negotiation process in order to advertise my caps */
+ SMC_SET_PHY_BMCR(lp, phyaddr, BMCR_ANENABLE | BMCR_ANRESTART);
+
+ smc911x_phy_check_media(dev, 1);
+
+smc911x_phy_configure_exit:
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+/*
+ * smc911x_phy_interrupt
+ *
+ * Purpose: Handle interrupts relating to PHY register 18. This is
+ * called from the "hard" interrupt handler under our private spinlock.
+ */
+static void smc911x_phy_interrupt(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int phyaddr = lp->mii.phy_id;
+ int status;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ if (lp->phy_type == 0)
+ return;
+
+ smc911x_phy_check_media(dev, 0);
+ /* read to clear status bits */
+ SMC_GET_PHY_INT_SRC(lp, phyaddr,status);
+ DBG(SMC_DEBUG_MISC, "%s: PHY interrupt status 0x%04x\n",
+ dev->name, status & 0xffff);
+ DBG(SMC_DEBUG_MISC, "%s: AFC_CFG 0x%08x\n",
+ dev->name, SMC_GET_AFC_CFG(lp));
+}
+
+/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
+
+/*
+ * This is the main routine of the driver, to handle the device when
+ * it needs some attention.
+ */
+static irqreturn_t smc911x_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int status, mask, timeout;
+ unsigned int rx_overrun=0, cr, pkts;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+
+ /* Spurious interrupt check */
+ if ((SMC_GET_IRQ_CFG(lp) & (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) !=
+ (INT_CFG_IRQ_INT_ | INT_CFG_IRQ_EN_)) {
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return IRQ_NONE;
+ }
+
+ mask = SMC_GET_INT_EN(lp);
+ SMC_SET_INT_EN(lp, 0);
+
+ /* set a timeout value, so I don't stay here forever */
+ timeout = 8;
+
+
+ do {
+ status = SMC_GET_INT(lp);
+
+ DBG(SMC_DEBUG_MISC, "%s: INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n",
+ dev->name, status, mask, status & ~mask);
+
+ status &= mask;
+ if (!status)
+ break;
+
+ /* Handle SW interrupt condition */
+ if (status & INT_STS_SW_INT_) {
+ SMC_ACK_INT(lp, INT_STS_SW_INT_);
+ mask &= ~INT_EN_SW_INT_EN_;
+ }
+ /* Handle various error conditions */
+ if (status & INT_STS_RXE_) {
+ SMC_ACK_INT(lp, INT_STS_RXE_);
+ dev->stats.rx_errors++;
+ }
+ if (status & INT_STS_RXDFH_INT_) {
+ SMC_ACK_INT(lp, INT_STS_RXDFH_INT_);
+ dev->stats.rx_dropped+=SMC_GET_RX_DROP(lp);
+ }
+ /* Undocumented interrupt-what is the right thing to do here? */
+ if (status & INT_STS_RXDF_INT_) {
+ SMC_ACK_INT(lp, INT_STS_RXDF_INT_);
+ }
+
+ /* Rx Data FIFO exceeds set level */
+ if (status & INT_STS_RDFL_) {
+ if (IS_REV_A(lp->revision)) {
+ rx_overrun=1;
+ SMC_GET_MAC_CR(lp, cr);
+ cr &= ~MAC_CR_RXEN_;
+ SMC_SET_MAC_CR(lp, cr);
+ DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+ dev->stats.rx_errors++;
+ dev->stats.rx_fifo_errors++;
+ }
+ SMC_ACK_INT(lp, INT_STS_RDFL_);
+ }
+ if (status & INT_STS_RDFO_) {
+ if (!IS_REV_A(lp->revision)) {
+ SMC_GET_MAC_CR(lp, cr);
+ cr &= ~MAC_CR_RXEN_;
+ SMC_SET_MAC_CR(lp, cr);
+ rx_overrun=1;
+ DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name);
+ dev->stats.rx_errors++;
+ dev->stats.rx_fifo_errors++;
+ }
+ SMC_ACK_INT(lp, INT_STS_RDFO_);
+ }
+ /* Handle receive condition */
+ if ((status & INT_STS_RSFL_) || rx_overrun) {
+ unsigned int fifo;
+ DBG(SMC_DEBUG_RX, "%s: RX irq\n", dev->name);
+ fifo = SMC_GET_RX_FIFO_INF(lp);
+ pkts = (fifo & RX_FIFO_INF_RXSUSED_) >> 16;
+ DBG(SMC_DEBUG_RX, "%s: Rx FIFO pkts %d, bytes %d\n",
+ dev->name, pkts, fifo & 0xFFFF );
+ if (pkts != 0) {
+#ifdef SMC_USE_DMA
+ unsigned int fifo;
+ if (lp->rxdma_active){
+ DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
+ "%s: RX DMA active\n", dev->name);
+ /* The DMA is already running so up the IRQ threshold */
+ fifo = SMC_GET_FIFO_INT(lp) & ~0xFF;
+ fifo |= pkts & 0xFF;
+ DBG(SMC_DEBUG_RX,
+ "%s: Setting RX stat FIFO threshold to %d\n",
+ dev->name, fifo & 0xff);
+ SMC_SET_FIFO_INT(lp, fifo);
+ } else
+#endif
+ smc911x_rcv(dev);
+ }
+ SMC_ACK_INT(lp, INT_STS_RSFL_);
+ }
+ /* Handle transmit FIFO available */
+ if (status & INT_STS_TDFA_) {
+ DBG(SMC_DEBUG_TX, "%s: TX data FIFO space available irq\n", dev->name);
+ SMC_SET_FIFO_TDA(lp, 0xFF);
+ lp->tx_throttle = 0;
+#ifdef SMC_USE_DMA
+ if (!lp->txdma_active)
+#endif
+ netif_wake_queue(dev);
+ SMC_ACK_INT(lp, INT_STS_TDFA_);
+ }
+ /* Handle transmit done condition */
+#if 1
+ if (status & (INT_STS_TSFL_ | INT_STS_GPT_INT_)) {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC,
+ "%s: Tx stat FIFO limit (%d) /GPT irq\n",
+ dev->name, (SMC_GET_FIFO_INT(lp) & 0x00ff0000) >> 16);
+ smc911x_tx(dev);
+ SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
+ SMC_ACK_INT(lp, INT_STS_TSFL_);
+ SMC_ACK_INT(lp, INT_STS_TSFL_ | INT_STS_GPT_INT_);
+ }
+#else
+ if (status & INT_STS_TSFL_) {
+ DBG(SMC_DEBUG_TX, "%s: TX status FIFO limit (%d) irq\n", dev->name, );
+ smc911x_tx(dev);
+ SMC_ACK_INT(lp, INT_STS_TSFL_);
+ }
+
+ if (status & INT_STS_GPT_INT_) {
+ DBG(SMC_DEBUG_RX, "%s: IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n",
+ dev->name,
+ SMC_GET_IRQ_CFG(lp),
+ SMC_GET_FIFO_INT(lp),
+ SMC_GET_RX_CFG(lp));
+ DBG(SMC_DEBUG_RX, "%s: Rx Stat FIFO Used 0x%02x "
+ "Data FIFO Used 0x%04x Stat FIFO 0x%08x\n",
+ dev->name,
+ (SMC_GET_RX_FIFO_INF(lp) & 0x00ff0000) >> 16,
+ SMC_GET_RX_FIFO_INF(lp) & 0xffff,
+ SMC_GET_RX_STS_FIFO_PEEK(lp));
+ SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000);
+ SMC_ACK_INT(lp, INT_STS_GPT_INT_);
+ }
+#endif
+
+ /* Handle PHY interrupt condition */
+ if (status & INT_STS_PHY_INT_) {
+ DBG(SMC_DEBUG_MISC, "%s: PHY irq\n", dev->name);
+ smc911x_phy_interrupt(dev);
+ SMC_ACK_INT(lp, INT_STS_PHY_INT_);
+ }
+ } while (--timeout);
+
+ /* restore mask state */
+ SMC_SET_INT_EN(lp, mask);
+
+ DBG(SMC_DEBUG_MISC, "%s: Interrupt done (%d loops)\n",
+ dev->name, 8-timeout);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef SMC_USE_DMA
+static void
+smc911x_tx_dma_irq(int dma, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct smc911x_local *lp = netdev_priv(dev);
+ struct sk_buff *skb = lp->current_tx_skb;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: TX DMA irq handler\n", dev->name);
+ /* Clear the DMA interrupt sources */
+ SMC_DMA_ACK_IRQ(dev, dma);
+ BUG_ON(skb == NULL);
+ dma_unmap_single(NULL, tx_dmabuf, tx_dmalen, DMA_TO_DEVICE);
+ dev->trans_start = jiffies;
+ dev_kfree_skb_irq(skb);
+ lp->current_tx_skb = NULL;
+ if (lp->pending_tx_skb != NULL)
+ smc911x_hardware_send_pkt(dev);
+ else {
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
+ "%s: No pending Tx packets. DMA disabled\n", dev->name);
+ spin_lock_irqsave(&lp->lock, flags);
+ lp->txdma_active = 0;
+ if (!lp->tx_throttle) {
+ netif_wake_queue(dev);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+
+ DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA,
+ "%s: TX DMA irq completed\n", dev->name);
+}
+static void
+smc911x_rx_dma_irq(int dma, void *data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ unsigned long ioaddr = dev->base_addr;
+ struct smc911x_local *lp = netdev_priv(dev);
+ struct sk_buff *skb = lp->current_rx_skb;
+ unsigned long flags;
+ unsigned int pkts;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+ DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA irq handler\n", dev->name);
+ /* Clear the DMA interrupt sources */
+ SMC_DMA_ACK_IRQ(dev, dma);
+ dma_unmap_single(NULL, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE);
+ BUG_ON(skb == NULL);
+ lp->current_rx_skb = NULL;
+ PRINT_PKT(skb->data, skb->len);
+ skb->protocol = eth_type_trans(skb, dev);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ netif_rx(skb);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ pkts = (SMC_GET_RX_FIFO_INF(lp) & RX_FIFO_INF_RXSUSED_) >> 16;
+ if (pkts != 0) {
+ smc911x_rcv(dev);
+ }else {
+ lp->rxdma_active = 0;
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+ DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA,
+ "%s: RX DMA irq completed. DMA RX FIFO PKTS %d\n",
+ dev->name, pkts);
+}
+#endif /* SMC_USE_DMA */
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void smc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smc911x_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+/* Our watchdog timed out. Called by the networking layer */
+static void smc911x_timeout(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int status, mask;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ status = SMC_GET_INT(lp);
+ mask = SMC_GET_INT_EN(lp);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ DBG(SMC_DEBUG_MISC, "%s: INT 0x%02x MASK 0x%02x\n",
+ dev->name, status, mask);
+
+ /* Dump the current TX FIFO contents and restart */
+ mask = SMC_GET_TX_CFG(lp);
+ SMC_SET_TX_CFG(lp, mask | TX_CFG_TXS_DUMP_ | TX_CFG_TXD_DUMP_);
+ /*
+ * Reconfiguring the PHY doesn't seem like a bad idea here, but
+ * smc911x_phy_configure() calls msleep() which calls schedule_timeout()
+ * which calls schedule(). Hence we use a work queue.
+ */
+ if (lp->phy_type != 0)
+ schedule_work(&lp->phy_configure);
+
+ /* We can accept TX packets again */
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void smc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int multicast_table[2];
+ unsigned int mcr, update_multicast = 0;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MAC_CR(lp, mcr);
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ if (dev->flags & IFF_PROMISC) {
+
+ DBG(SMC_DEBUG_MISC, "%s: RCR_PRMS\n", dev->name);
+ mcr |= MAC_CR_PRMS_;
+ }
+ /*
+ * Here, I am setting this to accept all multicast packets.
+ * I don't need to zero the multicast table, because the flag is
+ * checked before the table is
+ */
+ else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) {
+ DBG(SMC_DEBUG_MISC, "%s: RCR_ALMUL\n", dev->name);
+ mcr |= MAC_CR_MCPAS_;
+ }
+
+ /*
+ * This sets the internal hardware table to filter out unwanted
+ * multicast packets before they take up memory.
+ *
+ * The SMC chip uses a hash table where the high 6 bits of the CRC of
+ * address are the offset into the table. If that bit is 1, then the
+ * multicast packet is accepted. Otherwise, it's dropped silently.
+ *
+ * To use the 6 bits as an offset into the table, the high 1 bit is
+ * the number of the 32 bit register, while the low 5 bits are the bit
+ * within that register.
+ */
+ else if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+
+ /* Set the Hash perfec mode */
+ mcr |= MAC_CR_HPFILT_;
+
+ /* start with a table of all zeros: reject all */
+ memset(multicast_table, 0, sizeof(multicast_table));
+
+ netdev_for_each_mc_addr(ha, dev) {
+ u32 position;
+
+ /* upper 6 bits are used as hash index */
+ position = ether_crc(ETH_ALEN, ha->addr)>>26;
+
+ multicast_table[position>>5] |= 1 << (position&0x1f);
+ }
+
+ /* be sure I get rid of flags I might have set */
+ mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ /* now, the table can be loaded into the chipset */
+ update_multicast = 1;
+ } else {
+ DBG(SMC_DEBUG_MISC, "%s: ~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n",
+ dev->name);
+ mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ /*
+ * since I'm disabling all multicast entirely, I need to
+ * clear the multicast list
+ */
+ memset(multicast_table, 0, sizeof(multicast_table));
+ update_multicast = 1;
+ }
+
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_SET_MAC_CR(lp, mcr);
+ if (update_multicast) {
+ DBG(SMC_DEBUG_MISC,
+ "%s: update mcast hash table 0x%08x 0x%08x\n",
+ dev->name, multicast_table[0], multicast_table[1]);
+ SMC_SET_HASHL(lp, multicast_table[0]);
+ SMC_SET_HASHH(lp, multicast_table[1]);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int
+smc911x_open(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ /*
+ * Check that the address is valid. If its not, refuse
+ * to bring the device up. The user must specify an
+ * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+ */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ PRINTK("%s: no valid ethernet hw addr\n", __func__);
+ return -EINVAL;
+ }
+
+ /* reset the hardware */
+ smc911x_reset(dev);
+
+ /* Configure the PHY, initialize the link state */
+ smc911x_phy_configure(&lp->phy_configure);
+
+ /* Turn on Tx + Rx */
+ smc911x_enable(dev);
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * smc911x_close
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world. Caused by
+ * an 'ifconfig ethX down'
+ */
+static int smc911x_close(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ /* clear everything */
+ smc911x_shutdown(dev);
+
+ if (lp->phy_type != 0) {
+ /* We need to ensure that no calls to
+ * smc911x_phy_configure are pending.
+ */
+ cancel_work_sync(&lp->phy_configure);
+ smc911x_phy_powerdown(dev, lp->mii.phy_id);
+ }
+
+ if (lp->pending_tx_skb) {
+ dev_kfree_skb(lp->pending_tx_skb);
+ lp->pending_tx_skb = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Ethtool support
+ */
+static int
+smc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret, status;
+ unsigned long flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irqsave(&lp->lock, flags);
+ ret = mii_ethtool_gset(&lp->mii, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ } else {
+ cmd->supported = SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_TP | SUPPORTED_AUI;
+
+ if (lp->ctl_rspeed == 10)
+ ethtool_cmd_speed_set(cmd, SPEED_10);
+ else if (lp->ctl_rspeed == 100)
+ ethtool_cmd_speed_set(cmd, SPEED_100);
+
+ cmd->autoneg = AUTONEG_DISABLE;
+ if (lp->mii.phy_id==1)
+ cmd->transceiver = XCVR_INTERNAL;
+ else
+ cmd->transceiver = XCVR_EXTERNAL;
+ cmd->port = 0;
+ SMC_GET_PHY_SPECIAL(lp, lp->mii.phy_id, status);
+ cmd->duplex =
+ (status & (PHY_SPECIAL_SPD_10FULL_ | PHY_SPECIAL_SPD_100FULL_)) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+smc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret;
+ unsigned long flags;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irqsave(&lp->lock, flags);
+ ret = mii_ethtool_sset(&lp->mii, cmd);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ } else {
+ if (cmd->autoneg != AUTONEG_DISABLE ||
+ cmd->speed != SPEED_10 ||
+ (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) ||
+ (cmd->port != PORT_TP && cmd->port != PORT_AUI))
+ return -EINVAL;
+
+ lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void
+smc911x_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, CARDNAME, sizeof(info->driver));
+ strncpy(info->version, version, sizeof(info->version));
+ strncpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static int smc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret = -EINVAL;
+ unsigned long flags;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irqsave(&lp->lock, flags);
+ ret = mii_nway_restart(&lp->mii);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ }
+
+ return ret;
+}
+
+static u32 smc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ return lp->msg_enable;
+}
+
+static void smc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ lp->msg_enable = level;
+}
+
+static int smc911x_ethtool_getregslen(struct net_device *dev)
+{
+ /* System regs + MAC regs + PHY regs */
+ return (((E2P_CMD - ID_REV)/4 + 1) +
+ (WUCSR - MAC_CR)+1 + 32) * sizeof(u32);
+}
+
+static void smc911x_ethtool_getregs(struct net_device *dev,
+ struct ethtool_regs* regs, void *buf)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned long flags;
+ u32 reg,i,j=0;
+ u32 *data = (u32*)buf;
+
+ regs->version = lp->version;
+ for(i=ID_REV;i<=E2P_CMD;i+=4) {
+ data[j++] = SMC_inl(lp, i);
+ }
+ for(i=MAC_CR;i<=WUCSR;i++) {
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MAC_CSR(lp, i, reg);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ data[j++] = reg;
+ }
+ for(i=0;i<=31;i++) {
+ spin_lock_irqsave(&lp->lock, flags);
+ SMC_GET_MII(lp, i, lp->mii.phy_id, reg);
+ spin_unlock_irqrestore(&lp->lock, flags);
+ data[j++] = reg & 0xFFFF;
+ }
+}
+
+static int smc911x_ethtool_wait_eeprom_ready(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ unsigned int timeout;
+ int e2p_cmd;
+
+ e2p_cmd = SMC_GET_E2P_CMD(lp);
+ for(timeout=10;(e2p_cmd & E2P_CMD_EPC_BUSY_) && timeout; timeout--) {
+ if (e2p_cmd & E2P_CMD_EPC_TIMEOUT_) {
+ PRINTK("%s: %s timeout waiting for EEPROM to respond\n",
+ dev->name, __func__);
+ return -EFAULT;
+ }
+ mdelay(1);
+ e2p_cmd = SMC_GET_E2P_CMD(lp);
+ }
+ if (timeout == 0) {
+ PRINTK("%s: %s timeout waiting for EEPROM CMD not busy\n",
+ dev->name, __func__);
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static inline int smc911x_ethtool_write_eeprom_cmd(struct net_device *dev,
+ int cmd, int addr)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret;
+
+ if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+ return ret;
+ SMC_SET_E2P_CMD(lp, E2P_CMD_EPC_BUSY_ |
+ ((cmd) & (0x7<<28)) |
+ ((addr) & 0xFF));
+ return 0;
+}
+
+static inline int smc911x_ethtool_read_eeprom_byte(struct net_device *dev,
+ u8 *data)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret;
+
+ if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+ return ret;
+ *data = SMC_GET_E2P_DATA(lp);
+ return 0;
+}
+
+static inline int smc911x_ethtool_write_eeprom_byte(struct net_device *dev,
+ u8 data)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int ret;
+
+ if ((ret = smc911x_ethtool_wait_eeprom_ready(dev))!=0)
+ return ret;
+ SMC_SET_E2P_DATA(lp, data);
+ return 0;
+}
+
+static int smc911x_ethtool_geteeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ u8 eebuf[SMC911X_EEPROM_LEN];
+ int i, ret;
+
+ for(i=0;i<SMC911X_EEPROM_LEN;i++) {
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_READ_, i ))!=0)
+ return ret;
+ if ((ret=smc911x_ethtool_read_eeprom_byte(dev, &eebuf[i]))!=0)
+ return ret;
+ }
+ memcpy(data, eebuf+eeprom->offset, eeprom->len);
+ return 0;
+}
+
+static int smc911x_ethtool_seteeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int i, ret;
+
+ /* Enable erase */
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_EWEN_, 0 ))!=0)
+ return ret;
+ for(i=eeprom->offset;i<(eeprom->offset+eeprom->len);i++) {
+ /* erase byte */
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_ERASE_, i ))!=0)
+ return ret;
+ /* write byte */
+ if ((ret=smc911x_ethtool_write_eeprom_byte(dev, *data))!=0)
+ return ret;
+ if ((ret=smc911x_ethtool_write_eeprom_cmd(dev, E2P_CMD_EPC_CMD_WRITE_, i ))!=0)
+ return ret;
+ }
+ return 0;
+}
+
+static int smc911x_ethtool_geteeprom_len(struct net_device *dev)
+{
+ return SMC911X_EEPROM_LEN;
+}
+
+static const struct ethtool_ops smc911x_ethtool_ops = {
+ .get_settings = smc911x_ethtool_getsettings,
+ .set_settings = smc911x_ethtool_setsettings,
+ .get_drvinfo = smc911x_ethtool_getdrvinfo,
+ .get_msglevel = smc911x_ethtool_getmsglevel,
+ .set_msglevel = smc911x_ethtool_setmsglevel,
+ .nway_reset = smc911x_ethtool_nwayreset,
+ .get_link = ethtool_op_get_link,
+ .get_regs_len = smc911x_ethtool_getregslen,
+ .get_regs = smc911x_ethtool_getregs,
+ .get_eeprom_len = smc911x_ethtool_geteeprom_len,
+ .get_eeprom = smc911x_ethtool_geteeprom,
+ .set_eeprom = smc911x_ethtool_seteeprom,
+};
+
+/*
+ * smc911x_findirq
+ *
+ * This routine has a simple purpose -- make the SMC chip generate an
+ * interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ */
+static int __devinit smc911x_findirq(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int timeout = 20;
+ unsigned long cookie;
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+
+ cookie = probe_irq_on();
+
+ /*
+ * Force a SW interrupt
+ */
+
+ SMC_SET_INT_EN(lp, INT_EN_SW_INT_EN_);
+
+ /*
+ * Wait until positive that the interrupt has been generated
+ */
+ do {
+ int int_status;
+ udelay(10);
+ int_status = SMC_GET_INT_EN(lp);
+ if (int_status & INT_EN_SW_INT_EN_)
+ break; /* got the interrupt */
+ } while (--timeout);
+
+ /*
+ * there is really nothing that I can do here if timeout fails,
+ * as autoirq_report will return a 0 anyway, which is what I
+ * want in this case. Plus, the clean up is needed in both
+ * cases.
+ */
+
+ /* and disable all interrupts again */
+ SMC_SET_INT_EN(lp, 0);
+
+ /* and return what I found */
+ return probe_irq_off(cookie);
+}
+
+static const struct net_device_ops smc911x_netdev_ops = {
+ .ndo_open = smc911x_open,
+ .ndo_stop = smc911x_close,
+ .ndo_start_xmit = smc911x_hard_start_xmit,
+ .ndo_tx_timeout = smc911x_timeout,
+ .ndo_set_multicast_list = smc911x_set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = smc911x_poll_controller,
+#endif
+};
+
+/*
+ * Function: smc911x_probe(unsigned long ioaddr)
+ *
+ * Purpose:
+ * Tests to see if a given ioaddr points to an SMC911x chip.
+ * Returns a 0 on success
+ *
+ * Algorithm:
+ * (1) see if the endian word is OK
+ * (1) see if I recognize the chip ID in the appropriate register
+ *
+ * Here I do typical initialization tasks.
+ *
+ * o Initialize the structure if needed
+ * o print out my vanity message if not done so already
+ * o print out what type of hardware is detected
+ * o print out the ethernet address
+ * o find the IRQ
+ * o set up my private data
+ * o configure the dev structure with my subroutines
+ * o actually GRAB the irq.
+ * o GRAB the region
+ */
+static int __devinit smc911x_probe(struct net_device *dev)
+{
+ struct smc911x_local *lp = netdev_priv(dev);
+ int i, retval;
+ unsigned int val, chip_id, revision;
+ const char *version_string;
+ unsigned long irq_flags;
+
+ DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__);
+
+ /* First, see if the endian word is recognized */
+ val = SMC_GET_BYTE_TEST(lp);
+ DBG(SMC_DEBUG_MISC, "%s: endian probe returned 0x%04x\n", CARDNAME, val);
+ if (val != 0x87654321) {
+ printk(KERN_ERR "Invalid chip endian 0x%08x\n",val);
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /*
+ * check if the revision register is something that I
+ * recognize. These might need to be added to later,
+ * as future revisions could be added.
+ */
+ chip_id = SMC_GET_PN(lp);
+ DBG(SMC_DEBUG_MISC, "%s: id probe returned 0x%04x\n", CARDNAME, chip_id);
+ for(i=0;chip_ids[i].id != 0; i++) {
+ if (chip_ids[i].id == chip_id) break;
+ }
+ if (!chip_ids[i].id) {
+ printk(KERN_ERR "Unknown chip ID %04x\n", chip_id);
+ retval = -ENODEV;
+ goto err_out;
+ }
+ version_string = chip_ids[i].name;
+
+ revision = SMC_GET_REV(lp);
+ DBG(SMC_DEBUG_MISC, "%s: revision = 0x%04x\n", CARDNAME, revision);
+
+ /* At this point I'll assume that the chip is an SMC911x. */
+ DBG(SMC_DEBUG_MISC, "%s: Found a %s\n", CARDNAME, chip_ids[i].name);
+
+ /* Validate the TX FIFO size requested */
+ if ((tx_fifo_kb < 2) || (tx_fifo_kb > 14)) {
+ printk(KERN_ERR "Invalid TX FIFO size requested %d\n", tx_fifo_kb);
+ retval = -EINVAL;
+ goto err_out;
+ }
+
+ /* fill in some of the fields */
+ lp->version = chip_ids[i].id;
+ lp->revision = revision;
+ lp->tx_fifo_kb = tx_fifo_kb;
+ /* Reverse calculate the RX FIFO size from the TX */
+ lp->tx_fifo_size=(lp->tx_fifo_kb<<10) - 512;
+ lp->rx_fifo_size= ((0x4000 - 512 - lp->tx_fifo_size) / 16) * 15;
+
+ /* Set the automatic flow control values */
+ switch(lp->tx_fifo_kb) {
+ /*
+ * AFC_HI is about ((Rx Data Fifo Size)*2/3)/64
+ * AFC_LO is AFC_HI/2
+ * BACK_DUR is about 5uS*(AFC_LO) rounded down
+ */
+ case 2:/* 13440 Rx Data Fifo Size */
+ lp->afc_cfg=0x008C46AF;break;
+ case 3:/* 12480 Rx Data Fifo Size */
+ lp->afc_cfg=0x0082419F;break;
+ case 4:/* 11520 Rx Data Fifo Size */
+ lp->afc_cfg=0x00783C9F;break;
+ case 5:/* 10560 Rx Data Fifo Size */
+ lp->afc_cfg=0x006E374F;break;
+ case 6:/* 9600 Rx Data Fifo Size */
+ lp->afc_cfg=0x0064328F;break;
+ case 7:/* 8640 Rx Data Fifo Size */
+ lp->afc_cfg=0x005A2D7F;break;
+ case 8:/* 7680 Rx Data Fifo Size */
+ lp->afc_cfg=0x0050287F;break;
+ case 9:/* 6720 Rx Data Fifo Size */
+ lp->afc_cfg=0x0046236F;break;
+ case 10:/* 5760 Rx Data Fifo Size */
+ lp->afc_cfg=0x003C1E6F;break;
+ case 11:/* 4800 Rx Data Fifo Size */
+ lp->afc_cfg=0x0032195F;break;
+ /*
+ * AFC_HI is ~1520 bytes less than RX Data Fifo Size
+ * AFC_LO is AFC_HI/2
+ * BACK_DUR is about 5uS*(AFC_LO) rounded down
+ */
+ case 12:/* 3840 Rx Data Fifo Size */
+ lp->afc_cfg=0x0024124F;break;
+ case 13:/* 2880 Rx Data Fifo Size */
+ lp->afc_cfg=0x0015073F;break;
+ case 14:/* 1920 Rx Data Fifo Size */
+ lp->afc_cfg=0x0006032F;break;
+ default:
+ PRINTK("%s: ERROR -- no AFC_CFG setting found",
+ dev->name);
+ break;
+ }
+
+ DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX,
+ "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME,
+ lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg);
+
+ spin_lock_init(&lp->lock);
+
+ /* Get the MAC address */
+ SMC_GET_MAC_ADDR(lp, dev->dev_addr);
+
+ /* now, reset the chip, and put it into a known state */
+ smc911x_reset(dev);
+
+ /*
+ * If dev->irq is 0, then the device has to be banged on to see
+ * what the IRQ is.
+ *
+ * Specifying an IRQ is done with the assumption that the user knows
+ * what (s)he is doing. No checking is done!!!!
+ */
+ if (dev->irq < 1) {
+ int trials;
+
+ trials = 3;
+ while (trials--) {
+ dev->irq = smc911x_findirq(dev);
+ if (dev->irq)
+ break;
+ /* kick the card and try again */
+ smc911x_reset(dev);
+ }
+ }
+ if (dev->irq == 0) {
+ printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
+ dev->name);
+ retval = -ENODEV;
+ goto err_out;
+ }
+ dev->irq = irq_canonicalize(dev->irq);
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ dev->netdev_ops = &smc911x_netdev_ops;
+ dev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ dev->ethtool_ops = &smc911x_ethtool_ops;
+
+ INIT_WORK(&lp->phy_configure, smc911x_phy_configure);
+ lp->mii.phy_id_mask = 0x1f;
+ lp->mii.reg_num_mask = 0x1f;
+ lp->mii.force_media = 0;
+ lp->mii.full_duplex = 0;
+ lp->mii.dev = dev;
+ lp->mii.mdio_read = smc911x_phy_read;
+ lp->mii.mdio_write = smc911x_phy_write;
+
+ /*
+ * Locate the phy, if any.
+ */
+ smc911x_phy_detect(dev);
+
+ /* Set default parameters */
+ lp->msg_enable = NETIF_MSG_LINK;
+ lp->ctl_rfduplx = 1;
+ lp->ctl_rspeed = 100;
+
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+ irq_flags = lp->cfg.irq_flags;
+#else
+ irq_flags = IRQF_SHARED | SMC_IRQ_SENSE;
+#endif
+
+ /* Grab the IRQ */
+ retval = request_irq(dev->irq, smc911x_interrupt,
+ irq_flags, dev->name, dev);
+ if (retval)
+ goto err_out;
+
+#ifdef SMC_USE_DMA
+ lp->rxdma = SMC_DMA_REQUEST(dev, smc911x_rx_dma_irq);
+ lp->txdma = SMC_DMA_REQUEST(dev, smc911x_tx_dma_irq);
+ lp->rxdma_active = 0;
+ lp->txdma_active = 0;
+ dev->dma = lp->rxdma;
+#endif
+
+ retval = register_netdev(dev);
+ if (retval == 0) {
+ /* now, print out the card info, in a short format.. */
+ printk("%s: %s (rev %d) at %#lx IRQ %d",
+ dev->name, version_string, lp->revision,
+ dev->base_addr, dev->irq);
+
+#ifdef SMC_USE_DMA
+ if (lp->rxdma != -1)
+ printk(" RXDMA %d ", lp->rxdma);
+
+ if (lp->txdma != -1)
+ printk("TXDMA %d", lp->txdma);
+#endif
+ printk("\n");
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ printk("%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", dev->name);
+ } else {
+ /* Print the Ethernet address */
+ printk("%s: Ethernet addr: %pM\n",
+ dev->name, dev->dev_addr);
+ }
+
+ if (lp->phy_type == 0) {
+ PRINTK("%s: No PHY found\n", dev->name);
+ } else if ((lp->phy_type & ~0xff) == LAN911X_INTERNAL_PHY_ID) {
+ PRINTK("%s: LAN911x Internal PHY\n", dev->name);
+ } else {
+ PRINTK("%s: External PHY 0x%08x\n", dev->name, lp->phy_type);
+ }
+ }
+
+err_out:
+#ifdef SMC_USE_DMA
+ if (retval) {
+ if (lp->rxdma != -1) {
+ SMC_DMA_FREE(dev, lp->rxdma);
+ }
+ if (lp->txdma != -1) {
+ SMC_DMA_FREE(dev, lp->txdma);
+ }
+ }
+#endif
+ return retval;
+}
+
+/*
+ * smc911x_init(void)
+ *
+ * Output:
+ * 0 --> there is a device
+ * anything else, error
+ */
+static int __devinit smc911x_drv_probe(struct platform_device *pdev)
+{
+ struct net_device *ndev;
+ struct resource *res;
+ struct smc911x_local *lp;
+ unsigned int *addr;
+ int ret;
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /*
+ * Request the regions.
+ */
+ if (!request_mem_region(res->start, SMC911X_IO_EXTENT, CARDNAME)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ndev = alloc_etherdev(sizeof(struct smc911x_local));
+ if (!ndev) {
+ printk("%s: could not allocate device.\n", CARDNAME);
+ ret = -ENOMEM;
+ goto release_1;
+ }
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ ndev->dma = (unsigned char)-1;
+ ndev->irq = platform_get_irq(pdev, 0);
+ lp = netdev_priv(ndev);
+ lp->netdev = ndev;
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+ {
+ struct smc911x_platdata *pd = pdev->dev.platform_data;
+ if (!pd) {
+ ret = -EINVAL;
+ goto release_both;
+ }
+ memcpy(&lp->cfg, pd, sizeof(lp->cfg));
+ }
+#endif
+
+ addr = ioremap(res->start, SMC911X_IO_EXTENT);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto release_both;
+ }
+
+ platform_set_drvdata(pdev, ndev);
+ lp->base = addr;
+ ndev->base_addr = res->start;
+ ret = smc911x_probe(ndev);
+ if (ret != 0) {
+ platform_set_drvdata(pdev, NULL);
+ iounmap(addr);
+release_both:
+ free_netdev(ndev);
+release_1:
+ release_mem_region(res->start, SMC911X_IO_EXTENT);
+out:
+ printk("%s: not found (%d).\n", CARDNAME, ret);
+ }
+#ifdef SMC_USE_DMA
+ else {
+ lp->physaddr = res->start;
+ lp->dev = &pdev->dev;
+ }
+#endif
+
+ return ret;
+}
+
+static int __devexit smc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct smc911x_local *lp = netdev_priv(ndev);
+ struct resource *res;
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+
+ free_irq(ndev->irq, ndev);
+
+#ifdef SMC_USE_DMA
+ {
+ if (lp->rxdma != -1) {
+ SMC_DMA_FREE(dev, lp->rxdma);
+ }
+ if (lp->txdma != -1) {
+ SMC_DMA_FREE(dev, lp->txdma);
+ }
+ }
+#endif
+ iounmap(lp->base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, SMC911X_IO_EXTENT);
+
+ free_netdev(ndev);
+ return 0;
+}
+
+static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+ struct smc911x_local *lp = netdev_priv(ndev);
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+ if (ndev) {
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ smc911x_shutdown(ndev);
+#if POWER_DOWN
+ /* Set D2 - Energy detect only setting */
+ SMC_SET_PMT_CTRL(lp, 2<<12);
+#endif
+ }
+ }
+ return 0;
+}
+
+static int smc911x_drv_resume(struct platform_device *dev)
+{
+ struct net_device *ndev = platform_get_drvdata(dev);
+
+ DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__);
+ if (ndev) {
+ struct smc911x_local *lp = netdev_priv(ndev);
+
+ if (netif_running(ndev)) {
+ smc911x_reset(ndev);
+ if (lp->phy_type != 0)
+ smc911x_phy_configure(&lp->phy_configure);
+ smc911x_enable(ndev);
+ netif_device_attach(ndev);
+ }
+ }
+ return 0;
+}
+
+static struct platform_driver smc911x_driver = {
+ .probe = smc911x_drv_probe,
+ .remove = __devexit_p(smc911x_drv_remove),
+ .suspend = smc911x_drv_suspend,
+ .resume = smc911x_drv_resume,
+ .driver = {
+ .name = CARDNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init smc911x_init(void)
+{
+ return platform_driver_register(&smc911x_driver);
+}
+
+static void __exit smc911x_cleanup(void)
+{
+ platform_driver_unregister(&smc911x_driver);
+}
+
+module_init(smc911x_init);
+module_exit(smc911x_cleanup);
diff --git a/drivers/net/ethernet/smsc/smc911x.h b/drivers/net/ethernet/smsc/smc911x.h
new file mode 100644
index 000000000000..3269292efecc
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc911x.h
@@ -0,0 +1,924 @@
+/*------------------------------------------------------------------------
+ . smc911x.h - macros for SMSC's LAN911{5,6,7,8} single-chip Ethernet device.
+ .
+ . Copyright (C) 2005 Sensoria Corp.
+ . Derived from the unified SMC91x driver by Nicolas Pitre
+ .
+ . This program is free software; you can redistribute it and/or modify
+ . it under the terms of the GNU General Public License as published by
+ . the Free Software Foundation; either version 2 of the License, or
+ . (at your option) any later version.
+ .
+ . This program is distributed in the hope that it will be useful,
+ . but WITHOUT ANY WARRANTY; without even the implied warranty of
+ . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ . GNU General Public License for more details.
+ .
+ . You should have received a copy of the GNU General Public License
+ . along with this program; if not, write to the Free Software
+ . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ .
+ . Information contained in this file was obtained from the LAN9118
+ . manual from SMC. To get a copy, if you really want one, you can find
+ . information under www.smsc.com.
+ .
+ . Authors
+ . Dustin McIntire <dustin@sensoria.com>
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC911X_H_
+#define _SMC911X_H_
+
+#include <linux/smc911x.h>
+/*
+ * Use the DMA feature on PXA chips
+ */
+#ifdef CONFIG_ARCH_PXA
+ #define SMC_USE_PXA_DMA 1
+ #define SMC_USE_16BIT 0
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_FALLING
+#elif defined(CONFIG_SH_MAGIC_PANEL_R2)
+ #define SMC_USE_16BIT 0
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW
+#elif defined(CONFIG_ARCH_OMAP3)
+ #define SMC_USE_16BIT 0
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW
+ #define SMC_MEM_RESERVED 1
+#elif defined(CONFIG_ARCH_OMAP2)
+ #define SMC_USE_16BIT 0
+ #define SMC_USE_32BIT 1
+ #define SMC_IRQ_SENSE IRQF_TRIGGER_LOW
+ #define SMC_MEM_RESERVED 1
+#else
+/*
+ * Default configuration
+ */
+
+#define SMC_DYNAMIC_BUS_CONFIG
+#endif
+
+#ifdef SMC_USE_PXA_DMA
+#define SMC_USE_DMA
+#endif
+
+/* store this information for the driver.. */
+struct smc911x_local {
+ /*
+ * If I have to wait until the DMA is finished and ready to reload a
+ * packet, I will store the skbuff here. Then, the DMA will send it
+ * out and free it.
+ */
+ struct sk_buff *pending_tx_skb;
+
+ /* version/revision of the SMC911x chip */
+ u16 version;
+ u16 revision;
+
+ /* FIFO sizes */
+ int tx_fifo_kb;
+ int tx_fifo_size;
+ int rx_fifo_size;
+ int afc_cfg;
+
+ /* Contains the current active receive/phy mode */
+ int ctl_rfduplx;
+ int ctl_rspeed;
+
+ u32 msg_enable;
+ u32 phy_type;
+ struct mii_if_info mii;
+
+ /* work queue */
+ struct work_struct phy_configure;
+
+ int tx_throttle;
+ spinlock_t lock;
+
+ struct net_device *netdev;
+
+#ifdef SMC_USE_DMA
+ /* DMA needs the physical address of the chip */
+ u_long physaddr;
+ int rxdma;
+ int txdma;
+ int rxdma_active;
+ int txdma_active;
+ struct sk_buff *current_rx_skb;
+ struct sk_buff *current_tx_skb;
+ struct device *dev;
+#endif
+ void __iomem *base;
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+ struct smc911x_platdata cfg;
+#endif
+};
+
+/*
+ * Define the bus width specific IO macros
+ */
+
+#ifdef SMC_DYNAMIC_BUS_CONFIG
+static inline unsigned int SMC_inl(struct smc911x_local *lp, int reg)
+{
+ void __iomem *ioaddr = lp->base + reg;
+
+ if (lp->cfg.flags & SMC911X_USE_32BIT)
+ return readl(ioaddr);
+
+ if (lp->cfg.flags & SMC911X_USE_16BIT)
+ return readw(ioaddr) | (readw(ioaddr + 2) << 16);
+
+ BUG();
+}
+
+static inline void SMC_outl(unsigned int value, struct smc911x_local *lp,
+ int reg)
+{
+ void __iomem *ioaddr = lp->base + reg;
+
+ if (lp->cfg.flags & SMC911X_USE_32BIT) {
+ writel(value, ioaddr);
+ return;
+ }
+
+ if (lp->cfg.flags & SMC911X_USE_16BIT) {
+ writew(value & 0xffff, ioaddr);
+ writew(value >> 16, ioaddr + 2);
+ return;
+ }
+
+ BUG();
+}
+
+static inline void SMC_insl(struct smc911x_local *lp, int reg,
+ void *addr, unsigned int count)
+{
+ void __iomem *ioaddr = lp->base + reg;
+
+ if (lp->cfg.flags & SMC911X_USE_32BIT) {
+ readsl(ioaddr, addr, count);
+ return;
+ }
+
+ if (lp->cfg.flags & SMC911X_USE_16BIT) {
+ readsw(ioaddr, addr, count * 2);
+ return;
+ }
+
+ BUG();
+}
+
+static inline void SMC_outsl(struct smc911x_local *lp, int reg,
+ void *addr, unsigned int count)
+{
+ void __iomem *ioaddr = lp->base + reg;
+
+ if (lp->cfg.flags & SMC911X_USE_32BIT) {
+ writesl(ioaddr, addr, count);
+ return;
+ }
+
+ if (lp->cfg.flags & SMC911X_USE_16BIT) {
+ writesw(ioaddr, addr, count * 2);
+ return;
+ }
+
+ BUG();
+}
+#else
+#if SMC_USE_16BIT
+#define SMC_inl(lp, r) ((readw((lp)->base + (r)) & 0xFFFF) + (readw((lp)->base + (r) + 2) << 16))
+#define SMC_outl(v, lp, r) \
+ do{ \
+ writew(v & 0xFFFF, (lp)->base + (r)); \
+ writew(v >> 16, (lp)->base + (r) + 2); \
+ } while (0)
+#define SMC_insl(lp, r, p, l) readsw((short*)((lp)->base + (r)), p, l*2)
+#define SMC_outsl(lp, r, p, l) writesw((short*)((lp)->base + (r)), p, l*2)
+
+#elif SMC_USE_32BIT
+#define SMC_inl(lp, r) readl((lp)->base + (r))
+#define SMC_outl(v, lp, r) writel(v, (lp)->base + (r))
+#define SMC_insl(lp, r, p, l) readsl((int*)((lp)->base + (r)), p, l)
+#define SMC_outsl(lp, r, p, l) writesl((int*)((lp)->base + (r)), p, l)
+
+#endif /* SMC_USE_16BIT */
+#endif /* SMC_DYNAMIC_BUS_CONFIG */
+
+
+#ifdef SMC_USE_PXA_DMA
+
+#include <mach/dma.h>
+
+/*
+ * Define the request and free functions
+ * These are unfortunately architecture specific as no generic allocation
+ * mechanism exits
+ */
+#define SMC_DMA_REQUEST(dev, handler) \
+ pxa_request_dma(dev->name, DMA_PRIO_LOW, handler, dev)
+
+#define SMC_DMA_FREE(dev, dma) \
+ pxa_free_dma(dma)
+
+#define SMC_DMA_ACK_IRQ(dev, dma) \
+{ \
+ if (DCSR(dma) & DCSR_BUSERR) { \
+ printk("%s: DMA %d bus error!\n", dev->name, dma); \
+ } \
+ DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; \
+}
+
+/*
+ * Use a DMA for RX and TX packets.
+ */
+#include <linux/dma-mapping.h>
+
+static dma_addr_t rx_dmabuf, tx_dmabuf;
+static int rx_dmalen, tx_dmalen;
+
+#ifdef SMC_insl
+#undef SMC_insl
+#define SMC_insl(lp, r, p, l) \
+ smc_pxa_dma_insl(lp, lp->physaddr, r, lp->rxdma, p, l)
+
+static inline void
+smc_pxa_dma_insl(struct smc911x_local *lp, u_long physaddr,
+ int reg, int dma, u_char *buf, int len)
+{
+ /* 64 bit alignment is required for memory to memory DMA */
+ if ((long)buf & 4) {
+ *((u32 *)buf) = SMC_inl(lp, reg);
+ buf += 4;
+ len--;
+ }
+
+ len *= 4;
+ rx_dmabuf = dma_map_single(lp->dev, buf, len, DMA_FROM_DEVICE);
+ rx_dmalen = len;
+ DCSR(dma) = DCSR_NODESC;
+ DTADR(dma) = rx_dmabuf;
+ DSADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+ DCMD_WIDTH4 | DCMD_ENDIRQEN | (DCMD_LENGTH & rx_dmalen));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+
+#ifdef SMC_outsl
+#undef SMC_outsl
+#define SMC_outsl(lp, r, p, l) \
+ smc_pxa_dma_outsl(lp, lp->physaddr, r, lp->txdma, p, l)
+
+static inline void
+smc_pxa_dma_outsl(struct smc911x_local *lp, u_long physaddr,
+ int reg, int dma, u_char *buf, int len)
+{
+ /* 64 bit alignment is required for memory to memory DMA */
+ if ((long)buf & 4) {
+ SMC_outl(*((u32 *)buf), lp, reg);
+ buf += 4;
+ len--;
+ }
+
+ len *= 4;
+ tx_dmabuf = dma_map_single(lp->dev, buf, len, DMA_TO_DEVICE);
+ tx_dmalen = len;
+ DCSR(dma) = DCSR_NODESC;
+ DSADR(dma) = tx_dmabuf;
+ DTADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCSRCADDR | DCMD_BURST32 |
+ DCMD_WIDTH4 | DCMD_ENDIRQEN | (DCMD_LENGTH & tx_dmalen));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+}
+#endif
+#endif /* SMC_USE_PXA_DMA */
+
+
+/* Chip Parameters and Register Definitions */
+
+#define SMC911X_TX_FIFO_LOW_THRESHOLD (1536*2)
+
+#define SMC911X_IO_EXTENT 0x100
+
+#define SMC911X_EEPROM_LEN 7
+
+/* Below are the register offsets and bit definitions
+ * of the Lan911x memory space
+ */
+#define RX_DATA_FIFO (0x00)
+
+#define TX_DATA_FIFO (0x20)
+#define TX_CMD_A_INT_ON_COMP_ (0x80000000)
+#define TX_CMD_A_INT_BUF_END_ALGN_ (0x03000000)
+#define TX_CMD_A_INT_4_BYTE_ALGN_ (0x00000000)
+#define TX_CMD_A_INT_16_BYTE_ALGN_ (0x01000000)
+#define TX_CMD_A_INT_32_BYTE_ALGN_ (0x02000000)
+#define TX_CMD_A_INT_DATA_OFFSET_ (0x001F0000)
+#define TX_CMD_A_INT_FIRST_SEG_ (0x00002000)
+#define TX_CMD_A_INT_LAST_SEG_ (0x00001000)
+#define TX_CMD_A_BUF_SIZE_ (0x000007FF)
+#define TX_CMD_B_PKT_TAG_ (0xFFFF0000)
+#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000)
+#define TX_CMD_B_DISABLE_PADDING_ (0x00001000)
+#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FF)
+
+#define RX_STATUS_FIFO (0x40)
+#define RX_STS_PKT_LEN_ (0x3FFF0000)
+#define RX_STS_ES_ (0x00008000)
+#define RX_STS_BCST_ (0x00002000)
+#define RX_STS_LEN_ERR_ (0x00001000)
+#define RX_STS_RUNT_ERR_ (0x00000800)
+#define RX_STS_MCAST_ (0x00000400)
+#define RX_STS_TOO_LONG_ (0x00000080)
+#define RX_STS_COLL_ (0x00000040)
+#define RX_STS_ETH_TYPE_ (0x00000020)
+#define RX_STS_WDOG_TMT_ (0x00000010)
+#define RX_STS_MII_ERR_ (0x00000008)
+#define RX_STS_DRIBBLING_ (0x00000004)
+#define RX_STS_CRC_ERR_ (0x00000002)
+#define RX_STATUS_FIFO_PEEK (0x44)
+#define TX_STATUS_FIFO (0x48)
+#define TX_STS_TAG_ (0xFFFF0000)
+#define TX_STS_ES_ (0x00008000)
+#define TX_STS_LOC_ (0x00000800)
+#define TX_STS_NO_CARR_ (0x00000400)
+#define TX_STS_LATE_COLL_ (0x00000200)
+#define TX_STS_MANY_COLL_ (0x00000100)
+#define TX_STS_COLL_CNT_ (0x00000078)
+#define TX_STS_MANY_DEFER_ (0x00000004)
+#define TX_STS_UNDERRUN_ (0x00000002)
+#define TX_STS_DEFERRED_ (0x00000001)
+#define TX_STATUS_FIFO_PEEK (0x4C)
+#define ID_REV (0x50)
+#define ID_REV_CHIP_ID_ (0xFFFF0000) /* RO */
+#define ID_REV_REV_ID_ (0x0000FFFF) /* RO */
+
+#define INT_CFG (0x54)
+#define INT_CFG_INT_DEAS_ (0xFF000000) /* R/W */
+#define INT_CFG_INT_DEAS_CLR_ (0x00004000)
+#define INT_CFG_INT_DEAS_STS_ (0x00002000)
+#define INT_CFG_IRQ_INT_ (0x00001000) /* RO */
+#define INT_CFG_IRQ_EN_ (0x00000100) /* R/W */
+#define INT_CFG_IRQ_POL_ (0x00000010) /* R/W Not Affected by SW Reset */
+#define INT_CFG_IRQ_TYPE_ (0x00000001) /* R/W Not Affected by SW Reset */
+
+#define INT_STS (0x58)
+#define INT_STS_SW_INT_ (0x80000000) /* R/WC */
+#define INT_STS_TXSTOP_INT_ (0x02000000) /* R/WC */
+#define INT_STS_RXSTOP_INT_ (0x01000000) /* R/WC */
+#define INT_STS_RXDFH_INT_ (0x00800000) /* R/WC */
+#define INT_STS_RXDF_INT_ (0x00400000) /* R/WC */
+#define INT_STS_TX_IOC_ (0x00200000) /* R/WC */
+#define INT_STS_RXD_INT_ (0x00100000) /* R/WC */
+#define INT_STS_GPT_INT_ (0x00080000) /* R/WC */
+#define INT_STS_PHY_INT_ (0x00040000) /* RO */
+#define INT_STS_PME_INT_ (0x00020000) /* R/WC */
+#define INT_STS_TXSO_ (0x00010000) /* R/WC */
+#define INT_STS_RWT_ (0x00008000) /* R/WC */
+#define INT_STS_RXE_ (0x00004000) /* R/WC */
+#define INT_STS_TXE_ (0x00002000) /* R/WC */
+//#define INT_STS_ERX_ (0x00001000) /* R/WC */
+#define INT_STS_TDFU_ (0x00000800) /* R/WC */
+#define INT_STS_TDFO_ (0x00000400) /* R/WC */
+#define INT_STS_TDFA_ (0x00000200) /* R/WC */
+#define INT_STS_TSFF_ (0x00000100) /* R/WC */
+#define INT_STS_TSFL_ (0x00000080) /* R/WC */
+//#define INT_STS_RXDF_ (0x00000040) /* R/WC */
+#define INT_STS_RDFO_ (0x00000040) /* R/WC */
+#define INT_STS_RDFL_ (0x00000020) /* R/WC */
+#define INT_STS_RSFF_ (0x00000010) /* R/WC */
+#define INT_STS_RSFL_ (0x00000008) /* R/WC */
+#define INT_STS_GPIO2_INT_ (0x00000004) /* R/WC */
+#define INT_STS_GPIO1_INT_ (0x00000002) /* R/WC */
+#define INT_STS_GPIO0_INT_ (0x00000001) /* R/WC */
+
+#define INT_EN (0x5C)
+#define INT_EN_SW_INT_EN_ (0x80000000) /* R/W */
+#define INT_EN_TXSTOP_INT_EN_ (0x02000000) /* R/W */
+#define INT_EN_RXSTOP_INT_EN_ (0x01000000) /* R/W */
+#define INT_EN_RXDFH_INT_EN_ (0x00800000) /* R/W */
+//#define INT_EN_RXDF_INT_EN_ (0x00400000) /* R/W */
+#define INT_EN_TIOC_INT_EN_ (0x00200000) /* R/W */
+#define INT_EN_RXD_INT_EN_ (0x00100000) /* R/W */
+#define INT_EN_GPT_INT_EN_ (0x00080000) /* R/W */
+#define INT_EN_PHY_INT_EN_ (0x00040000) /* R/W */
+#define INT_EN_PME_INT_EN_ (0x00020000) /* R/W */
+#define INT_EN_TXSO_EN_ (0x00010000) /* R/W */
+#define INT_EN_RWT_EN_ (0x00008000) /* R/W */
+#define INT_EN_RXE_EN_ (0x00004000) /* R/W */
+#define INT_EN_TXE_EN_ (0x00002000) /* R/W */
+//#define INT_EN_ERX_EN_ (0x00001000) /* R/W */
+#define INT_EN_TDFU_EN_ (0x00000800) /* R/W */
+#define INT_EN_TDFO_EN_ (0x00000400) /* R/W */
+#define INT_EN_TDFA_EN_ (0x00000200) /* R/W */
+#define INT_EN_TSFF_EN_ (0x00000100) /* R/W */
+#define INT_EN_TSFL_EN_ (0x00000080) /* R/W */
+//#define INT_EN_RXDF_EN_ (0x00000040) /* R/W */
+#define INT_EN_RDFO_EN_ (0x00000040) /* R/W */
+#define INT_EN_RDFL_EN_ (0x00000020) /* R/W */
+#define INT_EN_RSFF_EN_ (0x00000010) /* R/W */
+#define INT_EN_RSFL_EN_ (0x00000008) /* R/W */
+#define INT_EN_GPIO2_INT_ (0x00000004) /* R/W */
+#define INT_EN_GPIO1_INT_ (0x00000002) /* R/W */
+#define INT_EN_GPIO0_INT_ (0x00000001) /* R/W */
+
+#define BYTE_TEST (0x64)
+#define FIFO_INT (0x68)
+#define FIFO_INT_TX_AVAIL_LEVEL_ (0xFF000000) /* R/W */
+#define FIFO_INT_TX_STS_LEVEL_ (0x00FF0000) /* R/W */
+#define FIFO_INT_RX_AVAIL_LEVEL_ (0x0000FF00) /* R/W */
+#define FIFO_INT_RX_STS_LEVEL_ (0x000000FF) /* R/W */
+
+#define RX_CFG (0x6C)
+#define RX_CFG_RX_END_ALGN_ (0xC0000000) /* R/W */
+#define RX_CFG_RX_END_ALGN4_ (0x00000000) /* R/W */
+#define RX_CFG_RX_END_ALGN16_ (0x40000000) /* R/W */
+#define RX_CFG_RX_END_ALGN32_ (0x80000000) /* R/W */
+#define RX_CFG_RX_DMA_CNT_ (0x0FFF0000) /* R/W */
+#define RX_CFG_RX_DUMP_ (0x00008000) /* R/W */
+#define RX_CFG_RXDOFF_ (0x00001F00) /* R/W */
+//#define RX_CFG_RXBAD_ (0x00000001) /* R/W */
+
+#define TX_CFG (0x70)
+//#define TX_CFG_TX_DMA_LVL_ (0xE0000000) /* R/W */
+//#define TX_CFG_TX_DMA_CNT_ (0x0FFF0000) /* R/W Self Clearing */
+#define TX_CFG_TXS_DUMP_ (0x00008000) /* Self Clearing */
+#define TX_CFG_TXD_DUMP_ (0x00004000) /* Self Clearing */
+#define TX_CFG_TXSAO_ (0x00000004) /* R/W */
+#define TX_CFG_TX_ON_ (0x00000002) /* R/W */
+#define TX_CFG_STOP_TX_ (0x00000001) /* Self Clearing */
+
+#define HW_CFG (0x74)
+#define HW_CFG_TTM_ (0x00200000) /* R/W */
+#define HW_CFG_SF_ (0x00100000) /* R/W */
+#define HW_CFG_TX_FIF_SZ_ (0x000F0000) /* R/W */
+#define HW_CFG_TR_ (0x00003000) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_ (0x00000060) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ (0x00000000) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ (0x00000020) /* R/W */
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ (0x00000040) /* R/W */
+#define HW_CFG_SMI_SEL_ (0x00000010) /* R/W */
+#define HW_CFG_EXT_PHY_DET_ (0x00000008) /* RO */
+#define HW_CFG_EXT_PHY_EN_ (0x00000004) /* R/W */
+#define HW_CFG_32_16_BIT_MODE_ (0x00000004) /* RO */
+#define HW_CFG_SRST_TO_ (0x00000002) /* RO */
+#define HW_CFG_SRST_ (0x00000001) /* Self Clearing */
+
+#define RX_DP_CTRL (0x78)
+#define RX_DP_CTRL_RX_FFWD_ (0x80000000) /* R/W */
+#define RX_DP_CTRL_FFWD_BUSY_ (0x80000000) /* RO */
+
+#define RX_FIFO_INF (0x7C)
+#define RX_FIFO_INF_RXSUSED_ (0x00FF0000) /* RO */
+#define RX_FIFO_INF_RXDUSED_ (0x0000FFFF) /* RO */
+
+#define TX_FIFO_INF (0x80)
+#define TX_FIFO_INF_TSUSED_ (0x00FF0000) /* RO */
+#define TX_FIFO_INF_TDFREE_ (0x0000FFFF) /* RO */
+
+#define PMT_CTRL (0x84)
+#define PMT_CTRL_PM_MODE_ (0x00003000) /* Self Clearing */
+#define PMT_CTRL_PHY_RST_ (0x00000400) /* Self Clearing */
+#define PMT_CTRL_WOL_EN_ (0x00000200) /* R/W */
+#define PMT_CTRL_ED_EN_ (0x00000100) /* R/W */
+#define PMT_CTRL_PME_TYPE_ (0x00000040) /* R/W Not Affected by SW Reset */
+#define PMT_CTRL_WUPS_ (0x00000030) /* R/WC */
+#define PMT_CTRL_WUPS_NOWAKE_ (0x00000000) /* R/WC */
+#define PMT_CTRL_WUPS_ED_ (0x00000010) /* R/WC */
+#define PMT_CTRL_WUPS_WOL_ (0x00000020) /* R/WC */
+#define PMT_CTRL_WUPS_MULTI_ (0x00000030) /* R/WC */
+#define PMT_CTRL_PME_IND_ (0x00000008) /* R/W */
+#define PMT_CTRL_PME_POL_ (0x00000004) /* R/W */
+#define PMT_CTRL_PME_EN_ (0x00000002) /* R/W Not Affected by SW Reset */
+#define PMT_CTRL_READY_ (0x00000001) /* RO */
+
+#define GPIO_CFG (0x88)
+#define GPIO_CFG_LED3_EN_ (0x40000000) /* R/W */
+#define GPIO_CFG_LED2_EN_ (0x20000000) /* R/W */
+#define GPIO_CFG_LED1_EN_ (0x10000000) /* R/W */
+#define GPIO_CFG_GPIO2_INT_POL_ (0x04000000) /* R/W */
+#define GPIO_CFG_GPIO1_INT_POL_ (0x02000000) /* R/W */
+#define GPIO_CFG_GPIO0_INT_POL_ (0x01000000) /* R/W */
+#define GPIO_CFG_EEPR_EN_ (0x00700000) /* R/W */
+#define GPIO_CFG_GPIOBUF2_ (0x00040000) /* R/W */
+#define GPIO_CFG_GPIOBUF1_ (0x00020000) /* R/W */
+#define GPIO_CFG_GPIOBUF0_ (0x00010000) /* R/W */
+#define GPIO_CFG_GPIODIR2_ (0x00000400) /* R/W */
+#define GPIO_CFG_GPIODIR1_ (0x00000200) /* R/W */
+#define GPIO_CFG_GPIODIR0_ (0x00000100) /* R/W */
+#define GPIO_CFG_GPIOD4_ (0x00000010) /* R/W */
+#define GPIO_CFG_GPIOD3_ (0x00000008) /* R/W */
+#define GPIO_CFG_GPIOD2_ (0x00000004) /* R/W */
+#define GPIO_CFG_GPIOD1_ (0x00000002) /* R/W */
+#define GPIO_CFG_GPIOD0_ (0x00000001) /* R/W */
+
+#define GPT_CFG (0x8C)
+#define GPT_CFG_TIMER_EN_ (0x20000000) /* R/W */
+#define GPT_CFG_GPT_LOAD_ (0x0000FFFF) /* R/W */
+
+#define GPT_CNT (0x90)
+#define GPT_CNT_GPT_CNT_ (0x0000FFFF) /* RO */
+
+#define ENDIAN (0x98)
+#define FREE_RUN (0x9C)
+#define RX_DROP (0xA0)
+#define MAC_CSR_CMD (0xA4)
+#define MAC_CSR_CMD_CSR_BUSY_ (0x80000000) /* Self Clearing */
+#define MAC_CSR_CMD_R_NOT_W_ (0x40000000) /* R/W */
+#define MAC_CSR_CMD_CSR_ADDR_ (0x000000FF) /* R/W */
+
+#define MAC_CSR_DATA (0xA8)
+#define AFC_CFG (0xAC)
+#define AFC_CFG_AFC_HI_ (0x00FF0000) /* R/W */
+#define AFC_CFG_AFC_LO_ (0x0000FF00) /* R/W */
+#define AFC_CFG_BACK_DUR_ (0x000000F0) /* R/W */
+#define AFC_CFG_FCMULT_ (0x00000008) /* R/W */
+#define AFC_CFG_FCBRD_ (0x00000004) /* R/W */
+#define AFC_CFG_FCADD_ (0x00000002) /* R/W */
+#define AFC_CFG_FCANY_ (0x00000001) /* R/W */
+
+#define E2P_CMD (0xB0)
+#define E2P_CMD_EPC_BUSY_ (0x80000000) /* Self Clearing */
+#define E2P_CMD_EPC_CMD_ (0x70000000) /* R/W */
+#define E2P_CMD_EPC_CMD_READ_ (0x00000000) /* R/W */
+#define E2P_CMD_EPC_CMD_EWDS_ (0x10000000) /* R/W */
+#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000) /* R/W */
+#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000) /* R/W */
+#define E2P_CMD_EPC_CMD_WRAL_ (0x40000000) /* R/W */
+#define E2P_CMD_EPC_CMD_ERASE_ (0x50000000) /* R/W */
+#define E2P_CMD_EPC_CMD_ERAL_ (0x60000000) /* R/W */
+#define E2P_CMD_EPC_CMD_RELOAD_ (0x70000000) /* R/W */
+#define E2P_CMD_EPC_TIMEOUT_ (0x00000200) /* RO */
+#define E2P_CMD_MAC_ADDR_LOADED_ (0x00000100) /* RO */
+#define E2P_CMD_EPC_ADDR_ (0x000000FF) /* R/W */
+
+#define E2P_DATA (0xB4)
+#define E2P_DATA_EEPROM_DATA_ (0x000000FF) /* R/W */
+/* end of LAN register offsets and bit definitions */
+
+/*
+ ****************************************************************************
+ ****************************************************************************
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ ****************************************************************************
+ ****************************************************************************
+ *
+ */
+#define MAC_CR (0x01) /* R/W */
+
+/* MAC_CR - MAC Control Register */
+#define MAC_CR_RXALL_ (0x80000000)
+// TODO: delete this bit? It is not described in the data sheet.
+#define MAC_CR_HBDIS_ (0x10000000)
+#define MAC_CR_RCVOWN_ (0x00800000)
+#define MAC_CR_LOOPBK_ (0x00200000)
+#define MAC_CR_FDPX_ (0x00100000)
+#define MAC_CR_MCPAS_ (0x00080000)
+#define MAC_CR_PRMS_ (0x00040000)
+#define MAC_CR_INVFILT_ (0x00020000)
+#define MAC_CR_PASSBAD_ (0x00010000)
+#define MAC_CR_HFILT_ (0x00008000)
+#define MAC_CR_HPFILT_ (0x00002000)
+#define MAC_CR_LCOLL_ (0x00001000)
+#define MAC_CR_BCAST_ (0x00000800)
+#define MAC_CR_DISRTY_ (0x00000400)
+#define MAC_CR_PADSTR_ (0x00000100)
+#define MAC_CR_BOLMT_MASK_ (0x000000C0)
+#define MAC_CR_DFCHK_ (0x00000020)
+#define MAC_CR_TXEN_ (0x00000008)
+#define MAC_CR_RXEN_ (0x00000004)
+
+#define ADDRH (0x02) /* R/W mask 0x0000FFFFUL */
+#define ADDRL (0x03) /* R/W mask 0xFFFFFFFFUL */
+#define HASHH (0x04) /* R/W */
+#define HASHL (0x05) /* R/W */
+
+#define MII_ACC (0x06) /* R/W */
+#define MII_ACC_PHY_ADDR_ (0x0000F800)
+#define MII_ACC_MIIRINDA_ (0x000007C0)
+#define MII_ACC_MII_WRITE_ (0x00000002)
+#define MII_ACC_MII_BUSY_ (0x00000001)
+
+#define MII_DATA (0x07) /* R/W mask 0x0000FFFFUL */
+
+#define FLOW (0x08) /* R/W */
+#define FLOW_FCPT_ (0xFFFF0000)
+#define FLOW_FCPASS_ (0x00000004)
+#define FLOW_FCEN_ (0x00000002)
+#define FLOW_FCBSY_ (0x00000001)
+
+#define VLAN1 (0x09) /* R/W mask 0x0000FFFFUL */
+#define VLAN1_VTI1_ (0x0000ffff)
+
+#define VLAN2 (0x0A) /* R/W mask 0x0000FFFFUL */
+#define VLAN2_VTI2_ (0x0000ffff)
+
+#define WUFF (0x0B) /* WO */
+
+#define WUCSR (0x0C) /* R/W */
+#define WUCSR_GUE_ (0x00000200)
+#define WUCSR_WUFR_ (0x00000040)
+#define WUCSR_MPR_ (0x00000020)
+#define WUCSR_WAKE_EN_ (0x00000004)
+#define WUCSR_MPEN_ (0x00000002)
+
+/*
+ ****************************************************************************
+ * Chip Specific MII Defines
+ ****************************************************************************
+ *
+ * Phy register offsets and bit definitions
+ *
+ */
+
+#define PHY_MODE_CTRL_STS ((u32)17) /* Mode Control/Status Register */
+//#define MODE_CTRL_STS_FASTRIP_ ((u16)0x4000)
+#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000)
+//#define MODE_CTRL_STS_LOWSQEN_ ((u16)0x0800)
+//#define MODE_CTRL_STS_MDPREBP_ ((u16)0x0400)
+//#define MODE_CTRL_STS_FARLOOPBACK_ ((u16)0x0200)
+//#define MODE_CTRL_STS_FASTEST_ ((u16)0x0100)
+//#define MODE_CTRL_STS_REFCLKEN_ ((u16)0x0010)
+//#define MODE_CTRL_STS_PHYADBP_ ((u16)0x0008)
+//#define MODE_CTRL_STS_FORCE_G_LINK_ ((u16)0x0004)
+#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002)
+
+#define PHY_INT_SRC ((u32)29)
+#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_SRC_ANEG_LP_ACK_ ((u16)0x0008)
+#define PHY_INT_SRC_PAR_DET_FAULT_ ((u16)0x0004)
+#define PHY_INT_SRC_ANEG_PGRX_ ((u16)0x0002)
+
+#define PHY_INT_MASK ((u32)30)
+#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_MASK_ANEG_LP_ACK_ ((u16)0x0008)
+#define PHY_INT_MASK_PAR_DET_FAULT_ ((u16)0x0004)
+#define PHY_INT_MASK_ANEG_PGRX_ ((u16)0x0002)
+
+#define PHY_SPECIAL ((u32)31)
+#define PHY_SPECIAL_ANEG_DONE_ ((u16)0x1000)
+#define PHY_SPECIAL_RES_ ((u16)0x0040)
+#define PHY_SPECIAL_RES_MASK_ ((u16)0x0FE1)
+#define PHY_SPECIAL_SPD_ ((u16)0x001C)
+#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004)
+#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014)
+#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008)
+#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018)
+
+#define LAN911X_INTERNAL_PHY_ID (0x0007C000)
+
+/* Chip ID values */
+#define CHIP_9115 0x0115
+#define CHIP_9116 0x0116
+#define CHIP_9117 0x0117
+#define CHIP_9118 0x0118
+#define CHIP_9211 0x9211
+#define CHIP_9215 0x115A
+#define CHIP_9217 0x117A
+#define CHIP_9218 0x118A
+
+struct chip_id {
+ u16 id;
+ char *name;
+};
+
+static const struct chip_id chip_ids[] = {
+ { CHIP_9115, "LAN9115" },
+ { CHIP_9116, "LAN9116" },
+ { CHIP_9117, "LAN9117" },
+ { CHIP_9118, "LAN9118" },
+ { CHIP_9211, "LAN9211" },
+ { CHIP_9215, "LAN9215" },
+ { CHIP_9217, "LAN9217" },
+ { CHIP_9218, "LAN9218" },
+ { 0, NULL },
+};
+
+#define IS_REV_A(x) ((x & 0xFFFF)==0)
+
+/*
+ * Macros to abstract register access according to the data bus
+ * capabilities. Please use those and not the in/out primitives.
+ */
+/* FIFO read/write macros */
+#define SMC_PUSH_DATA(lp, p, l) SMC_outsl( lp, TX_DATA_FIFO, p, (l) >> 2 )
+#define SMC_PULL_DATA(lp, p, l) SMC_insl ( lp, RX_DATA_FIFO, p, (l) >> 2 )
+#define SMC_SET_TX_FIFO(lp, x) SMC_outl( x, lp, TX_DATA_FIFO )
+#define SMC_GET_RX_FIFO(lp) SMC_inl( lp, RX_DATA_FIFO )
+
+
+/* I/O mapped register read/write macros */
+#define SMC_GET_TX_STS_FIFO(lp) SMC_inl( lp, TX_STATUS_FIFO )
+#define SMC_GET_RX_STS_FIFO(lp) SMC_inl( lp, RX_STATUS_FIFO )
+#define SMC_GET_RX_STS_FIFO_PEEK(lp) SMC_inl( lp, RX_STATUS_FIFO_PEEK )
+#define SMC_GET_PN(lp) (SMC_inl( lp, ID_REV ) >> 16)
+#define SMC_GET_REV(lp) (SMC_inl( lp, ID_REV ) & 0xFFFF)
+#define SMC_GET_IRQ_CFG(lp) SMC_inl( lp, INT_CFG )
+#define SMC_SET_IRQ_CFG(lp, x) SMC_outl( x, lp, INT_CFG )
+#define SMC_GET_INT(lp) SMC_inl( lp, INT_STS )
+#define SMC_ACK_INT(lp, x) SMC_outl( x, lp, INT_STS )
+#define SMC_GET_INT_EN(lp) SMC_inl( lp, INT_EN )
+#define SMC_SET_INT_EN(lp, x) SMC_outl( x, lp, INT_EN )
+#define SMC_GET_BYTE_TEST(lp) SMC_inl( lp, BYTE_TEST )
+#define SMC_SET_BYTE_TEST(lp, x) SMC_outl( x, lp, BYTE_TEST )
+#define SMC_GET_FIFO_INT(lp) SMC_inl( lp, FIFO_INT )
+#define SMC_SET_FIFO_INT(lp, x) SMC_outl( x, lp, FIFO_INT )
+#define SMC_SET_FIFO_TDA(lp, x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<24); \
+ SMC_SET_FIFO_INT( (lp), __mask | (x)<<24 ); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_SET_FIFO_TSL(lp, x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<16); \
+ SMC_SET_FIFO_INT( (lp), __mask | (((x) & 0xFF)<<16)); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_SET_FIFO_RSA(lp, x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT((lp)) & ~(0xFF<<8); \
+ SMC_SET_FIFO_INT( (lp), __mask | (((x) & 0xFF)<<8)); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_SET_FIFO_RSL(lp, x) \
+ do { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_GET_FIFO_INT((lp)) & ~0xFF; \
+ SMC_SET_FIFO_INT( (lp),__mask | ((x) & 0xFF)); \
+ local_irq_restore(__flags); \
+ } while (0)
+#define SMC_GET_RX_CFG(lp) SMC_inl( lp, RX_CFG )
+#define SMC_SET_RX_CFG(lp, x) SMC_outl( x, lp, RX_CFG )
+#define SMC_GET_TX_CFG(lp) SMC_inl( lp, TX_CFG )
+#define SMC_SET_TX_CFG(lp, x) SMC_outl( x, lp, TX_CFG )
+#define SMC_GET_HW_CFG(lp) SMC_inl( lp, HW_CFG )
+#define SMC_SET_HW_CFG(lp, x) SMC_outl( x, lp, HW_CFG )
+#define SMC_GET_RX_DP_CTRL(lp) SMC_inl( lp, RX_DP_CTRL )
+#define SMC_SET_RX_DP_CTRL(lp, x) SMC_outl( x, lp, RX_DP_CTRL )
+#define SMC_GET_PMT_CTRL(lp) SMC_inl( lp, PMT_CTRL )
+#define SMC_SET_PMT_CTRL(lp, x) SMC_outl( x, lp, PMT_CTRL )
+#define SMC_GET_GPIO_CFG(lp) SMC_inl( lp, GPIO_CFG )
+#define SMC_SET_GPIO_CFG(lp, x) SMC_outl( x, lp, GPIO_CFG )
+#define SMC_GET_RX_FIFO_INF(lp) SMC_inl( lp, RX_FIFO_INF )
+#define SMC_SET_RX_FIFO_INF(lp, x) SMC_outl( x, lp, RX_FIFO_INF )
+#define SMC_GET_TX_FIFO_INF(lp) SMC_inl( lp, TX_FIFO_INF )
+#define SMC_SET_TX_FIFO_INF(lp, x) SMC_outl( x, lp, TX_FIFO_INF )
+#define SMC_GET_GPT_CFG(lp) SMC_inl( lp, GPT_CFG )
+#define SMC_SET_GPT_CFG(lp, x) SMC_outl( x, lp, GPT_CFG )
+#define SMC_GET_RX_DROP(lp) SMC_inl( lp, RX_DROP )
+#define SMC_SET_RX_DROP(lp, x) SMC_outl( x, lp, RX_DROP )
+#define SMC_GET_MAC_CMD(lp) SMC_inl( lp, MAC_CSR_CMD )
+#define SMC_SET_MAC_CMD(lp, x) SMC_outl( x, lp, MAC_CSR_CMD )
+#define SMC_GET_MAC_DATA(lp) SMC_inl( lp, MAC_CSR_DATA )
+#define SMC_SET_MAC_DATA(lp, x) SMC_outl( x, lp, MAC_CSR_DATA )
+#define SMC_GET_AFC_CFG(lp) SMC_inl( lp, AFC_CFG )
+#define SMC_SET_AFC_CFG(lp, x) SMC_outl( x, lp, AFC_CFG )
+#define SMC_GET_E2P_CMD(lp) SMC_inl( lp, E2P_CMD )
+#define SMC_SET_E2P_CMD(lp, x) SMC_outl( x, lp, E2P_CMD )
+#define SMC_GET_E2P_DATA(lp) SMC_inl( lp, E2P_DATA )
+#define SMC_SET_E2P_DATA(lp, x) SMC_outl( x, lp, E2P_DATA )
+
+/* MAC register read/write macros */
+#define SMC_GET_MAC_CSR(lp,a,v) \
+ do { \
+ while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \
+ SMC_SET_MAC_CMD((lp),MAC_CSR_CMD_CSR_BUSY_ | \
+ MAC_CSR_CMD_R_NOT_W_ | (a) ); \
+ while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \
+ v = SMC_GET_MAC_DATA((lp)); \
+ } while (0)
+#define SMC_SET_MAC_CSR(lp,a,v) \
+ do { \
+ while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \
+ SMC_SET_MAC_DATA((lp), v); \
+ SMC_SET_MAC_CMD((lp), MAC_CSR_CMD_CSR_BUSY_ | (a) ); \
+ while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \
+ } while (0)
+#define SMC_GET_MAC_CR(lp, x) SMC_GET_MAC_CSR( (lp), MAC_CR, x )
+#define SMC_SET_MAC_CR(lp, x) SMC_SET_MAC_CSR( (lp), MAC_CR, x )
+#define SMC_GET_ADDRH(lp, x) SMC_GET_MAC_CSR( (lp), ADDRH, x )
+#define SMC_SET_ADDRH(lp, x) SMC_SET_MAC_CSR( (lp), ADDRH, x )
+#define SMC_GET_ADDRL(lp, x) SMC_GET_MAC_CSR( (lp), ADDRL, x )
+#define SMC_SET_ADDRL(lp, x) SMC_SET_MAC_CSR( (lp), ADDRL, x )
+#define SMC_GET_HASHH(lp, x) SMC_GET_MAC_CSR( (lp), HASHH, x )
+#define SMC_SET_HASHH(lp, x) SMC_SET_MAC_CSR( (lp), HASHH, x )
+#define SMC_GET_HASHL(lp, x) SMC_GET_MAC_CSR( (lp), HASHL, x )
+#define SMC_SET_HASHL(lp, x) SMC_SET_MAC_CSR( (lp), HASHL, x )
+#define SMC_GET_MII_ACC(lp, x) SMC_GET_MAC_CSR( (lp), MII_ACC, x )
+#define SMC_SET_MII_ACC(lp, x) SMC_SET_MAC_CSR( (lp), MII_ACC, x )
+#define SMC_GET_MII_DATA(lp, x) SMC_GET_MAC_CSR( (lp), MII_DATA, x )
+#define SMC_SET_MII_DATA(lp, x) SMC_SET_MAC_CSR( (lp), MII_DATA, x )
+#define SMC_GET_FLOW(lp, x) SMC_GET_MAC_CSR( (lp), FLOW, x )
+#define SMC_SET_FLOW(lp, x) SMC_SET_MAC_CSR( (lp), FLOW, x )
+#define SMC_GET_VLAN1(lp, x) SMC_GET_MAC_CSR( (lp), VLAN1, x )
+#define SMC_SET_VLAN1(lp, x) SMC_SET_MAC_CSR( (lp), VLAN1, x )
+#define SMC_GET_VLAN2(lp, x) SMC_GET_MAC_CSR( (lp), VLAN2, x )
+#define SMC_SET_VLAN2(lp, x) SMC_SET_MAC_CSR( (lp), VLAN2, x )
+#define SMC_SET_WUFF(lp, x) SMC_SET_MAC_CSR( (lp), WUFF, x )
+#define SMC_GET_WUCSR(lp, x) SMC_GET_MAC_CSR( (lp), WUCSR, x )
+#define SMC_SET_WUCSR(lp, x) SMC_SET_MAC_CSR( (lp), WUCSR, x )
+
+/* PHY register read/write macros */
+#define SMC_GET_MII(lp,a,phy,v) \
+ do { \
+ u32 __v; \
+ do { \
+ SMC_GET_MII_ACC((lp), __v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ SMC_SET_MII_ACC( (lp), ((phy)<<11) | ((a)<<6) | \
+ MII_ACC_MII_BUSY_); \
+ do { \
+ SMC_GET_MII_ACC( (lp), __v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ SMC_GET_MII_DATA((lp), v); \
+ } while (0)
+#define SMC_SET_MII(lp,a,phy,v) \
+ do { \
+ u32 __v; \
+ do { \
+ SMC_GET_MII_ACC((lp), __v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ SMC_SET_MII_DATA((lp), v); \
+ SMC_SET_MII_ACC( (lp), ((phy)<<11) | ((a)<<6) | \
+ MII_ACC_MII_BUSY_ | \
+ MII_ACC_MII_WRITE_ ); \
+ do { \
+ SMC_GET_MII_ACC((lp), __v); \
+ } while ( __v & MII_ACC_MII_BUSY_ ); \
+ } while (0)
+#define SMC_GET_PHY_BMCR(lp,phy,x) SMC_GET_MII( (lp), MII_BMCR, phy, x )
+#define SMC_SET_PHY_BMCR(lp,phy,x) SMC_SET_MII( (lp), MII_BMCR, phy, x )
+#define SMC_GET_PHY_BMSR(lp,phy,x) SMC_GET_MII( (lp), MII_BMSR, phy, x )
+#define SMC_GET_PHY_ID1(lp,phy,x) SMC_GET_MII( (lp), MII_PHYSID1, phy, x )
+#define SMC_GET_PHY_ID2(lp,phy,x) SMC_GET_MII( (lp), MII_PHYSID2, phy, x )
+#define SMC_GET_PHY_MII_ADV(lp,phy,x) SMC_GET_MII( (lp), MII_ADVERTISE, phy, x )
+#define SMC_SET_PHY_MII_ADV(lp,phy,x) SMC_SET_MII( (lp), MII_ADVERTISE, phy, x )
+#define SMC_GET_PHY_MII_LPA(lp,phy,x) SMC_GET_MII( (lp), MII_LPA, phy, x )
+#define SMC_SET_PHY_MII_LPA(lp,phy,x) SMC_SET_MII( (lp), MII_LPA, phy, x )
+#define SMC_GET_PHY_CTRL_STS(lp,phy,x) SMC_GET_MII( (lp), PHY_MODE_CTRL_STS, phy, x )
+#define SMC_SET_PHY_CTRL_STS(lp,phy,x) SMC_SET_MII( (lp), PHY_MODE_CTRL_STS, phy, x )
+#define SMC_GET_PHY_INT_SRC(lp,phy,x) SMC_GET_MII( (lp), PHY_INT_SRC, phy, x )
+#define SMC_SET_PHY_INT_SRC(lp,phy,x) SMC_SET_MII( (lp), PHY_INT_SRC, phy, x )
+#define SMC_GET_PHY_INT_MASK(lp,phy,x) SMC_GET_MII( (lp), PHY_INT_MASK, phy, x )
+#define SMC_SET_PHY_INT_MASK(lp,phy,x) SMC_SET_MII( (lp), PHY_INT_MASK, phy, x )
+#define SMC_GET_PHY_SPECIAL(lp,phy,x) SMC_GET_MII( (lp), PHY_SPECIAL, phy, x )
+
+
+
+/* Misc read/write macros */
+
+#ifndef SMC_GET_MAC_ADDR
+#define SMC_GET_MAC_ADDR(lp, addr) \
+ do { \
+ unsigned int __v; \
+ \
+ SMC_GET_MAC_CSR((lp), ADDRL, __v); \
+ addr[0] = __v; addr[1] = __v >> 8; \
+ addr[2] = __v >> 16; addr[3] = __v >> 24; \
+ SMC_GET_MAC_CSR((lp), ADDRH, __v); \
+ addr[4] = __v; addr[5] = __v >> 8; \
+ } while (0)
+#endif
+
+#define SMC_SET_MAC_ADDR(lp, addr) \
+ do { \
+ SMC_SET_MAC_CSR((lp), ADDRL, \
+ addr[0] | \
+ (addr[1] << 8) | \
+ (addr[2] << 16) | \
+ (addr[3] << 24)); \
+ SMC_SET_MAC_CSR((lp), ADDRH, addr[4]|(addr[5] << 8));\
+ } while (0)
+
+
+#define SMC_WRITE_EEPROM_CMD(lp, cmd, addr) \
+ do { \
+ while (SMC_GET_E2P_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \
+ SMC_SET_MAC_CMD((lp), MAC_CSR_CMD_R_NOT_W_ | a ); \
+ while (SMC_GET_MAC_CMD((lp)) & MAC_CSR_CMD_CSR_BUSY_); \
+ } while (0)
+
+#endif /* _SMC911X_H_ */
diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c
new file mode 100644
index 000000000000..5b65ac4b3cef
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc9194.c
@@ -0,0 +1,1589 @@
+/*------------------------------------------------------------------------
+ . smc9194.c
+ . This is a driver for SMC's 9000 series of Ethernet cards.
+ .
+ . Copyright (C) 1996 by Erik Stahlman
+ . This software may be used and distributed according to the terms
+ . of the GNU General Public License, incorporated herein by reference.
+ .
+ . "Features" of the SMC chip:
+ . 4608 byte packet memory. ( for the 91C92. Others have more )
+ . EEPROM for configuration
+ . AUI/TP selection ( mine has 10Base2/10BaseT select )
+ .
+ . Arguments:
+ . io = for the base address
+ . irq = for the IRQ
+ . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 )
+ .
+ . author:
+ . Erik Stahlman ( erik@vt.edu )
+ . contributors:
+ . Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ .
+ . Hardware multicast code from Peter Cammaert ( pc@denkart.be )
+ .
+ . Sources:
+ . o SMC databook
+ . o skeleton.c by Donald Becker ( becker@scyld.com )
+ . o ( a LOT of advice from Becker as well )
+ .
+ . History:
+ . 12/07/95 Erik Stahlman written, got receive/xmit handled
+ . 01/03/96 Erik Stahlman worked out some bugs, actually usable!!! :-)
+ . 01/06/96 Erik Stahlman cleaned up some, better testing, etc
+ . 01/29/96 Erik Stahlman fixed autoirq, added multicast
+ . 02/01/96 Erik Stahlman 1. disabled all interrupts in smc_reset
+ . 2. got rid of post-decrementing bug -- UGH.
+ . 02/13/96 Erik Stahlman Tried to fix autoirq failure. Added more
+ . descriptive error messages.
+ . 02/15/96 Erik Stahlman Fixed typo that caused detection failure
+ . 02/23/96 Erik Stahlman Modified it to fit into kernel tree
+ . Added support to change hardware address
+ . Cleared stats on opens
+ . 02/26/96 Erik Stahlman Trial support for Kernel 1.2.13
+ . Kludge for automatic IRQ detection
+ . 03/04/96 Erik Stahlman Fixed kernel 1.3.70 +
+ . Fixed bug reported by Gardner Buchanan in
+ . smc_enable, with outw instead of outb
+ . 03/06/96 Erik Stahlman Added hardware multicast from Peter Cammaert
+ . 04/14/00 Heiko Pruessing (SMA Regelsysteme) Fixed bug in chip memory
+ . allocation
+ . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet
+ . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ"
+ . 11/08/01 Matt Domsch Use common crc32 function
+ ----------------------------------------------------------------------------*/
+
+static const char version[] =
+ "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/crc32.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/bitops.h>
+
+#include <asm/io.h>
+
+#include "smc9194.h"
+
+#define DRV_NAME "smc9194"
+
+/*------------------------------------------------------------------------
+ .
+ . Configuration options, for the experienced user to change.
+ .
+ -------------------------------------------------------------------------*/
+
+/*
+ . Do you want to use 32 bit xfers? This should work on all chips, as
+ . the chipset is designed to accommodate them.
+*/
+#ifdef __sh__
+#undef USE_32_BIT
+#else
+#define USE_32_BIT 1
+#endif
+
+#if defined(__H8300H__) || defined(__H8300S__)
+#define NO_AUTOPROBE
+#undef insl
+#undef outsl
+#define insl(a,b,l) io_insl_noswap(a,b,l)
+#define outsl(a,b,l) io_outsl_noswap(a,b,l)
+#endif
+
+/*
+ .the SMC9194 can be at any of the following port addresses. To change,
+ .for a slightly different card, you can add it to the array. Keep in
+ .mind that the array must end in zero.
+*/
+
+struct devlist {
+ unsigned int port;
+ unsigned int irq;
+};
+
+#if defined(CONFIG_H8S_EDOSK2674)
+static struct devlist smc_devlist[] __initdata = {
+ {.port = 0xf80000, .irq = 16},
+ {.port = 0, .irq = 0 },
+};
+#else
+static struct devlist smc_devlist[] __initdata = {
+ {.port = 0x200, .irq = 0},
+ {.port = 0x220, .irq = 0},
+ {.port = 0x240, .irq = 0},
+ {.port = 0x260, .irq = 0},
+ {.port = 0x280, .irq = 0},
+ {.port = 0x2A0, .irq = 0},
+ {.port = 0x2C0, .irq = 0},
+ {.port = 0x2E0, .irq = 0},
+ {.port = 0x300, .irq = 0},
+ {.port = 0x320, .irq = 0},
+ {.port = 0x340, .irq = 0},
+ {.port = 0x360, .irq = 0},
+ {.port = 0x380, .irq = 0},
+ {.port = 0x3A0, .irq = 0},
+ {.port = 0x3C0, .irq = 0},
+ {.port = 0x3E0, .irq = 0},
+ {.port = 0, .irq = 0},
+};
+#endif
+/*
+ . Wait time for memory to be free. This probably shouldn't be
+ . tuned that much, as waiting for this means nothing else happens
+ . in the system
+*/
+#define MEMORY_WAIT_TIME 16
+
+/*
+ . DEBUGGING LEVELS
+ .
+ . 0 for normal operation
+ . 1 for slightly more details
+ . >2 for various levels of increasingly useless information
+ . 2 for interrupt tracking, status flags
+ . 3 for packet dumps, etc.
+*/
+#define SMC_DEBUG 0
+
+#if (SMC_DEBUG > 2 )
+#define PRINTK3(x) printk x
+#else
+#define PRINTK3(x)
+#endif
+
+#if SMC_DEBUG > 1
+#define PRINTK2(x) printk x
+#else
+#define PRINTK2(x)
+#endif
+
+#ifdef SMC_DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x)
+#endif
+
+
+/*------------------------------------------------------------------------
+ .
+ . The internal workings of the driver. If you are changing anything
+ . here with the SMC stuff, you should have the datasheet and known
+ . what you are doing.
+ .
+ -------------------------------------------------------------------------*/
+#define CARDNAME "SMC9194"
+
+
+/* store this information for the driver.. */
+struct smc_local {
+ /*
+ If I have to wait until memory is available to send
+ a packet, I will store the skbuff here, until I get the
+ desired memory. Then, I'll send it out and free it.
+ */
+ struct sk_buff * saved_skb;
+
+ /*
+ . This keeps track of how many packets that I have
+ . sent out. When an TX_EMPTY interrupt comes, I know
+ . that all of these have been sent.
+ */
+ int packets_waiting;
+};
+
+
+/*-----------------------------------------------------------------
+ .
+ . The driver can be entered at any of the following entry points.
+ .
+ .------------------------------------------------------------------ */
+
+/*
+ . This is called by register_netdev(). It is responsible for
+ . checking the portlist for the SMC9000 series chipset. If it finds
+ . one, then it will initialize the device, find the hardware information,
+ . and sets up the appropriate device parameters.
+ . NOTE: Interrupts are *OFF* when this procedure is called.
+ .
+ . NB:This shouldn't be static since it is referred to externally.
+*/
+struct net_device *smc_init(int unit);
+
+/*
+ . The kernel calls this function when someone wants to use the device,
+ . typically 'ifconfig ethX up'.
+*/
+static int smc_open(struct net_device *dev);
+
+/*
+ . Our watchdog timed out. Called by the networking layer
+*/
+static void smc_timeout(struct net_device *dev);
+
+/*
+ . This is called by the kernel in response to 'ifconfig ethX down'. It
+ . is responsible for cleaning up everything that the open routine
+ . does, and maybe putting the card into a powerdown state.
+*/
+static int smc_close(struct net_device *dev);
+
+/*
+ . Finally, a call to set promiscuous mode ( for TCPDUMP and related
+ . programs ) and multicast modes.
+*/
+static void smc_set_multicast_list(struct net_device *dev);
+
+
+/*---------------------------------------------------------------
+ .
+ . Interrupt level calls..
+ .
+ ----------------------------------------------------------------*/
+
+/*
+ . Handles the actual interrupt
+*/
+static irqreturn_t smc_interrupt(int irq, void *);
+/*
+ . This is a separate procedure to handle the receipt of a packet, to
+ . leave the interrupt code looking slightly cleaner
+*/
+static inline void smc_rcv( struct net_device *dev );
+/*
+ . This handles a TX interrupt, which is only called when an error
+ . relating to a packet is sent.
+*/
+static inline void smc_tx( struct net_device * dev );
+
+/*
+ ------------------------------------------------------------
+ .
+ . Internal routines
+ .
+ ------------------------------------------------------------
+*/
+
+/*
+ . Test if a given location contains a chip, trying to cause as
+ . little damage as possible if it's not a SMC chip.
+*/
+static int smc_probe(struct net_device *dev, int ioaddr);
+
+/*
+ . A rather simple routine to print out a packet for debugging purposes.
+*/
+#if SMC_DEBUG > 2
+static void print_packet( byte *, int );
+#endif
+
+#define tx_done(dev) 1
+
+/* this is called to actually send the packet to the chip */
+static void smc_hardware_send_packet( struct net_device * dev );
+
+/* Since I am not sure if I will have enough room in the chip's ram
+ . to store the packet, I call this routine, which either sends it
+ . now, or generates an interrupt when the card is ready for the
+ . packet */
+static netdev_tx_t smc_wait_to_send_packet( struct sk_buff * skb,
+ struct net_device *dev );
+
+/* this does a soft reset on the device */
+static void smc_reset( int ioaddr );
+
+/* Enable Interrupts, Receive, and Transmit */
+static void smc_enable( int ioaddr );
+
+/* this puts the device in an inactive state */
+static void smc_shutdown( int ioaddr );
+
+/* This routine will find the IRQ of the driver if one is not
+ . specified in the input to the device. */
+static int smc_findirq( int ioaddr );
+
+/*
+ . Function: smc_reset( int ioaddr )
+ . Purpose:
+ . This sets the SMC91xx chip to its normal state, hopefully from whatever
+ . mess that any other DOS driver has put it in.
+ .
+ . Maybe I should reset more registers to defaults in here? SOFTRESET should
+ . do that for me.
+ .
+ . Method:
+ . 1. send a SOFT RESET
+ . 2. wait for it to finish
+ . 3. enable autorelease mode
+ . 4. reset the memory management unit
+ . 5. clear all interrupts
+ .
+*/
+static void smc_reset( int ioaddr )
+{
+ /* This resets the registers mostly to defaults, but doesn't
+ affect EEPROM. That seems unnecessary */
+ SMC_SELECT_BANK( 0 );
+ outw( RCR_SOFTRESET, ioaddr + RCR );
+
+ /* this should pause enough for the chip to be happy */
+ SMC_DELAY( );
+
+ /* Set the transmit and receive configuration registers to
+ default values */
+ outw( RCR_CLEAR, ioaddr + RCR );
+ outw( TCR_CLEAR, ioaddr + TCR );
+
+ /* set the control register to automatically
+ release successfully transmitted packets, to make the best
+ use out of our limited memory */
+ SMC_SELECT_BANK( 1 );
+ outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL );
+
+ /* Reset the MMU */
+ SMC_SELECT_BANK( 2 );
+ outw( MC_RESET, ioaddr + MMU_CMD );
+
+ /* Note: It doesn't seem that waiting for the MMU busy is needed here,
+ but this is a place where future chipsets _COULD_ break. Be wary
+ of issuing another MMU command right after this */
+
+ outb( 0, ioaddr + INT_MASK );
+}
+
+/*
+ . Function: smc_enable
+ . Purpose: let the chip talk to the outside work
+ . Method:
+ . 1. Enable the transmitter
+ . 2. Enable the receiver
+ . 3. Enable interrupts
+*/
+static void smc_enable( int ioaddr )
+{
+ SMC_SELECT_BANK( 0 );
+ /* see the header file for options in TCR/RCR NORMAL*/
+ outw( TCR_NORMAL, ioaddr + TCR );
+ outw( RCR_NORMAL, ioaddr + RCR );
+
+ /* now, enable interrupts */
+ SMC_SELECT_BANK( 2 );
+ outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK );
+}
+
+/*
+ . Function: smc_shutdown
+ . Purpose: closes down the SMC91xxx chip.
+ . Method:
+ . 1. zero the interrupt mask
+ . 2. clear the enable receive flag
+ . 3. clear the enable xmit flags
+ .
+ . TODO:
+ . (1) maybe utilize power down mode.
+ . Why not yet? Because while the chip will go into power down mode,
+ . the manual says that it will wake up in response to any I/O requests
+ . in the register space. Empirical results do not show this working.
+*/
+static void smc_shutdown( int ioaddr )
+{
+ /* no more interrupts for me */
+ SMC_SELECT_BANK( 2 );
+ outb( 0, ioaddr + INT_MASK );
+
+ /* and tell the card to stay away from that nasty outside world */
+ SMC_SELECT_BANK( 0 );
+ outb( RCR_CLEAR, ioaddr + RCR );
+ outb( TCR_CLEAR, ioaddr + TCR );
+#if 0
+ /* finally, shut the chip down */
+ SMC_SELECT_BANK( 1 );
+ outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL );
+#endif
+}
+
+
+/*
+ . Function: smc_setmulticast( int ioaddr, struct net_device *dev )
+ . Purpose:
+ . This sets the internal hardware table to filter out unwanted multicast
+ . packets before they take up memory.
+ .
+ . The SMC chip uses a hash table where the high 6 bits of the CRC of
+ . address are the offset into the table. If that bit is 1, then the
+ . multicast packet is accepted. Otherwise, it's dropped silently.
+ .
+ . To use the 6 bits as an offset into the table, the high 3 bits are the
+ . number of the 8 bit register, while the low 3 bits are the bit within
+ . that register.
+ .
+ . This routine is based very heavily on the one provided by Peter Cammaert.
+*/
+
+
+static void smc_setmulticast(int ioaddr, struct net_device *dev)
+{
+ int i;
+ unsigned char multicast_table[ 8 ];
+ struct netdev_hw_addr *ha;
+ /* table for flipping the order of 3 bits */
+ unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+ /* start with a table of all zeros: reject all */
+ memset( multicast_table, 0, sizeof( multicast_table ) );
+
+ netdev_for_each_mc_addr(ha, dev) {
+ int position;
+
+ /* only use the low order bits */
+ position = ether_crc_le(6, ha->addr) & 0x3f;
+
+ /* do some messy swapping to put the bit in the right spot */
+ multicast_table[invert3[position&7]] |=
+ (1<<invert3[(position>>3)&7]);
+
+ }
+ /* now, the table can be loaded into the chipset */
+ SMC_SELECT_BANK( 3 );
+
+ for ( i = 0; i < 8 ; i++ ) {
+ outb( multicast_table[i], ioaddr + MULTICAST1 + i );
+ }
+}
+
+/*
+ . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * )
+ . Purpose:
+ . Attempt to allocate memory for a packet, if chip-memory is not
+ . available, then tell the card to generate an interrupt when it
+ . is available.
+ .
+ . Algorithm:
+ .
+ . o if the saved_skb is not currently null, then drop this packet
+ . on the floor. This should never happen, because of TBUSY.
+ . o if the saved_skb is null, then replace it with the current packet,
+ . o See if I can sending it now.
+ . o (NO): Enable interrupts and let the interrupt handler deal with it.
+ . o (YES):Send it now.
+*/
+static netdev_tx_t smc_wait_to_send_packet(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ word length;
+ unsigned short numPages;
+ word time_out;
+
+ netif_stop_queue(dev);
+ /* Well, I want to send the packet.. but I don't know
+ if I can send it right now... */
+
+ if ( lp->saved_skb) {
+ /* THIS SHOULD NEVER HAPPEN. */
+ dev->stats.tx_aborted_errors++;
+ printk(CARDNAME": Bad Craziness - sent packet while busy.\n" );
+ return NETDEV_TX_BUSY;
+ }
+ lp->saved_skb = skb;
+
+ length = skb->len;
+
+ if (length < ETH_ZLEN) {
+ if (skb_padto(skb, ETH_ZLEN)) {
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+ }
+ length = ETH_ZLEN;
+ }
+
+ /*
+ ** The MMU wants the number of pages to be the number of 256 bytes
+ ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
+ **
+ ** Pkt size for allocating is data length +6 (for additional status words,
+ ** length and ctl!) If odd size last byte is included in this header.
+ */
+ numPages = ((length & 0xfffe) + 6) / 256;
+
+ if (numPages > 7 ) {
+ printk(CARDNAME": Far too big packet error.\n");
+ /* freeing the packet is a good thing here... but should
+ . any packets of this size get down here? */
+ dev_kfree_skb (skb);
+ lp->saved_skb = NULL;
+ /* this IS an error, but, i don't want the skb saved */
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+ }
+ /* either way, a packet is waiting now */
+ lp->packets_waiting++;
+
+ /* now, try to allocate the memory */
+ SMC_SELECT_BANK( 2 );
+ outw( MC_ALLOC | numPages, ioaddr + MMU_CMD );
+ /*
+ . Performance Hack
+ .
+ . wait a short amount of time.. if I can send a packet now, I send
+ . it now. Otherwise, I enable an interrupt and wait for one to be
+ . available.
+ .
+ . I could have handled this a slightly different way, by checking to
+ . see if any memory was available in the FREE MEMORY register. However,
+ . either way, I need to generate an allocation, and the allocation works
+ . no matter what, so I saw no point in checking free memory.
+ */
+ time_out = MEMORY_WAIT_TIME;
+ do {
+ word status;
+
+ status = inb( ioaddr + INTERRUPT );
+ if ( status & IM_ALLOC_INT ) {
+ /* acknowledge the interrupt */
+ outb( IM_ALLOC_INT, ioaddr + INTERRUPT );
+ break;
+ }
+ } while ( -- time_out );
+
+ if ( !time_out ) {
+ /* oh well, wait until the chip finds memory later */
+ SMC_ENABLE_INT( IM_ALLOC_INT );
+ PRINTK2((CARDNAME": memory allocation deferred.\n"));
+ /* it's deferred, but I'll handle it later */
+ return NETDEV_TX_OK;
+ }
+ /* or YES! I can send the packet now.. */
+ smc_hardware_send_packet(dev);
+ netif_wake_queue(dev);
+ return NETDEV_TX_OK;
+}
+
+/*
+ . Function: smc_hardware_send_packet(struct net_device * )
+ . Purpose:
+ . This sends the actual packet to the SMC9xxx chip.
+ .
+ . Algorithm:
+ . First, see if a saved_skb is available.
+ . ( this should NOT be called if there is no 'saved_skb'
+ . Now, find the packet number that the chip allocated
+ . Point the data pointers at it in memory
+ . Set the length word in the chip's memory
+ . Dump the packet to chip memory
+ . Check if a last byte is needed ( odd length packet )
+ . if so, set the control flag right
+ . Tell the card to send it
+ . Enable the transmit interrupt, so I know if it failed
+ . Free the kernel data if I actually sent it.
+*/
+static void smc_hardware_send_packet( struct net_device * dev )
+{
+ struct smc_local *lp = netdev_priv(dev);
+ byte packet_no;
+ struct sk_buff * skb = lp->saved_skb;
+ word length;
+ unsigned int ioaddr;
+ byte * buf;
+
+ ioaddr = dev->base_addr;
+
+ if ( !skb ) {
+ PRINTK((CARDNAME": In XMIT with no packet to send\n"));
+ return;
+ }
+ length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ buf = skb->data;
+
+ /* If I get here, I _know_ there is a packet slot waiting for me */
+ packet_no = inb( ioaddr + PNR_ARR + 1 );
+ if ( packet_no & 0x80 ) {
+ /* or isn't there? BAD CHIP! */
+ printk(KERN_DEBUG CARDNAME": Memory allocation failed.\n");
+ dev_kfree_skb_any(skb);
+ lp->saved_skb = NULL;
+ netif_wake_queue(dev);
+ return;
+ }
+
+ /* we have a packet address, so tell the card to use it */
+ outb( packet_no, ioaddr + PNR_ARR );
+
+ /* point to the beginning of the packet */
+ outw( PTR_AUTOINC , ioaddr + POINTER );
+
+ PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length ));
+#if SMC_DEBUG > 2
+ print_packet( buf, length );
+#endif
+
+ /* send the packet length ( +6 for status, length and ctl byte )
+ and the status word ( set to zeros ) */
+#ifdef USE_32_BIT
+ outl( (length +6 ) << 16 , ioaddr + DATA_1 );
+#else
+ outw( 0, ioaddr + DATA_1 );
+ /* send the packet length ( +6 for status words, length, and ctl*/
+ outb( (length+6) & 0xFF,ioaddr + DATA_1 );
+ outb( (length+6) >> 8 , ioaddr + DATA_1 );
+#endif
+
+ /* send the actual data
+ . I _think_ it's faster to send the longs first, and then
+ . mop up by sending the last word. It depends heavily
+ . on alignment, at least on the 486. Maybe it would be
+ . a good idea to check which is optimal? But that could take
+ . almost as much time as is saved?
+ */
+#ifdef USE_32_BIT
+ if ( length & 0x2 ) {
+ outsl(ioaddr + DATA_1, buf, length >> 2 );
+#if !defined(__H8300H__) && !defined(__H8300S__)
+ outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
+#else
+ ctrl_outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1);
+#endif
+ }
+ else
+ outsl(ioaddr + DATA_1, buf, length >> 2 );
+#else
+ outsw(ioaddr + DATA_1 , buf, (length ) >> 1);
+#endif
+ /* Send the last byte, if there is one. */
+
+ if ( (length & 1) == 0 ) {
+ outw( 0, ioaddr + DATA_1 );
+ } else {
+ outb( buf[length -1 ], ioaddr + DATA_1 );
+ outb( 0x20, ioaddr + DATA_1);
+ }
+
+ /* enable the interrupts */
+ SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) );
+
+ /* and let the chipset deal with it */
+ outw( MC_ENQUEUE , ioaddr + MMU_CMD );
+
+ PRINTK2((CARDNAME": Sent packet of length %d\n", length));
+
+ lp->saved_skb = NULL;
+ dev_kfree_skb_any (skb);
+
+ dev->trans_start = jiffies;
+
+ /* we can send another packet */
+ netif_wake_queue(dev);
+}
+
+/*-------------------------------------------------------------------------
+ |
+ | smc_init(int unit)
+ | Input parameters:
+ | dev->base_addr == 0, try to find all possible locations
+ | dev->base_addr == 1, return failure code
+ | dev->base_addr == 2, always allocate space, and return success
+ | dev->base_addr == <anything else> this is the address to check
+ |
+ | Output:
+ | pointer to net_device or ERR_PTR(error)
+ |
+ ---------------------------------------------------------------------------
+*/
+static int io;
+static int irq;
+static int ifport;
+
+struct net_device * __init smc_init(int unit)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct smc_local));
+ struct devlist *smcdev = smc_devlist;
+ int err = 0;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ if (unit >= 0) {
+ sprintf(dev->name, "eth%d", unit);
+ netdev_boot_setup_check(dev);
+ io = dev->base_addr;
+ irq = dev->irq;
+ }
+
+ if (io > 0x1ff) { /* Check a single specified location. */
+ err = smc_probe(dev, io);
+ } else if (io != 0) { /* Don't probe at all. */
+ err = -ENXIO;
+ } else {
+ for (;smcdev->port; smcdev++) {
+ if (smc_probe(dev, smcdev->port) == 0)
+ break;
+ }
+ if (!smcdev->port)
+ err = -ENODEV;
+ }
+ if (err)
+ goto out;
+ err = register_netdev(dev);
+ if (err)
+ goto out1;
+ return dev;
+out1:
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, SMC_IO_EXTENT);
+out:
+ free_netdev(dev);
+ return ERR_PTR(err);
+}
+
+/*----------------------------------------------------------------------
+ . smc_findirq
+ .
+ . This routine has a simple purpose -- make the SMC chip generate an
+ . interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ ------------------------------------------------------------------------
+*/
+static int __init smc_findirq(int ioaddr)
+{
+#ifndef NO_AUTOPROBE
+ int timeout = 20;
+ unsigned long cookie;
+
+
+ cookie = probe_irq_on();
+
+ /*
+ * What I try to do here is trigger an ALLOC_INT. This is done
+ * by allocating a small chunk of memory, which will give an interrupt
+ * when done.
+ */
+
+
+ SMC_SELECT_BANK(2);
+ /* enable ALLOCation interrupts ONLY */
+ outb( IM_ALLOC_INT, ioaddr + INT_MASK );
+
+ /*
+ . Allocate 512 bytes of memory. Note that the chip was just
+ . reset so all the memory is available
+ */
+ outw( MC_ALLOC | 1, ioaddr + MMU_CMD );
+
+ /*
+ . Wait until positive that the interrupt has been generated
+ */
+ while ( timeout ) {
+ byte int_status;
+
+ int_status = inb( ioaddr + INTERRUPT );
+
+ if ( int_status & IM_ALLOC_INT )
+ break; /* got the interrupt */
+ timeout--;
+ }
+ /* there is really nothing that I can do here if timeout fails,
+ as probe_irq_off will return a 0 anyway, which is what I
+ want in this case. Plus, the clean up is needed in both
+ cases. */
+
+ /* DELAY HERE!
+ On a fast machine, the status might change before the interrupt
+ is given to the processor. This means that the interrupt was
+ never detected, and probe_irq_off fails to report anything.
+ This should fix probe_irq_* problems.
+ */
+ SMC_DELAY();
+ SMC_DELAY();
+
+ /* and disable all interrupts again */
+ outb( 0, ioaddr + INT_MASK );
+
+ /* and return what I found */
+ return probe_irq_off(cookie);
+#else /* NO_AUTOPROBE */
+ struct devlist *smcdev;
+ for (smcdev = smc_devlist; smcdev->port; smcdev++) {
+ if (smcdev->port == ioaddr)
+ return smcdev->irq;
+ }
+ return 0;
+#endif
+}
+
+static const struct net_device_ops smc_netdev_ops = {
+ .ndo_open = smc_open,
+ .ndo_stop = smc_close,
+ .ndo_start_xmit = smc_wait_to_send_packet,
+ .ndo_tx_timeout = smc_timeout,
+ .ndo_set_multicast_list = smc_set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/*----------------------------------------------------------------------
+ . Function: smc_probe( int ioaddr )
+ .
+ . Purpose:
+ . Tests to see if a given ioaddr points to an SMC9xxx chip.
+ . Returns a 0 on success
+ .
+ . Algorithm:
+ . (1) see if the high byte of BANK_SELECT is 0x33
+ . (2) compare the ioaddr with the base register's address
+ . (3) see if I recognize the chip ID in the appropriate register
+ .
+ .---------------------------------------------------------------------
+ */
+
+/*---------------------------------------------------------------
+ . Here I do typical initialization tasks.
+ .
+ . o Initialize the structure if needed
+ . o print out my vanity message if not done so already
+ . o print out what type of hardware is detected
+ . o print out the ethernet address
+ . o find the IRQ
+ . o set up my private data
+ . o configure the dev structure with my subroutines
+ . o actually GRAB the irq.
+ . o GRAB the region
+ .-----------------------------------------------------------------
+*/
+static int __init smc_probe(struct net_device *dev, int ioaddr)
+{
+ int i, memory, retval;
+ static unsigned version_printed;
+ unsigned int bank;
+
+ const char *version_string;
+ const char *if_string;
+
+ /* registers */
+ word revision_register;
+ word base_address_register;
+ word configuration_register;
+ word memory_info_register;
+ word memory_cfg_register;
+
+ /* Grab the region so that no one else tries to probe our ioports. */
+ if (!request_region(ioaddr, SMC_IO_EXTENT, DRV_NAME))
+ return -EBUSY;
+
+ dev->irq = irq;
+ dev->if_port = ifport;
+
+ /* First, see if the high byte is 0x33 */
+ bank = inw( ioaddr + BANK_SELECT );
+ if ( (bank & 0xFF00) != 0x3300 ) {
+ retval = -ENODEV;
+ goto err_out;
+ }
+ /* The above MIGHT indicate a device, but I need to write to further
+ test this. */
+ outw( 0x0, ioaddr + BANK_SELECT );
+ bank = inw( ioaddr + BANK_SELECT );
+ if ( (bank & 0xFF00 ) != 0x3300 ) {
+ retval = -ENODEV;
+ goto err_out;
+ }
+#if !defined(CONFIG_H8S_EDOSK2674)
+ /* well, we've already written once, so hopefully another time won't
+ hurt. This time, I need to switch the bank register to bank 1,
+ so I can access the base address register */
+ SMC_SELECT_BANK(1);
+ base_address_register = inw( ioaddr + BASE );
+ if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) {
+ printk(CARDNAME ": IOADDR %x doesn't match configuration (%x). "
+ "Probably not a SMC chip\n",
+ ioaddr, base_address_register >> 3 & 0x3E0 );
+ /* well, the base address register didn't match. Must not have
+ been a SMC chip after all. */
+ retval = -ENODEV;
+ goto err_out;
+ }
+#else
+ (void)base_address_register; /* Warning suppression */
+#endif
+
+
+ /* check if the revision register is something that I recognize.
+ These might need to be added to later, as future revisions
+ could be added. */
+ SMC_SELECT_BANK(3);
+ revision_register = inw( ioaddr + REVISION );
+ if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) {
+ /* I don't recognize this chip, so... */
+ printk(CARDNAME ": IO %x: Unrecognized revision register:"
+ " %x, Contact author.\n", ioaddr, revision_register);
+
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /* at this point I'll assume that the chip is an SMC9xxx.
+ It might be prudent to check a listing of MAC addresses
+ against the hardware address, or do some other tests. */
+
+ if (version_printed++ == 0)
+ printk("%s", version);
+
+ /* fill in some of the fields */
+ dev->base_addr = ioaddr;
+
+ /*
+ . Get the MAC address ( bank 1, regs 4 - 9 )
+ */
+ SMC_SELECT_BANK( 1 );
+ for ( i = 0; i < 6; i += 2 ) {
+ word address;
+
+ address = inw( ioaddr + ADDR0 + i );
+ dev->dev_addr[ i + 1] = address >> 8;
+ dev->dev_addr[ i ] = address & 0xFF;
+ }
+
+ /* get the memory information */
+
+ SMC_SELECT_BANK( 0 );
+ memory_info_register = inw( ioaddr + MIR );
+ memory_cfg_register = inw( ioaddr + MCR );
+ memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */
+ memory *= 256 * ( memory_info_register & 0xFF );
+
+ /*
+ Now, I want to find out more about the chip. This is sort of
+ redundant, but it's cleaner to have it in both, rather than having
+ one VERY long probe procedure.
+ */
+ SMC_SELECT_BANK(3);
+ revision_register = inw( ioaddr + REVISION );
+ version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ];
+ if ( !version_string ) {
+ /* I shouldn't get here because this call was done before.... */
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /* is it using AUI or 10BaseT ? */
+ if ( dev->if_port == 0 ) {
+ SMC_SELECT_BANK(1);
+ configuration_register = inw( ioaddr + CONFIG );
+ if ( configuration_register & CFG_AUI_SELECT )
+ dev->if_port = 2;
+ else
+ dev->if_port = 1;
+ }
+ if_string = interfaces[ dev->if_port - 1 ];
+
+ /* now, reset the chip, and put it into a known state */
+ smc_reset( ioaddr );
+
+ /*
+ . If dev->irq is 0, then the device has to be banged on to see
+ . what the IRQ is.
+ .
+ . This banging doesn't always detect the IRQ, for unknown reasons.
+ . a workaround is to reset the chip and try again.
+ .
+ . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
+ . be what is requested on the command line. I don't do that, mostly
+ . because the card that I have uses a non-standard method of accessing
+ . the IRQs, and because this _should_ work in most configurations.
+ .
+ . Specifying an IRQ is done with the assumption that the user knows
+ . what (s)he is doing. No checking is done!!!!
+ .
+ */
+ if ( dev->irq < 2 ) {
+ int trials;
+
+ trials = 3;
+ while ( trials-- ) {
+ dev->irq = smc_findirq( ioaddr );
+ if ( dev->irq )
+ break;
+ /* kick the card and try again */
+ smc_reset( ioaddr );
+ }
+ }
+ if (dev->irq == 0 ) {
+ printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n");
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /* now, print out the card info, in a short format.. */
+
+ printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name,
+ version_string, revision_register & 0xF, ioaddr, dev->irq,
+ if_string, memory );
+ /*
+ . Print the Ethernet address
+ */
+ printk("ADDR: %pM\n", dev->dev_addr);
+
+ /* Grab the IRQ */
+ retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev);
+ if (retval) {
+ printk("%s: unable to get IRQ %d (irqval=%d).\n", DRV_NAME,
+ dev->irq, retval);
+ goto err_out;
+ }
+
+ dev->netdev_ops = &smc_netdev_ops;
+ dev->watchdog_timeo = HZ/20;
+
+ return 0;
+
+err_out:
+ release_region(ioaddr, SMC_IO_EXTENT);
+ return retval;
+}
+
+#if SMC_DEBUG > 2
+static void print_packet( byte * buf, int length )
+{
+#if 0
+ int i;
+ int remainder;
+ int lines;
+
+ printk("Packet of length %d\n", length);
+ lines = length / 16;
+ remainder = length % 16;
+
+ for ( i = 0; i < lines ; i ++ ) {
+ int cur;
+
+ for ( cur = 0; cur < 8; cur ++ ) {
+ byte a, b;
+
+ a = *(buf ++ );
+ b = *(buf ++ );
+ printk("%02x%02x ", a, b );
+ }
+ printk("\n");
+ }
+ for ( i = 0; i < remainder/2 ; i++ ) {
+ byte a, b;
+
+ a = *(buf ++ );
+ b = *(buf ++ );
+ printk("%02x%02x ", a, b );
+ }
+ printk("\n");
+#endif
+}
+#endif
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc ..
+ *
+ */
+static int smc_open(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ int i; /* used to set hw ethernet address */
+
+ /* clear out all the junk that was put here before... */
+ memset(netdev_priv(dev), 0, sizeof(struct smc_local));
+
+ /* reset the hardware */
+
+ smc_reset( ioaddr );
+ smc_enable( ioaddr );
+
+ /* Select which interface to use */
+
+ SMC_SELECT_BANK( 1 );
+ if ( dev->if_port == 1 ) {
+ outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT,
+ ioaddr + CONFIG );
+ }
+ else if ( dev->if_port == 2 ) {
+ outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT,
+ ioaddr + CONFIG );
+ }
+
+ /*
+ According to Becker, I have to set the hardware address
+ at this point, because the (l)user can set it with an
+ ioctl. Easily done...
+ */
+ SMC_SELECT_BANK( 1 );
+ for ( i = 0; i < 6; i += 2 ) {
+ word address;
+
+ address = dev->dev_addr[ i + 1 ] << 8 ;
+ address |= dev->dev_addr[ i ];
+ outw( address, ioaddr + ADDR0 + i );
+ }
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/*--------------------------------------------------------
+ . Called by the kernel to send a packet out into the void
+ . of the net. This routine is largely based on
+ . skeleton.c, from Becker.
+ .--------------------------------------------------------
+*/
+
+static void smc_timeout(struct net_device *dev)
+{
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n",
+ tx_done(dev) ? "IRQ conflict" :
+ "network cable problem");
+ /* "kick" the adaptor */
+ smc_reset( dev->base_addr );
+ smc_enable( dev->base_addr );
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ /* clear anything saved */
+ ((struct smc_local *)netdev_priv(dev))->saved_skb = NULL;
+ netif_wake_queue(dev);
+}
+
+/*-------------------------------------------------------------
+ .
+ . smc_rcv - receive a packet from the card
+ .
+ . There is ( at least ) a packet waiting to be read from
+ . chip-memory.
+ .
+ . o Read the status
+ . o If an error, record it
+ . o otherwise, read in the packet
+ --------------------------------------------------------------
+*/
+static void smc_rcv(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int packet_number;
+ word status;
+ word packet_length;
+
+ /* assume bank 2 */
+
+ packet_number = inw( ioaddr + FIFO_PORTS );
+
+ if ( packet_number & FP_RXEMPTY ) {
+ /* we got called , but nothing was on the FIFO */
+ PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO.\n"));
+ /* don't need to restore anything */
+ return;
+ }
+
+ /* start reading from the start of the packet */
+ outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER );
+
+ /* First two words are status and packet_length */
+ status = inw( ioaddr + DATA_1 );
+ packet_length = inw( ioaddr + DATA_1 );
+
+ packet_length &= 0x07ff; /* mask off top bits */
+
+ PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ));
+ /*
+ . the packet length contains 3 extra words :
+ . status, length, and an extra word with an odd byte .
+ */
+ packet_length -= 6;
+
+ if ( !(status & RS_ERRORS ) ){
+ /* do stuff to make a new packet */
+ struct sk_buff * skb;
+ byte * data;
+
+ /* read one extra byte */
+ if ( status & RS_ODDFRAME )
+ packet_length++;
+
+ /* set multicast stats */
+ if ( status & RS_MULTICAST )
+ dev->stats.multicast++;
+
+ skb = dev_alloc_skb( packet_length + 5);
+
+ if ( skb == NULL ) {
+ printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n");
+ dev->stats.rx_dropped++;
+ goto done;
+ }
+
+ /*
+ ! This should work without alignment, but it could be
+ ! in the worse case
+ */
+
+ skb_reserve( skb, 2 ); /* 16 bit alignment */
+
+ data = skb_put( skb, packet_length);
+
+#ifdef USE_32_BIT
+ /* QUESTION: Like in the TX routine, do I want
+ to send the DWORDs or the bytes first, or some
+ mixture. A mixture might improve already slow PIO
+ performance */
+ PRINTK3((" Reading %d dwords (and %d bytes)\n",
+ packet_length >> 2, packet_length & 3 ));
+ insl(ioaddr + DATA_1 , data, packet_length >> 2 );
+ /* read the left over bytes */
+ insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC),
+ packet_length & 0x3 );
+#else
+ PRINTK3((" Reading %d words and %d byte(s)\n",
+ (packet_length >> 1 ), packet_length & 1 ));
+ insw(ioaddr + DATA_1 , data, packet_length >> 1);
+ if ( packet_length & 1 ) {
+ data += packet_length & ~1;
+ *(data++) = inb( ioaddr + DATA_1 );
+ }
+#endif
+#if SMC_DEBUG > 2
+ print_packet( data, packet_length );
+#endif
+
+ skb->protocol = eth_type_trans(skb, dev );
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += packet_length;
+ } else {
+ /* error ... */
+ dev->stats.rx_errors++;
+
+ if ( status & RS_ALGNERR ) dev->stats.rx_frame_errors++;
+ if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
+ dev->stats.rx_length_errors++;
+ if ( status & RS_BADCRC) dev->stats.rx_crc_errors++;
+ }
+
+done:
+ /* error or good, tell the card to get rid of this packet */
+ outw( MC_RELEASE, ioaddr + MMU_CMD );
+}
+
+
+/*************************************************************************
+ . smc_tx
+ .
+ . Purpose: Handle a transmit error message. This will only be called
+ . when an error, because of the AUTO_RELEASE mode.
+ .
+ . Algorithm:
+ . Save pointer and packet no
+ . Get the packet no from the top of the queue
+ . check if it's valid ( if not, is this an error??? )
+ . read the status word
+ . record the error
+ . ( resend? Not really, since we don't want old packets around )
+ . Restore saved values
+ ************************************************************************/
+static void smc_tx( struct net_device * dev )
+{
+ int ioaddr = dev->base_addr;
+ struct smc_local *lp = netdev_priv(dev);
+ byte saved_packet;
+ byte packet_no;
+ word tx_status;
+
+
+ /* assume bank 2 */
+
+ saved_packet = inb( ioaddr + PNR_ARR );
+ packet_no = inw( ioaddr + FIFO_PORTS );
+ packet_no &= 0x7F;
+
+ /* select this as the packet to read from */
+ outb( packet_no, ioaddr + PNR_ARR );
+
+ /* read the first word from this packet */
+ outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER );
+
+ tx_status = inw( ioaddr + DATA_1 );
+ PRINTK3((CARDNAME": TX DONE STATUS: %4x\n", tx_status));
+
+ dev->stats.tx_errors++;
+ if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++;
+ if ( tx_status & TS_LATCOL ) {
+ printk(KERN_DEBUG CARDNAME
+ ": Late collision occurred on last xmit.\n");
+ dev->stats.tx_window_errors++;
+ }
+#if 0
+ if ( tx_status & TS_16COL ) { ... }
+#endif
+
+ if ( tx_status & TS_SUCCESS ) {
+ printk(CARDNAME": Successful packet caused interrupt\n");
+ }
+ /* re-enable transmit */
+ SMC_SELECT_BANK( 0 );
+ outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR );
+
+ /* kill the packet */
+ SMC_SELECT_BANK( 2 );
+ outw( MC_FREEPKT, ioaddr + MMU_CMD );
+
+ /* one less packet waiting for me */
+ lp->packets_waiting--;
+
+ outb( saved_packet, ioaddr + PNR_ARR );
+}
+
+/*--------------------------------------------------------------------
+ .
+ . This is the main routine of the driver, to handle the device when
+ . it needs some attention.
+ .
+ . So:
+ . first, save state of the chipset
+ . branch off into routines to handle each case, and acknowledge
+ . each to the interrupt register
+ . and finally restore state.
+ .
+ ---------------------------------------------------------------------*/
+
+static irqreturn_t smc_interrupt(int irq, void * dev_id)
+{
+ struct net_device *dev = dev_id;
+ int ioaddr = dev->base_addr;
+ struct smc_local *lp = netdev_priv(dev);
+
+ byte status;
+ word card_stats;
+ byte mask;
+ int timeout;
+ /* state registers */
+ word saved_bank;
+ word saved_pointer;
+ int handled = 0;
+
+
+ PRINTK3((CARDNAME": SMC interrupt started\n"));
+
+ saved_bank = inw( ioaddr + BANK_SELECT );
+
+ SMC_SELECT_BANK(2);
+ saved_pointer = inw( ioaddr + POINTER );
+
+ mask = inb( ioaddr + INT_MASK );
+ /* clear all interrupts */
+ outb( 0, ioaddr + INT_MASK );
+
+
+ /* set a timeout value, so I don't stay here forever */
+ timeout = 4;
+
+ PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x\n", mask));
+ do {
+ /* read the status flag, and mask it */
+ status = inb( ioaddr + INTERRUPT ) & mask;
+ if (!status )
+ break;
+
+ handled = 1;
+
+ PRINTK3((KERN_WARNING CARDNAME
+ ": Handling interrupt status %x\n", status));
+
+ if (status & IM_RCV_INT) {
+ /* Got a packet(s). */
+ PRINTK2((KERN_WARNING CARDNAME
+ ": Receive Interrupt\n"));
+ smc_rcv(dev);
+ } else if (status & IM_TX_INT ) {
+ PRINTK2((KERN_WARNING CARDNAME
+ ": TX ERROR handled\n"));
+ smc_tx(dev);
+ outb(IM_TX_INT, ioaddr + INTERRUPT );
+ } else if (status & IM_TX_EMPTY_INT ) {
+ /* update stats */
+ SMC_SELECT_BANK( 0 );
+ card_stats = inw( ioaddr + COUNTER );
+ /* single collisions */
+ dev->stats.collisions += card_stats & 0xF;
+ card_stats >>= 4;
+ /* multiple collisions */
+ dev->stats.collisions += card_stats & 0xF;
+
+ /* these are for when linux supports these statistics */
+
+ SMC_SELECT_BANK( 2 );
+ PRINTK2((KERN_WARNING CARDNAME
+ ": TX_BUFFER_EMPTY handled\n"));
+ outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT );
+ mask &= ~IM_TX_EMPTY_INT;
+ dev->stats.tx_packets += lp->packets_waiting;
+ lp->packets_waiting = 0;
+
+ } else if (status & IM_ALLOC_INT ) {
+ PRINTK2((KERN_DEBUG CARDNAME
+ ": Allocation interrupt\n"));
+ /* clear this interrupt so it doesn't happen again */
+ mask &= ~IM_ALLOC_INT;
+
+ smc_hardware_send_packet( dev );
+
+ /* enable xmit interrupts based on this */
+ mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
+
+ /* and let the card send more packets to me */
+ netif_wake_queue(dev);
+
+ PRINTK2((CARDNAME": Handoff done successfully.\n"));
+ } else if (status & IM_RX_OVRN_INT ) {
+ dev->stats.rx_errors++;
+ dev->stats.rx_fifo_errors++;
+ outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT );
+ } else if (status & IM_EPH_INT ) {
+ PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT\n"));
+ } else if (status & IM_ERCV_INT ) {
+ PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT\n"));
+ outb( IM_ERCV_INT, ioaddr + INTERRUPT );
+ }
+ } while ( timeout -- );
+
+
+ /* restore state register */
+ SMC_SELECT_BANK( 2 );
+ outb( mask, ioaddr + INT_MASK );
+
+ PRINTK3((KERN_WARNING CARDNAME ": MASK is now %x\n", mask));
+ outw( saved_pointer, ioaddr + POINTER );
+
+ SMC_SELECT_BANK( saved_bank );
+
+ PRINTK3((CARDNAME ": Interrupt done\n"));
+ return IRQ_RETVAL(handled);
+}
+
+
+/*----------------------------------------------------
+ . smc_close
+ .
+ . this makes the board clean up everything that it can
+ . and not talk to the outside world. Caused by
+ . an 'ifconfig ethX down'
+ .
+ -----------------------------------------------------*/
+static int smc_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ /* clear everything */
+ smc_shutdown( dev->base_addr );
+
+ /* Update the statistics here. */
+ return 0;
+}
+
+/*-----------------------------------------------------------
+ . smc_set_multicast_list
+ .
+ . This routine will, depending on the values passed to it,
+ . either make it accept multicast packets, go into
+ . promiscuous mode ( for TCPDUMP and cousins ) or accept
+ . a select set of multicast packets
+*/
+static void smc_set_multicast_list(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ SMC_SELECT_BANK(0);
+ if ( dev->flags & IFF_PROMISC )
+ outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR );
+
+/* BUG? I never disable promiscuous mode if multicasting was turned on.
+ Now, I turn off promiscuous mode, but I don't do anything to multicasting
+ when promiscuous mode is turned on.
+*/
+
+ /* Here, I am setting this to accept all multicast packets.
+ I don't need to zero the multicast table, because the flag is
+ checked before the table is
+ */
+ else if (dev->flags & IFF_ALLMULTI)
+ outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR );
+
+ /* We just get all multicast packets even if we only want them
+ . from one source. This will be changed at some future
+ . point. */
+ else if (!netdev_mc_empty(dev)) {
+ /* support hardware multicasting */
+
+ /* be sure I get rid of flags I might have set */
+ outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
+ ioaddr + RCR );
+ /* NOTE: this has to set the bank, so make sure it is the
+ last thing called. The bank is set to zero at the top */
+ smc_setmulticast(ioaddr, dev);
+ }
+ else {
+ outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL),
+ ioaddr + RCR );
+
+ /*
+ since I'm disabling all multicast entirely, I need to
+ clear the multicast list
+ */
+ SMC_SELECT_BANK( 3 );
+ outw( 0, ioaddr + MULTICAST1 );
+ outw( 0, ioaddr + MULTICAST2 );
+ outw( 0, ioaddr + MULTICAST3 );
+ outw( 0, ioaddr + MULTICAST4 );
+ }
+}
+
+#ifdef MODULE
+
+static struct net_device *devSMC9194;
+MODULE_LICENSE("GPL");
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(ifport, int, 0);
+MODULE_PARM_DESC(io, "SMC 99194 I/O base address");
+MODULE_PARM_DESC(irq, "SMC 99194 IRQ number");
+MODULE_PARM_DESC(ifport, "SMC 99194 interface port (0-default, 1-TP, 2-AUI)");
+
+int __init init_module(void)
+{
+ if (io == 0)
+ printk(KERN_WARNING
+ CARDNAME": You shouldn't use auto-probing with insmod!\n" );
+
+ /* copy the parameters from insmod into the device structure */
+ devSMC9194 = smc_init(-1);
+ if (IS_ERR(devSMC9194))
+ return PTR_ERR(devSMC9194);
+ return 0;
+}
+
+void __exit cleanup_module(void)
+{
+ unregister_netdev(devSMC9194);
+ free_irq(devSMC9194->irq, devSMC9194);
+ release_region(devSMC9194->base_addr, SMC_IO_EXTENT);
+ free_netdev(devSMC9194);
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/ethernet/smsc/smc9194.h b/drivers/net/ethernet/smsc/smc9194.h
new file mode 100644
index 000000000000..cf69d0a5a1cb
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc9194.h
@@ -0,0 +1,241 @@
+/*------------------------------------------------------------------------
+ . smc9194.h
+ . Copyright (C) 1996 by Erik Stahlman
+ .
+ . This software may be used and distributed according to the terms
+ . of the GNU General Public License, incorporated herein by reference.
+ .
+ . This file contains register information and access macros for
+ . the SMC91xxx chipset.
+ .
+ . Information contained in this file was obtained from the SMC91C94
+ . manual from SMC. To get a copy, if you really want one, you can find
+ . information under www.smc.com in the components division.
+ . ( this thanks to advice from Donald Becker ).
+ .
+ . Authors
+ . Erik Stahlman ( erik@vt.edu )
+ .
+ . History
+ . 01/06/96 Erik Stahlman moved definitions here from main .c file
+ . 01/19/96 Erik Stahlman polished this up some, and added better
+ . error handling
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC9194_H_
+#define _SMC9194_H_
+
+/* I want some simple types */
+
+typedef unsigned char byte;
+typedef unsigned short word;
+typedef unsigned long int dword;
+
+
+/* Because of bank switching, the SMC91xxx uses only 16 I/O ports */
+
+#define SMC_IO_EXTENT 16
+
+
+/*---------------------------------------------------------------
+ .
+ . A description of the SMC registers is probably in order here,
+ . although for details, the SMC datasheet is invaluable.
+ .
+ . Basically, the chip has 4 banks of registers ( 0 to 3 ), which
+ . are accessed by writing a number into the BANK_SELECT register
+ . ( I also use a SMC_SELECT_BANK macro for this ).
+ .
+ . The banks are configured so that for most purposes, bank 2 is all
+ . that is needed for simple run time tasks.
+ -----------------------------------------------------------------------*/
+
+/*
+ . Bank Select Register:
+ .
+ . yyyy yyyy 0000 00xx
+ . xx = bank number
+ . yyyy yyyy = 0x33, for identification purposes.
+*/
+#define BANK_SELECT 14
+
+/* BANK 0 */
+
+#define TCR 0 /* transmit control register */
+#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
+#define TCR_FDUPLX 0x0800 /* receive packets sent out */
+#define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */
+#define TCR_MON_CNS 0x0400 /* monitors the carrier status */
+#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */
+
+#define TCR_CLEAR 0 /* do NOTHING */
+/* the normal settings for the TCR register : */
+/* QUESTION: do I want to enable padding of short packets ? */
+#define TCR_NORMAL TCR_ENABLE
+
+
+#define EPH_STATUS 2
+#define ES_LINK_OK 0x4000 /* is the link integrity ok ? */
+
+#define RCR 4
+#define RCR_SOFTRESET 0x8000 /* resets the chip */
+#define RCR_STRIP_CRC 0x200 /* strips CRC */
+#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
+#define RCR_ALMUL 0x4 /* receive all multicast packets */
+#define RCR_PROMISC 0x2 /* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
+#define RCR_CLEAR 0x0 /* set it to a base state */
+
+#define COUNTER 6
+#define MIR 8
+#define MCR 10
+/* 12 is reserved */
+
+/* BANK 1 */
+#define CONFIG 0
+#define CFG_AUI_SELECT 0x100
+#define BASE 2
+#define ADDR0 4
+#define ADDR1 6
+#define ADDR2 8
+#define GENERAL 10
+#define CONTROL 12
+#define CTL_POWERDOWN 0x2000
+#define CTL_LE_ENABLE 0x80
+#define CTL_CR_ENABLE 0x40
+#define CTL_TE_ENABLE 0x0020
+#define CTL_AUTO_RELEASE 0x0800
+#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */
+
+/* BANK 2 */
+#define MMU_CMD 0
+#define MC_BUSY 1 /* only readable bit in the register */
+#define MC_NOP 0
+#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
+#define MC_RESET 0x40
+#define MC_REMOVE 0x60 /* remove the current rx packet */
+#define MC_RELEASE 0x80 /* remove and release the current rx packet */
+#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
+#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
+
+#define PNR_ARR 2
+#define FIFO_PORTS 4
+
+#define FP_RXEMPTY 0x8000
+#define FP_TXEMPTY 0x80
+
+#define POINTER 6
+#define PTR_READ 0x2000
+#define PTR_RCV 0x8000
+#define PTR_AUTOINC 0x4000
+#define PTR_AUTO_INC 0x0040
+
+#define DATA_1 8
+#define DATA_2 10
+#define INTERRUPT 12
+
+#define INT_MASK 13
+#define IM_RCV_INT 0x1
+#define IM_TX_INT 0x2
+#define IM_TX_EMPTY_INT 0x4
+#define IM_ALLOC_INT 0x8
+#define IM_RX_OVRN_INT 0x10
+#define IM_EPH_INT 0x20
+#define IM_ERCV_INT 0x40 /* not on SMC9192 */
+
+/* BANK 3 */
+#define MULTICAST1 0
+#define MULTICAST2 2
+#define MULTICAST3 4
+#define MULTICAST4 6
+#define MGMT 8
+#define REVISION 10 /* ( hi: chip id low: rev # ) */
+
+
+/* this is NOT on SMC9192 */
+#define ERCV 12
+
+#define CHIP_9190 3
+#define CHIP_9194 4
+#define CHIP_9195 5
+#define CHIP_91100 7
+
+static const char * chip_ids[ 15 ] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMC91C90/91C92",
+ /* 4 */ "SMC91C94",
+ /* 5 */ "SMC91C95",
+ NULL,
+ /* 7 */ "SMC91C100",
+ /* 8 */ "SMC91C100FD",
+ NULL, NULL, NULL,
+ NULL, NULL, NULL};
+
+/*
+ . Transmit status bits
+*/
+#define TS_SUCCESS 0x0001
+#define TS_LOSTCAR 0x0400
+#define TS_LATCOL 0x0200
+#define TS_16COL 0x0010
+
+/*
+ . Receive status bits
+*/
+#define RS_ALGNERR 0x8000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+static const char * interfaces[ 2 ] = { "TP", "AUI" };
+
+/*-------------------------------------------------------------------------
+ . I define some macros to make it easier to do somewhat common
+ . or slightly complicated, repeated tasks.
+ --------------------------------------------------------------------------*/
+
+/* select a register bank, 0 to 3 */
+
+#define SMC_SELECT_BANK(x) { outw( x, ioaddr + BANK_SELECT ); }
+
+/* define a small delay for the reset */
+#define SMC_DELAY() { inw( ioaddr + RCR );\
+ inw( ioaddr + RCR );\
+ inw( ioaddr + RCR ); }
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(x) {\
+ unsigned char mask;\
+ SMC_SELECT_BANK(2);\
+ mask = inb( ioaddr + INT_MASK );\
+ mask |= (x);\
+ outb( mask, ioaddr + INT_MASK ); \
+}
+
+/* this disables an interrupt from the interrupt mask register */
+
+#define SMC_DISABLE_INT(x) {\
+ unsigned char mask;\
+ SMC_SELECT_BANK(2);\
+ mask = inb( ioaddr + INT_MASK );\
+ mask &= ~(x);\
+ outb( mask, ioaddr + INT_MASK ); \
+}
+
+/*----------------------------------------------------------------------
+ . Define the interrupts that I want to receive from the card
+ .
+ . I want:
+ . IM_EPH_INT, for nasty errors
+ . IM_RCV_INT, for happy received packets
+ . IM_RX_OVRN_INT, because I have to kick the receiver
+ --------------------------------------------------------------------------*/
+#define SMC_INTERRUPT_MASK (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT)
+
+#endif /* _SMC_9194_H_ */
+
diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
new file mode 100644
index 000000000000..cffbc0373fa9
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc91c92_cs.c
@@ -0,0 +1,2070 @@
+/*======================================================================
+
+ A PCMCIA ethernet driver for SMC91c92-based cards.
+
+ This driver supports Megahertz PCMCIA ethernet cards; and
+ Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
+ multifunction cards.
+
+ Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+ smc91c92_cs.c 1.122 2002/10/25 06:26:39
+
+ This driver contains code written by Donald Becker
+ (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
+ David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
+ (erik@vt.edu). Donald wrote the SMC 91c92 code using parts of
+ Erik's SMC 91c94 driver. Rowan wrote a similar driver, and I've
+ incorporated some parts of his driver here. I (Dave) wrote most
+ of the PCMCIA glue code, and the Ositech support code. Kelly
+ Stephens (kstephen@holli.com) added support for the Motorola
+ Mariner, with help from Allen Brost.
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License, incorporated herein by reference.
+
+======================================================================*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/jiffies.h>
+#include <linux/firmware.h>
+
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/*====================================================================*/
+
+static const char *if_names[] = { "auto", "10baseT", "10base2"};
+
+/* Firmware name */
+#define FIRMWARE_NAME "ositech/Xilinx7OD.bin"
+
+/* Module parameters */
+
+MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE_NAME);
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/*
+ Transceiver/media type.
+ 0 = auto
+ 1 = 10baseT (and autoselect if #define AUTOSELECT),
+ 2 = AUI/10base2,
+*/
+INT_MODULE_PARM(if_port, 0);
+
+
+#define DRV_NAME "smc91c92_cs"
+#define DRV_VERSION "1.123"
+
+/*====================================================================*/
+
+/* Operational parameter that usually are not changed. */
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT ((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK 4
+
+/* Times to check the check the chip before concluding that it doesn't
+ currently have room for another Tx packet. */
+#define MEMORY_WAIT_TIME 8
+
+struct smc_private {
+ struct pcmcia_device *p_dev;
+ spinlock_t lock;
+ u_short manfid;
+ u_short cardid;
+
+ struct sk_buff *saved_skb;
+ int packets_waiting;
+ void __iomem *base;
+ u_short cfg;
+ struct timer_list media;
+ int watchdog, tx_err;
+ u_short media_status;
+ u_short fast_poll;
+ u_short link_status;
+ struct mii_if_info mii_if;
+ int duplex;
+ int rx_ovrn;
+};
+
+/* Special definitions for Megahertz multifunction cards */
+#define MEGAHERTZ_ISR 0x0380
+
+/* Special function registers for Motorola Mariner */
+#define MOT_LAN 0x0000
+#define MOT_UART 0x0020
+#define MOT_EEPROM 0x20
+
+#define MOT_NORMAL \
+(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
+
+/* Special function registers for Ositech cards */
+#define OSITECH_AUI_CTL 0x0c
+#define OSITECH_PWRDOWN 0x0d
+#define OSITECH_RESET 0x0e
+#define OSITECH_ISR 0x0f
+#define OSITECH_AUI_PWR 0x0c
+#define OSITECH_RESET_ISR 0x0e
+
+#define OSI_AUI_PWR 0x40
+#define OSI_LAN_PWRDOWN 0x02
+#define OSI_MODEM_PWRDOWN 0x01
+#define OSI_LAN_RESET 0x02
+#define OSI_MODEM_RESET 0x01
+
+/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
+#define BANK_SELECT 14 /* Window select register. */
+#define SMC_SELECT_BANK(x) { outw(x, ioaddr + BANK_SELECT); }
+
+/* Bank 0 registers. */
+#define TCR 0 /* transmit control register */
+#define TCR_CLEAR 0 /* do NOTHING */
+#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */
+#define TCR_PAD_EN 0x0080 /* pads short packets to 64 bytes */
+#define TCR_MONCSN 0x0400 /* Monitor Carrier. */
+#define TCR_FDUPLX 0x0800 /* Full duplex mode. */
+#define TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
+
+#define EPH 2 /* Ethernet Protocol Handler report. */
+#define EPH_TX_SUC 0x0001
+#define EPH_SNGLCOL 0x0002
+#define EPH_MULCOL 0x0004
+#define EPH_LTX_MULT 0x0008
+#define EPH_16COL 0x0010
+#define EPH_SQET 0x0020
+#define EPH_LTX_BRD 0x0040
+#define EPH_TX_DEFR 0x0080
+#define EPH_LAT_COL 0x0200
+#define EPH_LOST_CAR 0x0400
+#define EPH_EXC_DEF 0x0800
+#define EPH_CTR_ROL 0x1000
+#define EPH_RX_OVRN 0x2000
+#define EPH_LINK_OK 0x4000
+#define EPH_TX_UNRN 0x8000
+#define MEMINFO 8 /* Memory Information Register */
+#define MEMCFG 10 /* Memory Configuration Register */
+
+/* Bank 1 registers. */
+#define CONFIG 0
+#define CFG_MII_SELECT 0x8000 /* 91C100 only */
+#define CFG_NO_WAIT 0x1000
+#define CFG_FULL_STEP 0x0400
+#define CFG_SET_SQLCH 0x0200
+#define CFG_AUI_SELECT 0x0100
+#define CFG_16BIT 0x0080
+#define CFG_DIS_LINK 0x0040
+#define CFG_STATIC 0x0030
+#define CFG_IRQ_SEL_1 0x0004
+#define CFG_IRQ_SEL_0 0x0002
+#define BASE_ADDR 2
+#define ADDR0 4
+#define GENERAL 10
+#define CONTROL 12
+#define CTL_STORE 0x0001
+#define CTL_RELOAD 0x0002
+#define CTL_EE_SELECT 0x0004
+#define CTL_TE_ENABLE 0x0020
+#define CTL_CR_ENABLE 0x0040
+#define CTL_LE_ENABLE 0x0080
+#define CTL_AUTO_RELEASE 0x0800
+#define CTL_POWERDOWN 0x2000
+
+/* Bank 2 registers. */
+#define MMU_CMD 0
+#define MC_ALLOC 0x20 /* or with number of 256 byte packets */
+#define MC_RESET 0x40
+#define MC_RELEASE 0x80 /* remove and release the current rx packet */
+#define MC_FREEPKT 0xA0 /* Release packet in PNR register */
+#define MC_ENQUEUE 0xC0 /* Enqueue the packet for transmit */
+#define PNR_ARR 2
+#define FIFO_PORTS 4
+#define FP_RXEMPTY 0x8000
+#define POINTER 6
+#define PTR_AUTO_INC 0x0040
+#define PTR_READ 0x2000
+#define PTR_AUTOINC 0x4000
+#define PTR_RCV 0x8000
+#define DATA_1 8
+#define INTERRUPT 12
+#define IM_RCV_INT 0x1
+#define IM_TX_INT 0x2
+#define IM_TX_EMPTY_INT 0x4
+#define IM_ALLOC_INT 0x8
+#define IM_RX_OVRN_INT 0x10
+#define IM_EPH_INT 0x20
+
+#define RCR 4
+enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
+ RxEnable = 0x0100, RxStripCRC = 0x0200};
+#define RCR_SOFTRESET 0x8000 /* resets the chip */
+#define RCR_STRIP_CRC 0x200 /* strips CRC */
+#define RCR_ENABLE 0x100 /* IFF this is set, we can receive packets */
+#define RCR_ALMUL 0x4 /* receive all multicast packets */
+#define RCR_PROMISC 0x2 /* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define RCR_NORMAL (RCR_STRIP_CRC | RCR_ENABLE)
+#define RCR_CLEAR 0x0 /* set it to a base state */
+#define COUNTER 6
+
+/* BANK 3 -- not the same values as in smc9194! */
+#define MULTICAST0 0
+#define MULTICAST2 2
+#define MULTICAST4 4
+#define MULTICAST6 6
+#define MGMT 8
+#define REVISION 0x0a
+
+/* Transmit status bits. */
+#define TS_SUCCESS 0x0001
+#define TS_16COL 0x0010
+#define TS_LATCOL 0x0200
+#define TS_LOSTCAR 0x0400
+
+/* Receive status bits. */
+#define RS_ALGNERR 0x8000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+#define set_bits(v, p) outw(inw(p)|(v), (p))
+#define mask_bits(v, p) outw(inw(p)&(v), (p))
+
+/*====================================================================*/
+
+static void smc91c92_detach(struct pcmcia_device *p_dev);
+static int smc91c92_config(struct pcmcia_device *link);
+static void smc91c92_release(struct pcmcia_device *link);
+
+static int smc_open(struct net_device *dev);
+static int smc_close(struct net_device *dev);
+static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void smc_tx_timeout(struct net_device *dev);
+static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+static irqreturn_t smc_interrupt(int irq, void *dev_id);
+static void smc_rx(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static int s9k_config(struct net_device *dev, struct ifmap *map);
+static void smc_set_xcvr(struct net_device *dev, int if_port);
+static void smc_reset(struct net_device *dev);
+static void media_check(u_long arg);
+static void mdio_sync(unsigned int addr);
+static int mdio_read(struct net_device *dev, int phy_id, int loc);
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
+static int smc_link_ok(struct net_device *dev);
+static const struct ethtool_ops ethtool_ops;
+
+static const struct net_device_ops smc_netdev_ops = {
+ .ndo_open = smc_open,
+ .ndo_stop = smc_close,
+ .ndo_start_xmit = smc_start_xmit,
+ .ndo_tx_timeout = smc_tx_timeout,
+ .ndo_set_config = s9k_config,
+ .ndo_set_multicast_list = set_rx_mode,
+ .ndo_do_ioctl = smc_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static int smc91c92_probe(struct pcmcia_device *link)
+{
+ struct smc_private *smc;
+ struct net_device *dev;
+
+ dev_dbg(&link->dev, "smc91c92_attach()\n");
+
+ /* Create new ethernet device */
+ dev = alloc_etherdev(sizeof(struct smc_private));
+ if (!dev)
+ return -ENOMEM;
+ smc = netdev_priv(dev);
+ smc->p_dev = link;
+ link->priv = dev;
+
+ spin_lock_init(&smc->lock);
+
+ /* The SMC91c92-specific entries in the device structure. */
+ dev->netdev_ops = &smc_netdev_ops;
+ SET_ETHTOOL_OPS(dev, &ethtool_ops);
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ smc->mii_if.dev = dev;
+ smc->mii_if.mdio_read = mdio_read;
+ smc->mii_if.mdio_write = mdio_write;
+ smc->mii_if.phy_id_mask = 0x1f;
+ smc->mii_if.reg_num_mask = 0x1f;
+
+ return smc91c92_config(link);
+} /* smc91c92_attach */
+
+static void smc91c92_detach(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ dev_dbg(&link->dev, "smc91c92_detach\n");
+
+ unregister_netdev(dev);
+
+ smc91c92_release(link);
+
+ free_netdev(dev);
+} /* smc91c92_detach */
+
+/*====================================================================*/
+
+static int cvt_ascii_address(struct net_device *dev, char *s)
+{
+ int i, j, da, c;
+
+ if (strlen(s) != 12)
+ return -1;
+ for (i = 0; i < 6; i++) {
+ da = 0;
+ for (j = 0; j < 2; j++) {
+ c = *s++;
+ da <<= 4;
+ da += ((c >= '0') && (c <= '9')) ?
+ (c - '0') : ((c & 0x0f) + 9);
+ }
+ dev->dev_addr[i] = da;
+ }
+ return 0;
+}
+
+/*====================================================================
+
+ Configuration stuff for Megahertz cards
+
+ mhz_3288_power() is used to power up a 3288's ethernet chip.
+ mhz_mfc_config() handles socket setup for multifunction (1144
+ and 3288) cards. mhz_setup() gets a card's hardware ethernet
+ address.
+
+======================================================================*/
+
+static int mhz_3288_power(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *smc = netdev_priv(dev);
+ u_char tmp;
+
+ /* Read the ISR twice... */
+ readb(smc->base+MEGAHERTZ_ISR);
+ udelay(5);
+ readb(smc->base+MEGAHERTZ_ISR);
+
+ /* Pause 200ms... */
+ mdelay(200);
+
+ /* Now read and write the COR... */
+ tmp = readb(smc->base + link->config_base + CISREG_COR);
+ udelay(5);
+ writeb(tmp, smc->base + link->config_base + CISREG_COR);
+
+ return 0;
+}
+
+static int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data)
+{
+ int k;
+ p_dev->io_lines = 16;
+ p_dev->resource[1]->start = p_dev->resource[0]->start;
+ p_dev->resource[1]->end = 8;
+ p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+ p_dev->resource[0]->end = 16;
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+ for (k = 0; k < 0x400; k += 0x10) {
+ if (k & 0x80)
+ continue;
+ p_dev->resource[0]->start = k ^ 0x300;
+ if (!pcmcia_request_io(p_dev))
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static int mhz_mfc_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int offset;
+ int i;
+
+ link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ |
+ CONF_AUTO_SET_IO;
+
+ /* The Megahertz combo cards have modem-like CIS entries, so
+ we have to explicitly try a bunch of port combinations. */
+ if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
+ return -ENODEV;
+
+ dev->base_addr = link->resource[0]->start;
+
+ /* Allocate a memory window, for accessing the ISR */
+ link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+ link->resource[2]->start = link->resource[2]->end = 0;
+ i = pcmcia_request_window(link, link->resource[2], 0);
+ if (i != 0)
+ return -ENODEV;
+
+ smc->base = ioremap(link->resource[2]->start,
+ resource_size(link->resource[2]));
+ offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0;
+ i = pcmcia_map_mem_page(link, link->resource[2], offset);
+ if ((i == 0) &&
+ (smc->manfid == MANFID_MEGAHERTZ) &&
+ (smc->cardid == PRODID_MEGAHERTZ_EM3288))
+ mhz_3288_power(link);
+
+ return 0;
+}
+
+static int pcmcia_get_versmac(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv)
+{
+ struct net_device *dev = priv;
+ cisparse_t parse;
+ u8 *buf;
+
+ if (pcmcia_parse_tuple(tuple, &parse))
+ return -EINVAL;
+
+ buf = parse.version_1.str + parse.version_1.ofs[3];
+
+ if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0))
+ return 0;
+
+ return -EINVAL;
+};
+
+static int mhz_setup(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ size_t len;
+ u8 *buf;
+ int rc;
+
+ /* Read the station address from the CIS. It is stored as the last
+ (fourth) string in the Version 1 Version/ID tuple. */
+ if ((link->prod_id[3]) &&
+ (cvt_ascii_address(dev, link->prod_id[3]) == 0))
+ return 0;
+
+ /* Workarounds for broken cards start here. */
+ /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
+ if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev))
+ return 0;
+
+ /* Another possibility: for the EM3288, in a special tuple */
+ rc = -1;
+ len = pcmcia_get_tuple(link, 0x81, &buf);
+ if (buf && len >= 13) {
+ buf[12] = '\0';
+ if (cvt_ascii_address(dev, buf) == 0)
+ rc = 0;
+ }
+ kfree(buf);
+
+ return rc;
+};
+
+/*======================================================================
+
+ Configuration stuff for the Motorola Mariner
+
+ mot_config() writes directly to the Mariner configuration
+ registers because the CIS is just bogus.
+
+======================================================================*/
+
+static void mot_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ unsigned int iouart = link->resource[1]->start;
+
+ /* Set UART base address and force map with COR bit 1 */
+ writeb(iouart & 0xff, smc->base + MOT_UART + CISREG_IOBASE_0);
+ writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
+ writeb(MOT_NORMAL, smc->base + MOT_UART + CISREG_COR);
+
+ /* Set SMC base address and force map with COR bit 1 */
+ writeb(ioaddr & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_0);
+ writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
+ writeb(MOT_NORMAL, smc->base + MOT_LAN + CISREG_COR);
+
+ /* Wait for things to settle down */
+ mdelay(100);
+}
+
+static int mot_setup(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ int i, wait, loop;
+ u_int addr;
+
+ /* Read Ethernet address from Serial EEPROM */
+
+ for (i = 0; i < 3; i++) {
+ SMC_SELECT_BANK(2);
+ outw(MOT_EEPROM + i, ioaddr + POINTER);
+ SMC_SELECT_BANK(1);
+ outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
+
+ for (loop = wait = 0; loop < 200; loop++) {
+ udelay(10);
+ wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
+ if (wait == 0) break;
+ }
+
+ if (wait)
+ return -1;
+
+ addr = inw(ioaddr + GENERAL);
+ dev->dev_addr[2*i] = addr & 0xff;
+ dev->dev_addr[2*i+1] = (addr >> 8) & 0xff;
+ }
+
+ return 0;
+}
+
+/*====================================================================*/
+
+static int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data)
+{
+ p_dev->resource[0]->end = 16;
+ p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
+ p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
+
+ return pcmcia_request_io(p_dev);
+}
+
+static int smc_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ int i;
+
+ link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
+
+ i = pcmcia_loop_config(link, smc_configcheck, NULL);
+ if (!i)
+ dev->base_addr = link->resource[0]->start;
+
+ return i;
+}
+
+
+static int smc_setup(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ /* Check for a LAN function extension tuple */
+ if (!pcmcia_get_mac_from_cis(link, dev))
+ return 0;
+
+ /* Try the third string in the Version 1 Version/ID tuple. */
+ if (link->prod_id[2]) {
+ if (cvt_ascii_address(dev, link->prod_id[2]) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+/*====================================================================*/
+
+static int osi_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+ int i, j;
+
+ link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ;
+ link->resource[0]->end = 64;
+ link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
+ link->resource[1]->end = 8;
+
+ /* Enable Hard Decode, LAN, Modem */
+ link->io_lines = 16;
+ link->config_index = 0x23;
+
+ for (i = j = 0; j < 4; j++) {
+ link->resource[1]->start = com[j];
+ i = pcmcia_request_io(link);
+ if (i == 0)
+ break;
+ }
+ if (i != 0) {
+ /* Fallback: turn off hard decode */
+ link->config_index = 0x03;
+ link->resource[1]->end = 0;
+ i = pcmcia_request_io(link);
+ }
+ dev->base_addr = link->resource[0]->start + 0x10;
+ return i;
+}
+
+static int osi_load_firmware(struct pcmcia_device *link)
+{
+ const struct firmware *fw;
+ int i, err;
+
+ err = request_firmware(&fw, FIRMWARE_NAME, &link->dev);
+ if (err) {
+ pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME);
+ return err;
+ }
+
+ /* Download the Seven of Diamonds firmware */
+ for (i = 0; i < fw->size; i++) {
+ outb(fw->data[i], link->resource[0]->start + 2);
+ udelay(50);
+ }
+ release_firmware(fw);
+ return err;
+}
+
+static int pcmcia_osi_mac(struct pcmcia_device *p_dev,
+ tuple_t *tuple,
+ void *priv)
+{
+ struct net_device *dev = priv;
+ int i;
+
+ if (tuple->TupleDataLen < 8)
+ return -EINVAL;
+ if (tuple->TupleData[0] != 0x04)
+ return -EINVAL;
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = tuple->TupleData[i+2];
+ return 0;
+};
+
+
+static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
+{
+ struct net_device *dev = link->priv;
+ int rc;
+
+ /* Read the station address from tuple 0x90, subtuple 0x04 */
+ if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev))
+ return -1;
+
+ if (((manfid == MANFID_OSITECH) &&
+ (cardid == PRODID_OSITECH_SEVEN)) ||
+ ((manfid == MANFID_PSION) &&
+ (cardid == PRODID_PSION_NET100))) {
+ rc = osi_load_firmware(link);
+ if (rc)
+ return rc;
+ } else if (manfid == MANFID_OSITECH) {
+ /* Make sure both functions are powered up */
+ set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR);
+ /* Now, turn on the interrupt for both card functions */
+ set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR);
+ dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
+ inw(link->resource[0]->start + OSITECH_AUI_PWR),
+ inw(link->resource[0]->start + OSITECH_RESET_ISR));
+ }
+ return 0;
+}
+
+static int smc91c92_suspend(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+
+ if (link->open)
+ netif_device_detach(dev);
+
+ return 0;
+}
+
+static int smc91c92_resume(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *smc = netdev_priv(dev);
+ int i;
+
+ if ((smc->manfid == MANFID_MEGAHERTZ) &&
+ (smc->cardid == PRODID_MEGAHERTZ_EM3288))
+ mhz_3288_power(link);
+ if (smc->manfid == MANFID_MOTOROLA)
+ mot_config(link);
+ if ((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid != PRODID_OSITECH_SEVEN)) {
+ /* Power up the card and enable interrupts */
+ set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
+ set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
+ }
+ if (((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid == PRODID_OSITECH_SEVEN)) ||
+ ((smc->manfid == MANFID_PSION) &&
+ (smc->cardid == PRODID_PSION_NET100))) {
+ i = osi_load_firmware(link);
+ if (i) {
+ pr_err("smc91c92_cs: Failed to load firmware\n");
+ return i;
+ }
+ }
+ if (link->open) {
+ smc_reset(dev);
+ netif_device_attach(dev);
+ }
+
+ return 0;
+}
+
+
+/*======================================================================
+
+ This verifies that the chip is some SMC91cXX variant, and returns
+ the revision code if successful. Otherwise, it returns -ENODEV.
+
+======================================================================*/
+
+static int check_sig(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ unsigned int ioaddr = dev->base_addr;
+ int width;
+ u_short s;
+
+ SMC_SELECT_BANK(1);
+ if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
+ /* Try powering up the chip */
+ outw(0, ioaddr + CONTROL);
+ mdelay(55);
+ }
+
+ /* Try setting bus width */
+ width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO);
+ s = inb(ioaddr + CONFIG);
+ if (width)
+ s |= CFG_16BIT;
+ else
+ s &= ~CFG_16BIT;
+ outb(s, ioaddr + CONFIG);
+
+ /* Check Base Address Register to make sure bus width is OK */
+ s = inw(ioaddr + BASE_ADDR);
+ if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
+ ((s >> 8) != (s & 0xff))) {
+ SMC_SELECT_BANK(3);
+ s = inw(ioaddr + REVISION);
+ return s & 0xff;
+ }
+
+ if (width) {
+ pr_info("using 8-bit IO window\n");
+
+ smc91c92_suspend(link);
+ pcmcia_fixup_iowidth(link);
+ smc91c92_resume(link);
+ return check_sig(link);
+ }
+ return -ENODEV;
+}
+
+static int smc91c92_config(struct pcmcia_device *link)
+{
+ struct net_device *dev = link->priv;
+ struct smc_private *smc = netdev_priv(dev);
+ char *name;
+ int i, rev, j = 0;
+ unsigned int ioaddr;
+ u_long mir;
+
+ dev_dbg(&link->dev, "smc91c92_config\n");
+
+ smc->manfid = link->manf_id;
+ smc->cardid = link->card_id;
+
+ if ((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid != PRODID_OSITECH_SEVEN)) {
+ i = osi_config(link);
+ } else if ((smc->manfid == MANFID_MOTOROLA) ||
+ ((smc->manfid == MANFID_MEGAHERTZ) &&
+ ((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) ||
+ (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) {
+ i = mhz_mfc_config(link);
+ } else {
+ i = smc_config(link);
+ }
+ if (i)
+ goto config_failed;
+
+ i = pcmcia_request_irq(link, smc_interrupt);
+ if (i)
+ goto config_failed;
+ i = pcmcia_enable_device(link);
+ if (i)
+ goto config_failed;
+
+ if (smc->manfid == MANFID_MOTOROLA)
+ mot_config(link);
+
+ dev->irq = link->irq;
+
+ if ((if_port >= 0) && (if_port <= 2))
+ dev->if_port = if_port;
+ else
+ dev_notice(&link->dev, "invalid if_port requested\n");
+
+ switch (smc->manfid) {
+ case MANFID_OSITECH:
+ case MANFID_PSION:
+ i = osi_setup(link, smc->manfid, smc->cardid); break;
+ case MANFID_SMC:
+ case MANFID_NEW_MEDIA:
+ i = smc_setup(link); break;
+ case 0x128: /* For broken Megahertz cards */
+ case MANFID_MEGAHERTZ:
+ i = mhz_setup(link); break;
+ case MANFID_MOTOROLA:
+ default: /* get the hw address from EEPROM */
+ i = mot_setup(link); break;
+ }
+
+ if (i != 0) {
+ dev_notice(&link->dev, "Unable to find hardware address.\n");
+ goto config_failed;
+ }
+
+ smc->duplex = 0;
+ smc->rx_ovrn = 0;
+
+ rev = check_sig(link);
+ name = "???";
+ if (rev > 0)
+ switch (rev >> 4) {
+ case 3: name = "92"; break;
+ case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
+ case 5: name = "95"; break;
+ case 7: name = "100"; break;
+ case 8: name = "100-FD"; break;
+ case 9: name = "110"; break;
+ }
+
+ ioaddr = dev->base_addr;
+ if (rev > 0) {
+ u_long mcr;
+ SMC_SELECT_BANK(0);
+ mir = inw(ioaddr + MEMINFO) & 0xff;
+ if (mir == 0xff) mir++;
+ /* Get scale factor for memory size */
+ mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
+ mir *= 128 * (1<<((mcr >> 9) & 7));
+ SMC_SELECT_BANK(1);
+ smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
+ smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC;
+ if (smc->manfid == MANFID_OSITECH)
+ smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0;
+ if ((rev >> 4) >= 7)
+ smc->cfg |= CFG_MII_SELECT;
+ } else
+ mir = 0;
+
+ if (smc->cfg & CFG_MII_SELECT) {
+ SMC_SELECT_BANK(3);
+
+ for (i = 0; i < 32; i++) {
+ j = mdio_read(dev, i, 1);
+ if ((j != 0) && (j != 0xffff)) break;
+ }
+ smc->mii_if.phy_id = (i < 32) ? i : -1;
+
+ SMC_SELECT_BANK(0);
+ }
+
+ SET_NETDEV_DEV(dev, &link->dev);
+
+ if (register_netdev(dev) != 0) {
+ dev_err(&link->dev, "register_netdev() failed\n");
+ goto config_undo;
+ }
+
+ netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n",
+ name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr);
+
+ if (rev > 0) {
+ if (mir & 0x3ff)
+ netdev_info(dev, " %lu byte", mir);
+ else
+ netdev_info(dev, " %lu kb", mir>>10);
+ pr_cont(" buffer, %s xcvr\n",
+ (smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]);
+ }
+
+ if (smc->cfg & CFG_MII_SELECT) {
+ if (smc->mii_if.phy_id != -1) {
+ netdev_dbg(dev, " MII transceiver at index %d, status %x\n",
+ smc->mii_if.phy_id, j);
+ } else {
+ netdev_notice(dev, " No MII transceivers found!\n");
+ }
+ }
+ return 0;
+
+config_undo:
+ unregister_netdev(dev);
+config_failed:
+ smc91c92_release(link);
+ free_netdev(dev);
+ return -ENODEV;
+} /* smc91c92_config */
+
+static void smc91c92_release(struct pcmcia_device *link)
+{
+ dev_dbg(&link->dev, "smc91c92_release\n");
+ if (link->resource[2]->end) {
+ struct net_device *dev = link->priv;
+ struct smc_private *smc = netdev_priv(dev);
+ iounmap(smc->base);
+ }
+ pcmcia_disable_device(link);
+}
+
+/*======================================================================
+
+ MII interface support for SMC91cXX based cards
+======================================================================*/
+
+#define MDIO_SHIFT_CLK 0x04
+#define MDIO_DATA_OUT 0x01
+#define MDIO_DIR_WRITE 0x08
+#define MDIO_DATA_WRITE0 (MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ 0x02
+
+static void mdio_sync(unsigned int addr)
+{
+ int bits;
+ for (bits = 0; bits < 32; bits++) {
+ outb(MDIO_DATA_WRITE1, addr);
+ outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+ }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int loc)
+{
+ unsigned int addr = dev->base_addr + MGMT;
+ u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+ int i, retval = 0;
+
+ mdio_sync(addr);
+ for (i = 13; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(dat, addr);
+ outb(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 19; i > 0; i--) {
+ outb(0, addr);
+ retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+ outb(MDIO_SHIFT_CLK, addr);
+ }
+ return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
+{
+ unsigned int addr = dev->base_addr + MGMT;
+ u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+ int i;
+
+ mdio_sync(addr);
+ for (i = 31; i >= 0; i--) {
+ int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outb(dat, addr);
+ outb(dat | MDIO_SHIFT_CLK, addr);
+ }
+ for (i = 1; i >= 0; i--) {
+ outb(0, addr);
+ outb(MDIO_SHIFT_CLK, addr);
+ }
+}
+
+/*======================================================================
+
+ The driver core code, most of which should be common with a
+ non-PCMCIA implementation.
+
+======================================================================*/
+
+#ifdef PCMCIA_DEBUG
+static void smc_dump(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ u_short i, w, save;
+ save = inw(ioaddr + BANK_SELECT);
+ for (w = 0; w < 4; w++) {
+ SMC_SELECT_BANK(w);
+ netdev_printk(KERN_DEBUG, dev, "bank %d: ", w);
+ for (i = 0; i < 14; i += 2)
+ pr_cont(" %04x", inw(ioaddr + i));
+ pr_cont("\n");
+ }
+ outw(save, ioaddr + BANK_SELECT);
+}
+#endif
+
+static int smc_open(struct net_device *dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ struct pcmcia_device *link = smc->p_dev;
+
+ dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n",
+ dev->name, dev, inw(dev->base_addr + BANK_SELECT));
+#ifdef PCMCIA_DEBUG
+ smc_dump(dev);
+#endif
+
+ /* Check that the PCMCIA card is still here. */
+ if (!pcmcia_dev_present(link))
+ return -ENODEV;
+ /* Physical device present signature. */
+ if (check_sig(link) < 0) {
+ netdev_info(dev, "Yikes! Bad chip signature!\n");
+ return -ENODEV;
+ }
+ link->open++;
+
+ netif_start_queue(dev);
+ smc->saved_skb = NULL;
+ smc->packets_waiting = 0;
+
+ smc_reset(dev);
+ init_timer(&smc->media);
+ smc->media.function = media_check;
+ smc->media.data = (u_long) dev;
+ smc->media.expires = jiffies + HZ;
+ add_timer(&smc->media);
+
+ return 0;
+} /* smc_open */
+
+/*====================================================================*/
+
+static int smc_close(struct net_device *dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ struct pcmcia_device *link = smc->p_dev;
+ unsigned int ioaddr = dev->base_addr;
+
+ dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n",
+ dev->name, inw(ioaddr + BANK_SELECT));
+
+ netif_stop_queue(dev);
+
+ /* Shut off all interrupts, and turn off the Tx and Rx sections.
+ Don't bother to check for chip present. */
+ SMC_SELECT_BANK(2); /* Nominally paranoia, but do no assume... */
+ outw(0, ioaddr + INTERRUPT);
+ SMC_SELECT_BANK(0);
+ mask_bits(0xff00, ioaddr + RCR);
+ mask_bits(0xff00, ioaddr + TCR);
+
+ /* Put the chip into power-down mode. */
+ SMC_SELECT_BANK(1);
+ outw(CTL_POWERDOWN, ioaddr + CONTROL );
+
+ link->open--;
+ del_timer_sync(&smc->media);
+
+ return 0;
+} /* smc_close */
+
+/*======================================================================
+
+ Transfer a packet to the hardware and trigger the packet send.
+ This may be called at either from either the Tx queue code
+ or the interrupt handler.
+
+======================================================================*/
+
+static void smc_hardware_send_packet(struct net_device * dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ struct sk_buff *skb = smc->saved_skb;
+ unsigned int ioaddr = dev->base_addr;
+ u_char packet_no;
+
+ if (!skb) {
+ netdev_err(dev, "In XMIT with no packet to send\n");
+ return;
+ }
+
+ /* There should be a packet slot waiting. */
+ packet_no = inw(ioaddr + PNR_ARR) >> 8;
+ if (packet_no & 0x80) {
+ /* If not, there is a hardware problem! Likely an ejected card. */
+ netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n",
+ packet_no);
+ dev_kfree_skb_irq(skb);
+ smc->saved_skb = NULL;
+ netif_start_queue(dev);
+ return;
+ }
+
+ dev->stats.tx_bytes += skb->len;
+ /* The card should use the just-allocated buffer. */
+ outw(packet_no, ioaddr + PNR_ARR);
+ /* point to the beginning of the packet */
+ outw(PTR_AUTOINC , ioaddr + POINTER);
+
+ /* Send the packet length (+6 for status, length and ctl byte)
+ and the status word (set to zeros). */
+ {
+ u_char *buf = skb->data;
+ u_int length = skb->len; /* The chip will pad to ethernet min. */
+
+ netdev_dbg(dev, "Trying to xmit packet of length %d\n", length);
+
+ /* send the packet length: +6 for status word, length, and ctl */
+ outw(0, ioaddr + DATA_1);
+ outw(length + 6, ioaddr + DATA_1);
+ outsw(ioaddr + DATA_1, buf, length >> 1);
+
+ /* The odd last byte, if there is one, goes in the control word. */
+ outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
+ }
+
+ /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
+ outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
+ (inw(ioaddr + INTERRUPT) & 0xff00),
+ ioaddr + INTERRUPT);
+
+ /* The chip does the rest of the work. */
+ outw(MC_ENQUEUE , ioaddr + MMU_CMD);
+
+ smc->saved_skb = NULL;
+ dev_kfree_skb_irq(skb);
+ dev->trans_start = jiffies;
+ netif_start_queue(dev);
+}
+
+/*====================================================================*/
+
+static void smc_tx_timeout(struct net_device *dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+
+ netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n",
+ inw(ioaddr)&0xff, inw(ioaddr + 2));
+ dev->stats.tx_errors++;
+ smc_reset(dev);
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ smc->saved_skb = NULL;
+ netif_wake_queue(dev);
+}
+
+static netdev_tx_t smc_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u_short num_pages;
+ short time_out, ir;
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+
+ netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n",
+ skb->len, inw(ioaddr + 2));
+
+ if (smc->saved_skb) {
+ /* THIS SHOULD NEVER HAPPEN. */
+ dev->stats.tx_aborted_errors++;
+ netdev_printk(KERN_DEBUG, dev,
+ "Internal error -- sent packet while busy\n");
+ return NETDEV_TX_BUSY;
+ }
+ smc->saved_skb = skb;
+
+ num_pages = skb->len >> 8;
+
+ if (num_pages > 7) {
+ netdev_err(dev, "Far too big packet error: %d pages\n", num_pages);
+ dev_kfree_skb (skb);
+ smc->saved_skb = NULL;
+ dev->stats.tx_dropped++;
+ return NETDEV_TX_OK; /* Do not re-queue this packet. */
+ }
+ /* A packet is now waiting. */
+ smc->packets_waiting++;
+
+ spin_lock_irqsave(&smc->lock, flags);
+ SMC_SELECT_BANK(2); /* Paranoia, we should always be in window 2 */
+
+ /* need MC_RESET to keep the memory consistent. errata? */
+ if (smc->rx_ovrn) {
+ outw(MC_RESET, ioaddr + MMU_CMD);
+ smc->rx_ovrn = 0;
+ }
+
+ /* Allocate the memory; send the packet now if we win. */
+ outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
+ for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
+ ir = inw(ioaddr+INTERRUPT);
+ if (ir & IM_ALLOC_INT) {
+ /* Acknowledge the interrupt, send the packet. */
+ outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
+ smc_hardware_send_packet(dev); /* Send the packet now.. */
+ spin_unlock_irqrestore(&smc->lock, flags);
+ return NETDEV_TX_OK;
+ }
+ }
+
+ /* Otherwise defer until the Tx-space-allocated interrupt. */
+ pr_debug("%s: memory allocation deferred.\n", dev->name);
+ outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
+ spin_unlock_irqrestore(&smc->lock, flags);
+
+ return NETDEV_TX_OK;
+}
+
+/*======================================================================
+
+ Handle a Tx anomalous event. Entered while in Window 2.
+
+======================================================================*/
+
+static void smc_tx_err(struct net_device * dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
+ int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
+ int tx_status;
+
+ /* select this as the packet to read from */
+ outw(packet_no, ioaddr + PNR_ARR);
+
+ /* read the first word from this packet */
+ outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
+
+ tx_status = inw(ioaddr + DATA_1);
+
+ dev->stats.tx_errors++;
+ if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++;
+ if (tx_status & TS_LATCOL) dev->stats.tx_window_errors++;
+ if (tx_status & TS_16COL) {
+ dev->stats.tx_aborted_errors++;
+ smc->tx_err++;
+ }
+
+ if (tx_status & TS_SUCCESS) {
+ netdev_notice(dev, "Successful packet caused error interrupt?\n");
+ }
+ /* re-enable transmit */
+ SMC_SELECT_BANK(0);
+ outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
+ SMC_SELECT_BANK(2);
+
+ outw(MC_FREEPKT, ioaddr + MMU_CMD); /* Free the packet memory. */
+
+ /* one less packet waiting for me */
+ smc->packets_waiting--;
+
+ outw(saved_packet, ioaddr + PNR_ARR);
+}
+
+/*====================================================================*/
+
+static void smc_eph_irq(struct net_device *dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u_short card_stats, ephs;
+
+ SMC_SELECT_BANK(0);
+ ephs = inw(ioaddr + EPH);
+ pr_debug("%s: Ethernet protocol handler interrupt, status"
+ " %4.4x.\n", dev->name, ephs);
+ /* Could be a counter roll-over warning: update stats. */
+ card_stats = inw(ioaddr + COUNTER);
+ /* single collisions */
+ dev->stats.collisions += card_stats & 0xF;
+ card_stats >>= 4;
+ /* multiple collisions */
+ dev->stats.collisions += card_stats & 0xF;
+#if 0 /* These are for when linux supports these statistics */
+ card_stats >>= 4; /* deferred */
+ card_stats >>= 4; /* excess deferred */
+#endif
+ /* If we had a transmit error we must re-enable the transmitter. */
+ outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
+
+ /* Clear a link error interrupt. */
+ SMC_SELECT_BANK(1);
+ outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
+ outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+ ioaddr + CONTROL);
+ SMC_SELECT_BANK(2);
+}
+
+/*====================================================================*/
+
+static irqreturn_t smc_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr;
+ u_short saved_bank, saved_pointer, mask, status;
+ unsigned int handled = 1;
+ char bogus_cnt = INTR_WORK; /* Work we are willing to do. */
+
+ if (!netif_device_present(dev))
+ return IRQ_NONE;
+
+ ioaddr = dev->base_addr;
+
+ pr_debug("%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
+ irq, ioaddr);
+
+ spin_lock(&smc->lock);
+ smc->watchdog = 0;
+ saved_bank = inw(ioaddr + BANK_SELECT);
+ if ((saved_bank & 0xff00) != 0x3300) {
+ /* The device does not exist -- the card could be off-line, or
+ maybe it has been ejected. */
+ pr_debug("%s: SMC91c92 interrupt %d for non-existent"
+ "/ejected device.\n", dev->name, irq);
+ handled = 0;
+ goto irq_done;
+ }
+
+ SMC_SELECT_BANK(2);
+ saved_pointer = inw(ioaddr + POINTER);
+ mask = inw(ioaddr + INTERRUPT) >> 8;
+ /* clear all interrupts */
+ outw(0, ioaddr + INTERRUPT);
+
+ do { /* read the status flag, and mask it */
+ status = inw(ioaddr + INTERRUPT) & 0xff;
+ pr_debug("%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
+ status, mask);
+ if ((status & mask) == 0) {
+ if (bogus_cnt == INTR_WORK)
+ handled = 0;
+ break;
+ }
+ if (status & IM_RCV_INT) {
+ /* Got a packet(s). */
+ smc_rx(dev);
+ }
+ if (status & IM_TX_INT) {
+ smc_tx_err(dev);
+ outw(IM_TX_INT, ioaddr + INTERRUPT);
+ }
+ status &= mask;
+ if (status & IM_TX_EMPTY_INT) {
+ outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
+ mask &= ~IM_TX_EMPTY_INT;
+ dev->stats.tx_packets += smc->packets_waiting;
+ smc->packets_waiting = 0;
+ }
+ if (status & IM_ALLOC_INT) {
+ /* Clear this interrupt so it doesn't happen again */
+ mask &= ~IM_ALLOC_INT;
+
+ smc_hardware_send_packet(dev);
+
+ /* enable xmit interrupts based on this */
+ mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
+
+ /* and let the card send more packets to me */
+ netif_wake_queue(dev);
+ }
+ if (status & IM_RX_OVRN_INT) {
+ dev->stats.rx_errors++;
+ dev->stats.rx_fifo_errors++;
+ if (smc->duplex)
+ smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
+ outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
+ }
+ if (status & IM_EPH_INT)
+ smc_eph_irq(dev);
+ } while (--bogus_cnt);
+
+ pr_debug(" Restoring saved registers mask %2.2x bank %4.4x"
+ " pointer %4.4x.\n", mask, saved_bank, saved_pointer);
+
+ /* restore state register */
+ outw((mask<<8), ioaddr + INTERRUPT);
+ outw(saved_pointer, ioaddr + POINTER);
+ SMC_SELECT_BANK(saved_bank);
+
+ pr_debug("%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
+
+irq_done:
+
+ if ((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid != PRODID_OSITECH_SEVEN)) {
+ /* Retrigger interrupt if needed */
+ mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
+ set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
+ }
+ if (smc->manfid == MANFID_MOTOROLA) {
+ u_char cor;
+ cor = readb(smc->base + MOT_UART + CISREG_COR);
+ writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR);
+ writeb(cor, smc->base + MOT_UART + CISREG_COR);
+ cor = readb(smc->base + MOT_LAN + CISREG_COR);
+ writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR);
+ writeb(cor, smc->base + MOT_LAN + CISREG_COR);
+ }
+
+ if ((smc->base != NULL) && /* Megahertz MFC's */
+ (smc->manfid == MANFID_MEGAHERTZ) &&
+ (smc->cardid == PRODID_MEGAHERTZ_EM3288)) {
+
+ u_char tmp;
+ tmp = readb(smc->base+MEGAHERTZ_ISR);
+ tmp = readb(smc->base+MEGAHERTZ_ISR);
+
+ /* Retrigger interrupt if needed */
+ writeb(tmp, smc->base + MEGAHERTZ_ISR);
+ writeb(tmp, smc->base + MEGAHERTZ_ISR);
+ }
+
+ spin_unlock(&smc->lock);
+ return IRQ_RETVAL(handled);
+}
+
+/*====================================================================*/
+
+static void smc_rx(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ int rx_status;
+ int packet_length; /* Caution: not frame length, rather words
+ to transfer from the chip. */
+
+ /* Assertion: we are in Window 2. */
+
+ if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
+ netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n");
+ return;
+ }
+
+ /* Reset the read pointer, and read the status and packet length. */
+ outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
+ rx_status = inw(ioaddr + DATA_1);
+ packet_length = inw(ioaddr + DATA_1) & 0x07ff;
+
+ pr_debug("%s: Receive status %4.4x length %d.\n",
+ dev->name, rx_status, packet_length);
+
+ if (!(rx_status & RS_ERRORS)) {
+ /* do stuff to make a new packet */
+ struct sk_buff *skb;
+
+ /* Note: packet_length adds 5 or 6 extra bytes here! */
+ skb = dev_alloc_skb(packet_length+2);
+
+ if (skb == NULL) {
+ pr_debug("%s: Low memory, packet dropped.\n", dev->name);
+ dev->stats.rx_dropped++;
+ outw(MC_RELEASE, ioaddr + MMU_CMD);
+ return;
+ }
+
+ packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
+ skb_reserve(skb, 2);
+ insw(ioaddr+DATA_1, skb_put(skb, packet_length),
+ (packet_length+1)>>1);
+ skb->protocol = eth_type_trans(skb, dev);
+
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += packet_length;
+ if (rx_status & RS_MULTICAST)
+ dev->stats.multicast++;
+ } else {
+ /* error ... */
+ dev->stats.rx_errors++;
+
+ if (rx_status & RS_ALGNERR) dev->stats.rx_frame_errors++;
+ if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
+ dev->stats.rx_length_errors++;
+ if (rx_status & RS_BADCRC) dev->stats.rx_crc_errors++;
+ }
+ /* Let the MMU free the memory of this packet. */
+ outw(MC_RELEASE, ioaddr + MMU_CMD);
+}
+
+/*======================================================================
+
+ Set the receive mode.
+
+ This routine is used by both the protocol level to notify us of
+ promiscuous/multicast mode changes, and by the open/reset code to
+ initialize the Rx registers. We always set the multicast list and
+ leave the receiver running.
+
+======================================================================*/
+
+static void set_rx_mode(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned char multicast_table[8];
+ unsigned long flags;
+ u_short rx_cfg_setting;
+ int i;
+
+ memset(multicast_table, 0, sizeof(multicast_table));
+
+ if (dev->flags & IFF_PROMISC) {
+ rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
+ } else if (dev->flags & IFF_ALLMULTI)
+ rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
+ else {
+ if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+
+ netdev_for_each_mc_addr(ha, dev) {
+ u_int position = ether_crc(6, ha->addr);
+ multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
+ }
+ }
+ rx_cfg_setting = RxStripCRC | RxEnable;
+ }
+
+ /* Load MC table and Rx setting into the chip without interrupts. */
+ spin_lock_irqsave(&smc->lock, flags);
+ SMC_SELECT_BANK(3);
+ for (i = 0; i < 8; i++)
+ outb(multicast_table[i], ioaddr + MULTICAST0 + i);
+ SMC_SELECT_BANK(0);
+ outw(rx_cfg_setting, ioaddr + RCR);
+ SMC_SELECT_BANK(2);
+ spin_unlock_irqrestore(&smc->lock, flags);
+}
+
+/*======================================================================
+
+ Senses when a card's config changes. Here, it's coax or TP.
+
+======================================================================*/
+
+static int s9k_config(struct net_device *dev, struct ifmap *map)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+ if (smc->cfg & CFG_MII_SELECT)
+ return -EOPNOTSUPP;
+ else if (map->port > 2)
+ return -EINVAL;
+ dev->if_port = map->port;
+ netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
+ smc_reset(dev);
+ }
+ return 0;
+}
+
+/*======================================================================
+
+ Reset the chip, reloading every register that might be corrupted.
+
+======================================================================*/
+
+/*
+ Set transceiver type, perhaps to something other than what the user
+ specified in dev->if_port.
+*/
+static void smc_set_xcvr(struct net_device *dev, int if_port)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u_short saved_bank;
+
+ saved_bank = inw(ioaddr + BANK_SELECT);
+ SMC_SELECT_BANK(1);
+ if (if_port == 2) {
+ outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
+ if ((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid != PRODID_OSITECH_SEVEN))
+ set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+ smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
+ } else {
+ outw(smc->cfg, ioaddr + CONFIG);
+ if ((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid != PRODID_OSITECH_SEVEN))
+ mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+ smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
+ }
+ SMC_SELECT_BANK(saved_bank);
+}
+
+static void smc_reset(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ struct smc_private *smc = netdev_priv(dev);
+ int i;
+
+ pr_debug("%s: smc91c92 reset called.\n", dev->name);
+
+ /* The first interaction must be a write to bring the chip out
+ of sleep mode. */
+ SMC_SELECT_BANK(0);
+ /* Reset the chip. */
+ outw(RCR_SOFTRESET, ioaddr + RCR);
+ udelay(10);
+
+ /* Clear the transmit and receive configuration registers. */
+ outw(RCR_CLEAR, ioaddr + RCR);
+ outw(TCR_CLEAR, ioaddr + TCR);
+
+ /* Set the Window 1 control, configuration and station addr registers.
+ No point in writing the I/O base register ;-> */
+ SMC_SELECT_BANK(1);
+ /* Automatically release successfully transmitted packets,
+ Accept link errors, counter and Tx error interrupts. */
+ outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+ ioaddr + CONTROL);
+ smc_set_xcvr(dev, dev->if_port);
+ if ((smc->manfid == MANFID_OSITECH) &&
+ (smc->cardid != PRODID_OSITECH_SEVEN))
+ outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
+ (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
+ ioaddr - 0x10 + OSITECH_AUI_PWR);
+
+ /* Fill in the physical address. The databook is wrong about the order! */
+ for (i = 0; i < 6; i += 2)
+ outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
+ ioaddr + ADDR0 + i);
+
+ /* Reset the MMU */
+ SMC_SELECT_BANK(2);
+ outw(MC_RESET, ioaddr + MMU_CMD);
+ outw(0, ioaddr + INTERRUPT);
+
+ /* Re-enable the chip. */
+ SMC_SELECT_BANK(0);
+ outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
+ TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
+ set_rx_mode(dev);
+
+ if (smc->cfg & CFG_MII_SELECT) {
+ SMC_SELECT_BANK(3);
+
+ /* Reset MII */
+ mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
+
+ /* Advertise 100F, 100H, 10F, 10H */
+ mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
+
+ /* Restart MII autonegotiation */
+ mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
+ mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
+ }
+
+ /* Enable interrupts. */
+ SMC_SELECT_BANK(2);
+ outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
+ ioaddr + INTERRUPT);
+}
+
+/*======================================================================
+
+ Media selection timer routine
+
+======================================================================*/
+
+static void media_check(u_long arg)
+{
+ struct net_device *dev = (struct net_device *) arg;
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u_short i, media, saved_bank;
+ u_short link;
+ unsigned long flags;
+
+ spin_lock_irqsave(&smc->lock, flags);
+
+ saved_bank = inw(ioaddr + BANK_SELECT);
+
+ if (!netif_device_present(dev))
+ goto reschedule;
+
+ SMC_SELECT_BANK(2);
+
+ /* need MC_RESET to keep the memory consistent. errata? */
+ if (smc->rx_ovrn) {
+ outw(MC_RESET, ioaddr + MMU_CMD);
+ smc->rx_ovrn = 0;
+ }
+ i = inw(ioaddr + INTERRUPT);
+ SMC_SELECT_BANK(0);
+ media = inw(ioaddr + EPH) & EPH_LINK_OK;
+ SMC_SELECT_BANK(1);
+ media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
+
+ SMC_SELECT_BANK(saved_bank);
+ spin_unlock_irqrestore(&smc->lock, flags);
+
+ /* Check for pending interrupt with watchdog flag set: with
+ this, we can limp along even if the interrupt is blocked */
+ if (smc->watchdog++ && ((i>>8) & i)) {
+ if (!smc->fast_poll)
+ netdev_info(dev, "interrupt(s) dropped!\n");
+ local_irq_save(flags);
+ smc_interrupt(dev->irq, dev);
+ local_irq_restore(flags);
+ smc->fast_poll = HZ;
+ }
+ if (smc->fast_poll) {
+ smc->fast_poll--;
+ smc->media.expires = jiffies + HZ/100;
+ add_timer(&smc->media);
+ return;
+ }
+
+ spin_lock_irqsave(&smc->lock, flags);
+
+ saved_bank = inw(ioaddr + BANK_SELECT);
+
+ if (smc->cfg & CFG_MII_SELECT) {
+ if (smc->mii_if.phy_id < 0)
+ goto reschedule;
+
+ SMC_SELECT_BANK(3);
+ link = mdio_read(dev, smc->mii_if.phy_id, 1);
+ if (!link || (link == 0xffff)) {
+ netdev_info(dev, "MII is missing!\n");
+ smc->mii_if.phy_id = -1;
+ goto reschedule;
+ }
+
+ link &= 0x0004;
+ if (link != smc->link_status) {
+ u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
+ netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
+ smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
+ ? TCR_FDUPLX : 0);
+ if (link) {
+ netdev_info(dev, "autonegotiation complete: "
+ "%dbaseT-%cD selected\n",
+ (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H');
+ }
+ SMC_SELECT_BANK(0);
+ outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
+ smc->link_status = link;
+ }
+ goto reschedule;
+ }
+
+ /* Ignore collisions unless we've had no rx's recently */
+ if (time_after(jiffies, dev->last_rx + HZ)) {
+ if (smc->tx_err || (smc->media_status & EPH_16COL))
+ media |= EPH_16COL;
+ }
+ smc->tx_err = 0;
+
+ if (media != smc->media_status) {
+ if ((media & smc->media_status & 1) &&
+ ((smc->media_status ^ media) & EPH_LINK_OK))
+ netdev_info(dev, "%s link beat\n",
+ smc->media_status & EPH_LINK_OK ? "lost" : "found");
+ else if ((media & smc->media_status & 2) &&
+ ((smc->media_status ^ media) & EPH_16COL))
+ netdev_info(dev, "coax cable %s\n",
+ media & EPH_16COL ? "problem" : "ok");
+ if (dev->if_port == 0) {
+ if (media & 1) {
+ if (media & EPH_LINK_OK)
+ netdev_info(dev, "flipped to 10baseT\n");
+ else
+ smc_set_xcvr(dev, 2);
+ } else {
+ if (media & EPH_16COL)
+ smc_set_xcvr(dev, 1);
+ else
+ netdev_info(dev, "flipped to 10base2\n");
+ }
+ }
+ smc->media_status = media;
+ }
+
+reschedule:
+ smc->media.expires = jiffies + HZ;
+ add_timer(&smc->media);
+ SMC_SELECT_BANK(saved_bank);
+ spin_unlock_irqrestore(&smc->lock, flags);
+}
+
+static int smc_link_ok(struct net_device *dev)
+{
+ unsigned int ioaddr = dev->base_addr;
+ struct smc_private *smc = netdev_priv(dev);
+
+ if (smc->cfg & CFG_MII_SELECT) {
+ return mii_link_ok(&smc->mii_if);
+ } else {
+ SMC_SELECT_BANK(0);
+ return inw(ioaddr + EPH) & EPH_LINK_OK;
+ }
+}
+
+static int smc_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ u16 tmp;
+ unsigned int ioaddr = dev->base_addr;
+
+ ecmd->supported = (SUPPORTED_TP | SUPPORTED_AUI |
+ SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
+
+ SMC_SELECT_BANK(1);
+ tmp = inw(ioaddr + CONFIG);
+ ecmd->port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
+ ecmd->transceiver = XCVR_INTERNAL;
+ ethtool_cmd_speed_set(ecmd, SPEED_10);
+ ecmd->phy_address = ioaddr + MGMT;
+
+ SMC_SELECT_BANK(0);
+ tmp = inw(ioaddr + TCR);
+ ecmd->duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+
+ return 0;
+}
+
+static int smc_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ u16 tmp;
+ unsigned int ioaddr = dev->base_addr;
+
+ if (ethtool_cmd_speed(ecmd) != SPEED_10)
+ return -EINVAL;
+ if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+ return -EINVAL;
+ if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI)
+ return -EINVAL;
+ if (ecmd->transceiver != XCVR_INTERNAL)
+ return -EINVAL;
+
+ if (ecmd->port == PORT_AUI)
+ smc_set_xcvr(dev, 1);
+ else
+ smc_set_xcvr(dev, 0);
+
+ SMC_SELECT_BANK(0);
+ tmp = inw(ioaddr + TCR);
+ if (ecmd->duplex == DUPLEX_FULL)
+ tmp |= TCR_FDUPLX;
+ else
+ tmp &= ~TCR_FDUPLX;
+ outw(tmp, ioaddr + TCR);
+
+ return 0;
+}
+
+static int check_if_running(struct net_device *dev)
+{
+ if (!netif_running(dev))
+ return -EINVAL;
+ return 0;
+}
+
+static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_NAME);
+ strcpy(info->version, DRV_VERSION);
+}
+
+static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u16 saved_bank = inw(ioaddr + BANK_SELECT);
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&smc->lock, flags);
+ SMC_SELECT_BANK(3);
+ if (smc->cfg & CFG_MII_SELECT)
+ ret = mii_ethtool_gset(&smc->mii_if, ecmd);
+ else
+ ret = smc_netdev_get_ecmd(dev, ecmd);
+ SMC_SELECT_BANK(saved_bank);
+ spin_unlock_irqrestore(&smc->lock, flags);
+ return ret;
+}
+
+static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u16 saved_bank = inw(ioaddr + BANK_SELECT);
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&smc->lock, flags);
+ SMC_SELECT_BANK(3);
+ if (smc->cfg & CFG_MII_SELECT)
+ ret = mii_ethtool_sset(&smc->mii_if, ecmd);
+ else
+ ret = smc_netdev_set_ecmd(dev, ecmd);
+ SMC_SELECT_BANK(saved_bank);
+ spin_unlock_irqrestore(&smc->lock, flags);
+ return ret;
+}
+
+static u32 smc_get_link(struct net_device *dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ unsigned int ioaddr = dev->base_addr;
+ u16 saved_bank = inw(ioaddr + BANK_SELECT);
+ u32 ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&smc->lock, flags);
+ SMC_SELECT_BANK(3);
+ ret = smc_link_ok(dev);
+ SMC_SELECT_BANK(saved_bank);
+ spin_unlock_irqrestore(&smc->lock, flags);
+ return ret;
+}
+
+static int smc_nway_reset(struct net_device *dev)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ if (smc->cfg & CFG_MII_SELECT) {
+ unsigned int ioaddr = dev->base_addr;
+ u16 saved_bank = inw(ioaddr + BANK_SELECT);
+ int res;
+
+ SMC_SELECT_BANK(3);
+ res = mii_nway_restart(&smc->mii_if);
+ SMC_SELECT_BANK(saved_bank);
+
+ return res;
+ } else
+ return -EOPNOTSUPP;
+}
+
+static const struct ethtool_ops ethtool_ops = {
+ .begin = check_if_running,
+ .get_drvinfo = smc_get_drvinfo,
+ .get_settings = smc_get_settings,
+ .set_settings = smc_set_settings,
+ .get_link = smc_get_link,
+ .nway_reset = smc_nway_reset,
+};
+
+static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ struct smc_private *smc = netdev_priv(dev);
+ struct mii_ioctl_data *mii = if_mii(rq);
+ int rc = 0;
+ u16 saved_bank;
+ unsigned int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ spin_lock_irqsave(&smc->lock, flags);
+ saved_bank = inw(ioaddr + BANK_SELECT);
+ SMC_SELECT_BANK(3);
+ rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
+ SMC_SELECT_BANK(saved_bank);
+ spin_unlock_irqrestore(&smc->lock, flags);
+ return rc;
+}
+
+static const struct pcmcia_device_id smc91c92_ids[] = {
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501),
+ PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a),
+ PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
+ PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
+ PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
+ PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
+ PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
+ PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020),
+ PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023),
+ PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb),
+ PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc),
+ PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1),
+ PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5),
+ PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9),
+ PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953),
+ PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a),
+ PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314),
+ PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a),
+ PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc),
+ PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9),
+ PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d),
+ /* These conflict with other cards! */
+ /* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */
+ /* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids);
+
+static struct pcmcia_driver smc91c92_cs_driver = {
+ .owner = THIS_MODULE,
+ .name = "smc91c92_cs",
+ .probe = smc91c92_probe,
+ .remove = smc91c92_detach,
+ .id_table = smc91c92_ids,
+ .suspend = smc91c92_suspend,
+ .resume = smc91c92_resume,
+};
+
+static int __init init_smc91c92_cs(void)
+{
+ return pcmcia_register_driver(&smc91c92_cs_driver);
+}
+
+static void __exit exit_smc91c92_cs(void)
+{
+ pcmcia_unregister_driver(&smc91c92_cs_driver);
+}
+
+module_init(init_smc91c92_cs);
+module_exit(exit_smc91c92_cs);
diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c
new file mode 100644
index 000000000000..2b1d254d59af
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc91x.c
@@ -0,0 +1,2431 @@
+/*
+ * smc91x.c
+ * This is a driver for SMSC's 91C9x/91C1xx single-chip Ethernet devices.
+ *
+ * Copyright (C) 1996 by Erik Stahlman
+ * Copyright (C) 2001 Standard Microsystems Corporation
+ * Developed by Simple Network Magic Corporation
+ * Copyright (C) 2003 Monta Vista Software, Inc.
+ * Unified SMC91x driver by Nicolas Pitre
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Arguments:
+ * io = for the base address
+ * irq = for the IRQ
+ * nowait = 0 for normal wait states, 1 eliminates additional wait states
+ *
+ * original author:
+ * Erik Stahlman <erik@vt.edu>
+ *
+ * hardware multicast code:
+ * Peter Cammaert <pc@denkart.be>
+ *
+ * contributors:
+ * Daris A Nevil <dnevil@snmc.com>
+ * Nicolas Pitre <nico@fluxnic.net>
+ * Russell King <rmk@arm.linux.org.uk>
+ *
+ * History:
+ * 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet
+ * 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ"
+ * 03/16/01 Daris A Nevil modified smc9194.c for use with LAN91C111
+ * 08/22/01 Scott Anderson merge changes from smc9194 to smc91111
+ * 08/21/01 Pramod B Bhardwaj added support for RevB of LAN91C111
+ * 12/20/01 Jeff Sutherland initial port to Xscale PXA with DMA support
+ * 04/07/03 Nicolas Pitre unified SMC91x driver, killed irq races,
+ * more bus abstraction, big cleanup, etc.
+ * 29/09/03 Russell King - add driver model support
+ * - ethtool support
+ * - convert to use generic MII interface
+ * - add link up/down notification
+ * - don't try to handle full negotiation in
+ * smc_phy_configure
+ * - clean up (and fix stack overrun) in PHY
+ * MII read/write functions
+ * 22/09/04 Nicolas Pitre big update (see commit log for details)
+ */
+static const char version[] =
+ "smc91x.c: v1.1, sep 22 2004 by Nicolas Pitre <nico@fluxnic.net>\n";
+
+/* Debugging level */
+#ifndef SMC_DEBUG
+#define SMC_DEBUG 0
+#endif
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include "smc91x.h"
+
+#ifndef SMC_NOWAIT
+# define SMC_NOWAIT 0
+#endif
+static int nowait = SMC_NOWAIT;
+module_param(nowait, int, 0400);
+MODULE_PARM_DESC(nowait, "set to 1 for no wait state");
+
+/*
+ * Transmit timeout, default 5 seconds.
+ */
+static int watchdog = 1000;
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:smc91x");
+
+/*
+ * The internal workings of the driver. If you are changing anything
+ * here with the SMC stuff, you should have the datasheet and know
+ * what you are doing.
+ */
+#define CARDNAME "smc91x"
+
+/*
+ * Use power-down feature of the chip
+ */
+#define POWER_DOWN 1
+
+/*
+ * Wait time for memory to be free. This probably shouldn't be
+ * tuned that much, as waiting for this means nothing else happens
+ * in the system
+ */
+#define MEMORY_WAIT_TIME 16
+
+/*
+ * The maximum number of processing loops allowed for each call to the
+ * IRQ handler.
+ */
+#define MAX_IRQ_LOOPS 8
+
+/*
+ * This selects whether TX packets are sent one by one to the SMC91x internal
+ * memory and throttled until transmission completes. This may prevent
+ * RX overruns a litle by keeping much of the memory free for RX packets
+ * but to the expense of reduced TX throughput and increased IRQ overhead.
+ * Note this is not a cure for a too slow data bus or too high IRQ latency.
+ */
+#define THROTTLE_TX_PKTS 0
+
+/*
+ * The MII clock high/low times. 2x this number gives the MII clock period
+ * in microseconds. (was 50, but this gives 6.4ms for each MII transaction!)
+ */
+#define MII_DELAY 1
+
+#if SMC_DEBUG > 0
+#define DBG(n, args...) \
+ do { \
+ if (SMC_DEBUG >= (n)) \
+ printk(args); \
+ } while (0)
+
+#define PRINTK(args...) printk(args)
+#else
+#define DBG(n, args...) do { } while(0)
+#define PRINTK(args...) printk(KERN_DEBUG args)
+#endif
+
+#if SMC_DEBUG > 3
+static void PRINT_PKT(u_char *buf, int length)
+{
+ int i;
+ int remainder;
+ int lines;
+
+ lines = length / 16;
+ remainder = length % 16;
+
+ for (i = 0; i < lines ; i ++) {
+ int cur;
+ for (cur = 0; cur < 8; cur++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+ }
+ for (i = 0; i < remainder/2 ; i++) {
+ u_char a, b;
+ a = *buf++;
+ b = *buf++;
+ printk("%02x%02x ", a, b);
+ }
+ printk("\n");
+}
+#else
+#define PRINT_PKT(x...) do { } while(0)
+#endif
+
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(lp, x) do { \
+ unsigned char mask; \
+ unsigned long smc_enable_flags; \
+ spin_lock_irqsave(&lp->lock, smc_enable_flags); \
+ mask = SMC_GET_INT_MASK(lp); \
+ mask |= (x); \
+ SMC_SET_INT_MASK(lp, mask); \
+ spin_unlock_irqrestore(&lp->lock, smc_enable_flags); \
+} while (0)
+
+/* this disables an interrupt from the interrupt mask register */
+#define SMC_DISABLE_INT(lp, x) do { \
+ unsigned char mask; \
+ unsigned long smc_disable_flags; \
+ spin_lock_irqsave(&lp->lock, smc_disable_flags); \
+ mask = SMC_GET_INT_MASK(lp); \
+ mask &= ~(x); \
+ SMC_SET_INT_MASK(lp, mask); \
+ spin_unlock_irqrestore(&lp->lock, smc_disable_flags); \
+} while (0)
+
+/*
+ * Wait while MMU is busy. This is usually in the order of a few nanosecs
+ * if at all, but let's avoid deadlocking the system if the hardware
+ * decides to go south.
+ */
+#define SMC_WAIT_MMU_BUSY(lp) do { \
+ if (unlikely(SMC_GET_MMU_CMD(lp) & MC_BUSY)) { \
+ unsigned long timeout = jiffies + 2; \
+ while (SMC_GET_MMU_CMD(lp) & MC_BUSY) { \
+ if (time_after(jiffies, timeout)) { \
+ printk("%s: timeout %s line %d\n", \
+ dev->name, __FILE__, __LINE__); \
+ break; \
+ } \
+ cpu_relax(); \
+ } \
+ } \
+} while (0)
+
+
+/*
+ * this does a soft reset on the device
+ */
+static void smc_reset(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int ctl, cfg;
+ struct sk_buff *pending_skb;
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ /* Disable all interrupts, block TX tasklet */
+ spin_lock_irq(&lp->lock);
+ SMC_SELECT_BANK(lp, 2);
+ SMC_SET_INT_MASK(lp, 0);
+ pending_skb = lp->pending_tx_skb;
+ lp->pending_tx_skb = NULL;
+ spin_unlock_irq(&lp->lock);
+
+ /* free any pending tx skb */
+ if (pending_skb) {
+ dev_kfree_skb(pending_skb);
+ dev->stats.tx_errors++;
+ dev->stats.tx_aborted_errors++;
+ }
+
+ /*
+ * This resets the registers mostly to defaults, but doesn't
+ * affect EEPROM. That seems unnecessary
+ */
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_RCR(lp, RCR_SOFTRST);
+
+ /*
+ * Setup the Configuration Register
+ * This is necessary because the CONFIG_REG is not affected
+ * by a soft reset
+ */
+ SMC_SELECT_BANK(lp, 1);
+
+ cfg = CONFIG_DEFAULT;
+
+ /*
+ * Setup for fast accesses if requested. If the card/system
+ * can't handle it then there will be no recovery except for
+ * a hard reset or power cycle
+ */
+ if (lp->cfg.flags & SMC91X_NOWAIT)
+ cfg |= CONFIG_NO_WAIT;
+
+ /*
+ * Release from possible power-down state
+ * Configuration register is not affected by Soft Reset
+ */
+ cfg |= CONFIG_EPH_POWER_EN;
+
+ SMC_SET_CONFIG(lp, cfg);
+
+ /* this should pause enough for the chip to be happy */
+ /*
+ * elaborate? What does the chip _need_? --jgarzik
+ *
+ * This seems to be undocumented, but something the original
+ * driver(s) have always done. Suspect undocumented timing
+ * info/determined empirically. --rmk
+ */
+ udelay(1);
+
+ /* Disable transmit and receive functionality */
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_RCR(lp, RCR_CLEAR);
+ SMC_SET_TCR(lp, TCR_CLEAR);
+
+ SMC_SELECT_BANK(lp, 1);
+ ctl = SMC_GET_CTL(lp) | CTL_LE_ENABLE;
+
+ /*
+ * Set the control register to automatically release successfully
+ * transmitted packets, to make the best use out of our limited
+ * memory
+ */
+ if(!THROTTLE_TX_PKTS)
+ ctl |= CTL_AUTO_RELEASE;
+ else
+ ctl &= ~CTL_AUTO_RELEASE;
+ SMC_SET_CTL(lp, ctl);
+
+ /* Reset the MMU */
+ SMC_SELECT_BANK(lp, 2);
+ SMC_SET_MMU_CMD(lp, MC_RESET);
+ SMC_WAIT_MMU_BUSY(lp);
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static void smc_enable(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ int mask;
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ /* see the header file for options in TCR/RCR DEFAULT */
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_TCR(lp, lp->tcr_cur_mode);
+ SMC_SET_RCR(lp, lp->rcr_cur_mode);
+
+ SMC_SELECT_BANK(lp, 1);
+ SMC_SET_MAC_ADDR(lp, dev->dev_addr);
+
+ /* now, enable interrupts */
+ mask = IM_EPH_INT|IM_RX_OVRN_INT|IM_RCV_INT;
+ if (lp->version >= (CHIP_91100 << 4))
+ mask |= IM_MDINT;
+ SMC_SELECT_BANK(lp, 2);
+ SMC_SET_INT_MASK(lp, mask);
+
+ /*
+ * From this point the register bank must _NOT_ be switched away
+ * to something else than bank 2 without proper locking against
+ * races with any tasklet or interrupt handlers until smc_shutdown()
+ * or smc_reset() is called.
+ */
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void smc_shutdown(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ struct sk_buff *pending_skb;
+
+ DBG(2, "%s: %s\n", CARDNAME, __func__);
+
+ /* no more interrupts for me */
+ spin_lock_irq(&lp->lock);
+ SMC_SELECT_BANK(lp, 2);
+ SMC_SET_INT_MASK(lp, 0);
+ pending_skb = lp->pending_tx_skb;
+ lp->pending_tx_skb = NULL;
+ spin_unlock_irq(&lp->lock);
+ if (pending_skb)
+ dev_kfree_skb(pending_skb);
+
+ /* and tell the card to stay away from that nasty outside world */
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_RCR(lp, RCR_CLEAR);
+ SMC_SET_TCR(lp, TCR_CLEAR);
+
+#ifdef POWER_DOWN
+ /* finally, shut the chip down */
+ SMC_SELECT_BANK(lp, 1);
+ SMC_SET_CONFIG(lp, SMC_GET_CONFIG(lp) & ~CONFIG_EPH_POWER_EN);
+#endif
+}
+
+/*
+ * This is the procedure to handle the receipt of a packet.
+ */
+static inline void smc_rcv(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int packet_number, status, packet_len;
+
+ DBG(3, "%s: %s\n", dev->name, __func__);
+
+ packet_number = SMC_GET_RXFIFO(lp);
+ if (unlikely(packet_number & RXFIFO_REMPTY)) {
+ PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name);
+ return;
+ }
+
+ /* read from start of packet */
+ SMC_SET_PTR(lp, PTR_READ | PTR_RCV | PTR_AUTOINC);
+
+ /* First two words are status and packet length */
+ SMC_GET_PKT_HDR(lp, status, packet_len);
+ packet_len &= 0x07ff; /* mask off top bits */
+ DBG(2, "%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n",
+ dev->name, packet_number, status,
+ packet_len, packet_len);
+
+ back:
+ if (unlikely(packet_len < 6 || status & RS_ERRORS)) {
+ if (status & RS_TOOLONG && packet_len <= (1514 + 4 + 6)) {
+ /* accept VLAN packets */
+ status &= ~RS_TOOLONG;
+ goto back;
+ }
+ if (packet_len < 6) {
+ /* bloody hardware */
+ printk(KERN_ERR "%s: fubar (rxlen %u status %x\n",
+ dev->name, packet_len, status);
+ status |= RS_TOOSHORT;
+ }
+ SMC_WAIT_MMU_BUSY(lp);
+ SMC_SET_MMU_CMD(lp, MC_RELEASE);
+ dev->stats.rx_errors++;
+ if (status & RS_ALGNERR)
+ dev->stats.rx_frame_errors++;
+ if (status & (RS_TOOSHORT | RS_TOOLONG))
+ dev->stats.rx_length_errors++;
+ if (status & RS_BADCRC)
+ dev->stats.rx_crc_errors++;
+ } else {
+ struct sk_buff *skb;
+ unsigned char *data;
+ unsigned int data_len;
+
+ /* set multicast stats */
+ if (status & RS_MULTICAST)
+ dev->stats.multicast++;
+
+ /*
+ * Actual payload is packet_len - 6 (or 5 if odd byte).
+ * We want skb_reserve(2) and the final ctrl word
+ * (2 bytes, possibly containing the payload odd byte).
+ * Furthermore, we add 2 bytes to allow rounding up to
+ * multiple of 4 bytes on 32 bit buses.
+ * Hence packet_len - 6 + 2 + 2 + 2.
+ */
+ skb = dev_alloc_skb(packet_len);
+ if (unlikely(skb == NULL)) {
+ printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
+ dev->name);
+ SMC_WAIT_MMU_BUSY(lp);
+ SMC_SET_MMU_CMD(lp, MC_RELEASE);
+ dev->stats.rx_dropped++;
+ return;
+ }
+
+ /* Align IP header to 32 bits */
+ skb_reserve(skb, 2);
+
+ /* BUG: the LAN91C111 rev A never sets this bit. Force it. */
+ if (lp->version == 0x90)
+ status |= RS_ODDFRAME;
+
+ /*
+ * If odd length: packet_len - 5,
+ * otherwise packet_len - 6.
+ * With the trailing ctrl byte it's packet_len - 4.
+ */
+ data_len = packet_len - ((status & RS_ODDFRAME) ? 5 : 6);
+ data = skb_put(skb, data_len);
+ SMC_PULL_DATA(lp, data, packet_len - 4);
+
+ SMC_WAIT_MMU_BUSY(lp);
+ SMC_SET_MMU_CMD(lp, MC_RELEASE);
+
+ PRINT_PKT(data, packet_len - 4);
+
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += data_len;
+ }
+}
+
+#ifdef CONFIG_SMP
+/*
+ * On SMP we have the following problem:
+ *
+ * A = smc_hardware_send_pkt()
+ * B = smc_hard_start_xmit()
+ * C = smc_interrupt()
+ *
+ * A and B can never be executed simultaneously. However, at least on UP,
+ * it is possible (and even desirable) for C to interrupt execution of
+ * A or B in order to have better RX reliability and avoid overruns.
+ * C, just like A and B, must have exclusive access to the chip and
+ * each of them must lock against any other concurrent access.
+ * Unfortunately this is not possible to have C suspend execution of A or
+ * B taking place on another CPU. On UP this is no an issue since A and B
+ * are run from softirq context and C from hard IRQ context, and there is
+ * no other CPU where concurrent access can happen.
+ * If ever there is a way to force at least B and C to always be executed
+ * on the same CPU then we could use read/write locks to protect against
+ * any other concurrent access and C would always interrupt B. But life
+ * isn't that easy in a SMP world...
+ */
+#define smc_special_trylock(lock, flags) \
+({ \
+ int __ret; \
+ local_irq_save(flags); \
+ __ret = spin_trylock(lock); \
+ if (!__ret) \
+ local_irq_restore(flags); \
+ __ret; \
+})
+#define smc_special_lock(lock, flags) spin_lock_irqsave(lock, flags)
+#define smc_special_unlock(lock, flags) spin_unlock_irqrestore(lock, flags)
+#else
+#define smc_special_trylock(lock, flags) (flags == flags)
+#define smc_special_lock(lock, flags) do { flags = 0; } while (0)
+#define smc_special_unlock(lock, flags) do { flags = 0; } while (0)
+#endif
+
+/*
+ * This is called to actually send a packet to the chip.
+ */
+static void smc_hardware_send_pkt(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *)data;
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ struct sk_buff *skb;
+ unsigned int packet_no, len;
+ unsigned char *buf;
+ unsigned long flags;
+
+ DBG(3, "%s: %s\n", dev->name, __func__);
+
+ if (!smc_special_trylock(&lp->lock, flags)) {
+ netif_stop_queue(dev);
+ tasklet_schedule(&lp->tx_task);
+ return;
+ }
+
+ skb = lp->pending_tx_skb;
+ if (unlikely(!skb)) {
+ smc_special_unlock(&lp->lock, flags);
+ return;
+ }
+ lp->pending_tx_skb = NULL;
+
+ packet_no = SMC_GET_AR(lp);
+ if (unlikely(packet_no & AR_FAILED)) {
+ printk("%s: Memory allocation failed.\n", dev->name);
+ dev->stats.tx_errors++;
+ dev->stats.tx_fifo_errors++;
+ smc_special_unlock(&lp->lock, flags);
+ goto done;
+ }
+
+ /* point to the beginning of the packet */
+ SMC_SET_PN(lp, packet_no);
+ SMC_SET_PTR(lp, PTR_AUTOINC);
+
+ buf = skb->data;
+ len = skb->len;
+ DBG(2, "%s: TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p\n",
+ dev->name, packet_no, len, len, buf);
+ PRINT_PKT(buf, len);
+
+ /*
+ * Send the packet length (+6 for status words, length, and ctl.
+ * The card will pad to 64 bytes with zeroes if packet is too small.
+ */
+ SMC_PUT_PKT_HDR(lp, 0, len + 6);
+
+ /* send the actual data */
+ SMC_PUSH_DATA(lp, buf, len & ~1);
+
+ /* Send final ctl word with the last byte if there is one */
+ SMC_outw(((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG(lp));
+
+ /*
+ * If THROTTLE_TX_PKTS is set, we stop the queue here. This will
+ * have the effect of having at most one packet queued for TX
+ * in the chip's memory at all time.
+ *
+ * If THROTTLE_TX_PKTS is not set then the queue is stopped only
+ * when memory allocation (MC_ALLOC) does not succeed right away.
+ */
+ if (THROTTLE_TX_PKTS)
+ netif_stop_queue(dev);
+
+ /* queue the packet for TX */
+ SMC_SET_MMU_CMD(lp, MC_ENQUEUE);
+ smc_special_unlock(&lp->lock, flags);
+
+ dev->trans_start = jiffies;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += len;
+
+ SMC_ENABLE_INT(lp, IM_TX_INT | IM_TX_EMPTY_INT);
+
+done: if (!THROTTLE_TX_PKTS)
+ netif_wake_queue(dev);
+
+ dev_kfree_skb(skb);
+}
+
+/*
+ * Since I am not sure if I will have enough room in the chip's ram
+ * to store the packet, I call this routine which either sends it
+ * now, or set the card to generates an interrupt when ready
+ * for the packet.
+ */
+static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int numPages, poll_count, status;
+ unsigned long flags;
+
+ DBG(3, "%s: %s\n", dev->name, __func__);
+
+ BUG_ON(lp->pending_tx_skb != NULL);
+
+ /*
+ * The MMU wants the number of pages to be the number of 256 bytes
+ * 'pages', minus 1 (since a packet can't ever have 0 pages :))
+ *
+ * The 91C111 ignores the size bits, but earlier models don't.
+ *
+ * Pkt size for allocating is data length +6 (for additional status
+ * words, length and ctl)
+ *
+ * If odd size then last byte is included in ctl word.
+ */
+ numPages = ((skb->len & ~1) + (6 - 1)) >> 8;
+ if (unlikely(numPages > 7)) {
+ printk("%s: Far too big packet error.\n", dev->name);
+ dev->stats.tx_errors++;
+ dev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ smc_special_lock(&lp->lock, flags);
+
+ /* now, try to allocate the memory */
+ SMC_SET_MMU_CMD(lp, MC_ALLOC | numPages);
+
+ /*
+ * Poll the chip for a short amount of time in case the
+ * allocation succeeds quickly.
+ */
+ poll_count = MEMORY_WAIT_TIME;
+ do {
+ status = SMC_GET_INT(lp);
+ if (status & IM_ALLOC_INT) {
+ SMC_ACK_INT(lp, IM_ALLOC_INT);
+ break;
+ }
+ } while (--poll_count);
+
+ smc_special_unlock(&lp->lock, flags);
+
+ lp->pending_tx_skb = skb;
+ if (!poll_count) {
+ /* oh well, wait until the chip finds memory later */
+ netif_stop_queue(dev);
+ DBG(2, "%s: TX memory allocation deferred.\n", dev->name);
+ SMC_ENABLE_INT(lp, IM_ALLOC_INT);
+ } else {
+ /*
+ * Allocation succeeded: push packet to the chip's own memory
+ * immediately.
+ */
+ smc_hardware_send_pkt((unsigned long)dev);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/*
+ * This handles a TX interrupt, which is only called when:
+ * - a TX error occurred, or
+ * - CTL_AUTO_RELEASE is not set and TX of a packet completed.
+ */
+static void smc_tx(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int saved_packet, packet_no, tx_status, pkt_len;
+
+ DBG(3, "%s: %s\n", dev->name, __func__);
+
+ /* If the TX FIFO is empty then nothing to do */
+ packet_no = SMC_GET_TXFIFO(lp);
+ if (unlikely(packet_no & TXFIFO_TEMPTY)) {
+ PRINTK("%s: smc_tx with nothing on FIFO.\n", dev->name);
+ return;
+ }
+
+ /* select packet to read from */
+ saved_packet = SMC_GET_PN(lp);
+ SMC_SET_PN(lp, packet_no);
+
+ /* read the first word (status word) from this packet */
+ SMC_SET_PTR(lp, PTR_AUTOINC | PTR_READ);
+ SMC_GET_PKT_HDR(lp, tx_status, pkt_len);
+ DBG(2, "%s: TX STATUS 0x%04x PNR 0x%02x\n",
+ dev->name, tx_status, packet_no);
+
+ if (!(tx_status & ES_TX_SUC))
+ dev->stats.tx_errors++;
+
+ if (tx_status & ES_LOSTCARR)
+ dev->stats.tx_carrier_errors++;
+
+ if (tx_status & (ES_LATCOL | ES_16COL)) {
+ PRINTK("%s: %s occurred on last xmit\n", dev->name,
+ (tx_status & ES_LATCOL) ?
+ "late collision" : "too many collisions");
+ dev->stats.tx_window_errors++;
+ if (!(dev->stats.tx_window_errors & 63) && net_ratelimit()) {
+ printk(KERN_INFO "%s: unexpectedly large number of "
+ "bad collisions. Please check duplex "
+ "setting.\n", dev->name);
+ }
+ }
+
+ /* kill the packet */
+ SMC_WAIT_MMU_BUSY(lp);
+ SMC_SET_MMU_CMD(lp, MC_FREEPKT);
+
+ /* Don't restore Packet Number Reg until busy bit is cleared */
+ SMC_WAIT_MMU_BUSY(lp);
+ SMC_SET_PN(lp, saved_packet);
+
+ /* re-enable transmit */
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_TCR(lp, lp->tcr_cur_mode);
+ SMC_SELECT_BANK(lp, 2);
+}
+
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+
+static void smc_mii_out(struct net_device *dev, unsigned int val, int bits)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int mii_reg, mask;
+
+ mii_reg = SMC_GET_MII(lp) & ~(MII_MCLK | MII_MDOE | MII_MDO);
+ mii_reg |= MII_MDOE;
+
+ for (mask = 1 << (bits - 1); mask; mask >>= 1) {
+ if (val & mask)
+ mii_reg |= MII_MDO;
+ else
+ mii_reg &= ~MII_MDO;
+
+ SMC_SET_MII(lp, mii_reg);
+ udelay(MII_DELAY);
+ SMC_SET_MII(lp, mii_reg | MII_MCLK);
+ udelay(MII_DELAY);
+ }
+}
+
+static unsigned int smc_mii_in(struct net_device *dev, int bits)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int mii_reg, mask, val;
+
+ mii_reg = SMC_GET_MII(lp) & ~(MII_MCLK | MII_MDOE | MII_MDO);
+ SMC_SET_MII(lp, mii_reg);
+
+ for (mask = 1 << (bits - 1), val = 0; mask; mask >>= 1) {
+ if (SMC_GET_MII(lp) & MII_MDI)
+ val |= mask;
+
+ SMC_SET_MII(lp, mii_reg);
+ udelay(MII_DELAY);
+ SMC_SET_MII(lp, mii_reg | MII_MCLK);
+ udelay(MII_DELAY);
+ }
+
+ return val;
+}
+
+/*
+ * Reads a register from the MII Management serial interface
+ */
+static int smc_phy_read(struct net_device *dev, int phyaddr, int phyreg)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int phydata;
+
+ SMC_SELECT_BANK(lp, 3);
+
+ /* Idle - 32 ones */
+ smc_mii_out(dev, 0xffffffff, 32);
+
+ /* Start code (01) + read (10) + phyaddr + phyreg */
+ smc_mii_out(dev, 6 << 10 | phyaddr << 5 | phyreg, 14);
+
+ /* Turnaround (2bits) + phydata */
+ phydata = smc_mii_in(dev, 18);
+
+ /* Return to idle state */
+ SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO));
+
+ DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+ __func__, phyaddr, phyreg, phydata);
+
+ SMC_SELECT_BANK(lp, 2);
+ return phydata;
+}
+
+/*
+ * Writes a register to the MII Management serial interface
+ */
+static void smc_phy_write(struct net_device *dev, int phyaddr, int phyreg,
+ int phydata)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+
+ SMC_SELECT_BANK(lp, 3);
+
+ /* Idle - 32 ones */
+ smc_mii_out(dev, 0xffffffff, 32);
+
+ /* Start code (01) + write (01) + phyaddr + phyreg + turnaround + phydata */
+ smc_mii_out(dev, 5 << 28 | phyaddr << 23 | phyreg << 18 | 2 << 16 | phydata, 32);
+
+ /* Return to idle state */
+ SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO));
+
+ DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n",
+ __func__, phyaddr, phyreg, phydata);
+
+ SMC_SELECT_BANK(lp, 2);
+}
+
+/*
+ * Finds and reports the PHY address
+ */
+static void smc_phy_detect(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int phyaddr;
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ lp->phy_type = 0;
+
+ /*
+ * Scan all 32 PHY addresses if necessary, starting at
+ * PHY#1 to PHY#31, and then PHY#0 last.
+ */
+ for (phyaddr = 1; phyaddr < 33; ++phyaddr) {
+ unsigned int id1, id2;
+
+ /* Read the PHY identifiers */
+ id1 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID1);
+ id2 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID2);
+
+ DBG(3, "%s: phy_id1=0x%x, phy_id2=0x%x\n",
+ dev->name, id1, id2);
+
+ /* Make sure it is a valid identifier */
+ if (id1 != 0x0000 && id1 != 0xffff && id1 != 0x8000 &&
+ id2 != 0x0000 && id2 != 0xffff && id2 != 0x8000) {
+ /* Save the PHY's address */
+ lp->mii.phy_id = phyaddr & 31;
+ lp->phy_type = id1 << 16 | id2;
+ break;
+ }
+ }
+}
+
+/*
+ * Sets the PHY to a configuration as determined by the user
+ */
+static int smc_phy_fixed(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ int phyaddr = lp->mii.phy_id;
+ int bmcr, cfg1;
+
+ DBG(3, "%s: %s\n", dev->name, __func__);
+
+ /* Enter Link Disable state */
+ cfg1 = smc_phy_read(dev, phyaddr, PHY_CFG1_REG);
+ cfg1 |= PHY_CFG1_LNKDIS;
+ smc_phy_write(dev, phyaddr, PHY_CFG1_REG, cfg1);
+
+ /*
+ * Set our fixed capabilities
+ * Disable auto-negotiation
+ */
+ bmcr = 0;
+
+ if (lp->ctl_rfduplx)
+ bmcr |= BMCR_FULLDPLX;
+
+ if (lp->ctl_rspeed == 100)
+ bmcr |= BMCR_SPEED100;
+
+ /* Write our capabilities to the phy control register */
+ smc_phy_write(dev, phyaddr, MII_BMCR, bmcr);
+
+ /* Re-Configure the Receive/Phy Control register */
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_RPC(lp, lp->rpc_cur_mode);
+ SMC_SELECT_BANK(lp, 2);
+
+ return 1;
+}
+
+/*
+ * smc_phy_reset - reset the phy
+ * @dev: net device
+ * @phy: phy address
+ *
+ * Issue a software reset for the specified PHY and
+ * wait up to 100ms for the reset to complete. We should
+ * not access the PHY for 50ms after issuing the reset.
+ *
+ * The time to wait appears to be dependent on the PHY.
+ *
+ * Must be called with lp->lock locked.
+ */
+static int smc_phy_reset(struct net_device *dev, int phy)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned int bmcr;
+ int timeout;
+
+ smc_phy_write(dev, phy, MII_BMCR, BMCR_RESET);
+
+ for (timeout = 2; timeout; timeout--) {
+ spin_unlock_irq(&lp->lock);
+ msleep(50);
+ spin_lock_irq(&lp->lock);
+
+ bmcr = smc_phy_read(dev, phy, MII_BMCR);
+ if (!(bmcr & BMCR_RESET))
+ break;
+ }
+
+ return bmcr & BMCR_RESET;
+}
+
+/*
+ * smc_phy_powerdown - powerdown phy
+ * @dev: net device
+ *
+ * Power down the specified PHY
+ */
+static void smc_phy_powerdown(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ unsigned int bmcr;
+ int phy = lp->mii.phy_id;
+
+ if (lp->phy_type == 0)
+ return;
+
+ /* We need to ensure that no calls to smc_phy_configure are
+ pending.
+ */
+ cancel_work_sync(&lp->phy_configure);
+
+ bmcr = smc_phy_read(dev, phy, MII_BMCR);
+ smc_phy_write(dev, phy, MII_BMCR, bmcr | BMCR_PDOWN);
+}
+
+/*
+ * smc_phy_check_media - check the media status and adjust TCR
+ * @dev: net device
+ * @init: set true for initialisation
+ *
+ * Select duplex mode depending on negotiation state. This
+ * also updates our carrier state.
+ */
+static void smc_phy_check_media(struct net_device *dev, int init)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+
+ if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) {
+ /* duplex state has changed */
+ if (lp->mii.full_duplex) {
+ lp->tcr_cur_mode |= TCR_SWFDUP;
+ } else {
+ lp->tcr_cur_mode &= ~TCR_SWFDUP;
+ }
+
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_TCR(lp, lp->tcr_cur_mode);
+ }
+}
+
+/*
+ * Configures the specified PHY through the MII management interface
+ * using Autonegotiation.
+ * Calls smc_phy_fixed() if the user has requested a certain config.
+ * If RPC ANEG bit is set, the media selection is dependent purely on
+ * the selection by the MII (either in the MII BMCR reg or the result
+ * of autonegotiation.) If the RPC ANEG bit is cleared, the selection
+ * is controlled by the RPC SPEED and RPC DPLX bits.
+ */
+static void smc_phy_configure(struct work_struct *work)
+{
+ struct smc_local *lp =
+ container_of(work, struct smc_local, phy_configure);
+ struct net_device *dev = lp->dev;
+ void __iomem *ioaddr = lp->base;
+ int phyaddr = lp->mii.phy_id;
+ int my_phy_caps; /* My PHY capabilities */
+ int my_ad_caps; /* My Advertised capabilities */
+ int status;
+
+ DBG(3, "%s:smc_program_phy()\n", dev->name);
+
+ spin_lock_irq(&lp->lock);
+
+ /*
+ * We should not be called if phy_type is zero.
+ */
+ if (lp->phy_type == 0)
+ goto smc_phy_configure_exit;
+
+ if (smc_phy_reset(dev, phyaddr)) {
+ printk("%s: PHY reset timed out\n", dev->name);
+ goto smc_phy_configure_exit;
+ }
+
+ /*
+ * Enable PHY Interrupts (for register 18)
+ * Interrupts listed here are disabled
+ */
+ smc_phy_write(dev, phyaddr, PHY_MASK_REG,
+ PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
+ PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+ PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+ /* Configure the Receive/Phy Control register */
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_RPC(lp, lp->rpc_cur_mode);
+
+ /* If the user requested no auto neg, then go set his request */
+ if (lp->mii.force_media) {
+ smc_phy_fixed(dev);
+ goto smc_phy_configure_exit;
+ }
+
+ /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */
+ my_phy_caps = smc_phy_read(dev, phyaddr, MII_BMSR);
+
+ if (!(my_phy_caps & BMSR_ANEGCAPABLE)) {
+ printk(KERN_INFO "Auto negotiation NOT supported\n");
+ smc_phy_fixed(dev);
+ goto smc_phy_configure_exit;
+ }
+
+ my_ad_caps = ADVERTISE_CSMA; /* I am CSMA capable */
+
+ if (my_phy_caps & BMSR_100BASE4)
+ my_ad_caps |= ADVERTISE_100BASE4;
+ if (my_phy_caps & BMSR_100FULL)
+ my_ad_caps |= ADVERTISE_100FULL;
+ if (my_phy_caps & BMSR_100HALF)
+ my_ad_caps |= ADVERTISE_100HALF;
+ if (my_phy_caps & BMSR_10FULL)
+ my_ad_caps |= ADVERTISE_10FULL;
+ if (my_phy_caps & BMSR_10HALF)
+ my_ad_caps |= ADVERTISE_10HALF;
+
+ /* Disable capabilities not selected by our user */
+ if (lp->ctl_rspeed != 100)
+ my_ad_caps &= ~(ADVERTISE_100BASE4|ADVERTISE_100FULL|ADVERTISE_100HALF);
+
+ if (!lp->ctl_rfduplx)
+ my_ad_caps &= ~(ADVERTISE_100FULL|ADVERTISE_10FULL);
+
+ /* Update our Auto-Neg Advertisement Register */
+ smc_phy_write(dev, phyaddr, MII_ADVERTISE, my_ad_caps);
+ lp->mii.advertising = my_ad_caps;
+
+ /*
+ * Read the register back. Without this, it appears that when
+ * auto-negotiation is restarted, sometimes it isn't ready and
+ * the link does not come up.
+ */
+ status = smc_phy_read(dev, phyaddr, MII_ADVERTISE);
+
+ DBG(2, "%s: phy caps=%x\n", dev->name, my_phy_caps);
+ DBG(2, "%s: phy advertised caps=%x\n", dev->name, my_ad_caps);
+
+ /* Restart auto-negotiation process in order to advertise my caps */
+ smc_phy_write(dev, phyaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+
+ smc_phy_check_media(dev, 1);
+
+smc_phy_configure_exit:
+ SMC_SELECT_BANK(lp, 2);
+ spin_unlock_irq(&lp->lock);
+}
+
+/*
+ * smc_phy_interrupt
+ *
+ * Purpose: Handle interrupts relating to PHY register 18. This is
+ * called from the "hard" interrupt handler under our private spinlock.
+ */
+static void smc_phy_interrupt(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int phyaddr = lp->mii.phy_id;
+ int phy18;
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ if (lp->phy_type == 0)
+ return;
+
+ for(;;) {
+ smc_phy_check_media(dev, 0);
+
+ /* Read PHY Register 18, Status Output */
+ phy18 = smc_phy_read(dev, phyaddr, PHY_INT_REG);
+ if ((phy18 & PHY_INT_INT) == 0)
+ break;
+ }
+}
+
+/*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/
+
+static void smc_10bt_check_media(struct net_device *dev, int init)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int old_carrier, new_carrier;
+
+ old_carrier = netif_carrier_ok(dev) ? 1 : 0;
+
+ SMC_SELECT_BANK(lp, 0);
+ new_carrier = (SMC_GET_EPH_STATUS(lp) & ES_LINK_OK) ? 1 : 0;
+ SMC_SELECT_BANK(lp, 2);
+
+ if (init || (old_carrier != new_carrier)) {
+ if (!new_carrier) {
+ netif_carrier_off(dev);
+ } else {
+ netif_carrier_on(dev);
+ }
+ if (netif_msg_link(lp))
+ printk(KERN_INFO "%s: link %s\n", dev->name,
+ new_carrier ? "up" : "down");
+ }
+}
+
+static void smc_eph_interrupt(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned int ctl;
+
+ smc_10bt_check_media(dev, 0);
+
+ SMC_SELECT_BANK(lp, 1);
+ ctl = SMC_GET_CTL(lp);
+ SMC_SET_CTL(lp, ctl & ~CTL_LE_ENABLE);
+ SMC_SET_CTL(lp, ctl);
+ SMC_SELECT_BANK(lp, 2);
+}
+
+/*
+ * This is the main routine of the driver, to handle the device when
+ * it needs some attention.
+ */
+static irqreturn_t smc_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ int status, mask, timeout, card_stats;
+ int saved_pointer;
+
+ DBG(3, "%s: %s\n", dev->name, __func__);
+
+ spin_lock(&lp->lock);
+
+ /* A preamble may be used when there is a potential race
+ * between the interruptible transmit functions and this
+ * ISR. */
+ SMC_INTERRUPT_PREAMBLE;
+
+ saved_pointer = SMC_GET_PTR(lp);
+ mask = SMC_GET_INT_MASK(lp);
+ SMC_SET_INT_MASK(lp, 0);
+
+ /* set a timeout value, so I don't stay here forever */
+ timeout = MAX_IRQ_LOOPS;
+
+ do {
+ status = SMC_GET_INT(lp);
+
+ DBG(2, "%s: INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n",
+ dev->name, status, mask,
+ ({ int meminfo; SMC_SELECT_BANK(lp, 0);
+ meminfo = SMC_GET_MIR(lp);
+ SMC_SELECT_BANK(lp, 2); meminfo; }),
+ SMC_GET_FIFO(lp));
+
+ status &= mask;
+ if (!status)
+ break;
+
+ if (status & IM_TX_INT) {
+ /* do this before RX as it will free memory quickly */
+ DBG(3, "%s: TX int\n", dev->name);
+ smc_tx(dev);
+ SMC_ACK_INT(lp, IM_TX_INT);
+ if (THROTTLE_TX_PKTS)
+ netif_wake_queue(dev);
+ } else if (status & IM_RCV_INT) {
+ DBG(3, "%s: RX irq\n", dev->name);
+ smc_rcv(dev);
+ } else if (status & IM_ALLOC_INT) {
+ DBG(3, "%s: Allocation irq\n", dev->name);
+ tasklet_hi_schedule(&lp->tx_task);
+ mask &= ~IM_ALLOC_INT;
+ } else if (status & IM_TX_EMPTY_INT) {
+ DBG(3, "%s: TX empty\n", dev->name);
+ mask &= ~IM_TX_EMPTY_INT;
+
+ /* update stats */
+ SMC_SELECT_BANK(lp, 0);
+ card_stats = SMC_GET_COUNTER(lp);
+ SMC_SELECT_BANK(lp, 2);
+
+ /* single collisions */
+ dev->stats.collisions += card_stats & 0xF;
+ card_stats >>= 4;
+
+ /* multiple collisions */
+ dev->stats.collisions += card_stats & 0xF;
+ } else if (status & IM_RX_OVRN_INT) {
+ DBG(1, "%s: RX overrun (EPH_ST 0x%04x)\n", dev->name,
+ ({ int eph_st; SMC_SELECT_BANK(lp, 0);
+ eph_st = SMC_GET_EPH_STATUS(lp);
+ SMC_SELECT_BANK(lp, 2); eph_st; }));
+ SMC_ACK_INT(lp, IM_RX_OVRN_INT);
+ dev->stats.rx_errors++;
+ dev->stats.rx_fifo_errors++;
+ } else if (status & IM_EPH_INT) {
+ smc_eph_interrupt(dev);
+ } else if (status & IM_MDINT) {
+ SMC_ACK_INT(lp, IM_MDINT);
+ smc_phy_interrupt(dev);
+ } else if (status & IM_ERCV_INT) {
+ SMC_ACK_INT(lp, IM_ERCV_INT);
+ PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT\n", dev->name);
+ }
+ } while (--timeout);
+
+ /* restore register states */
+ SMC_SET_PTR(lp, saved_pointer);
+ SMC_SET_INT_MASK(lp, mask);
+ spin_unlock(&lp->lock);
+
+#ifndef CONFIG_NET_POLL_CONTROLLER
+ if (timeout == MAX_IRQ_LOOPS)
+ PRINTK("%s: spurious interrupt (mask = 0x%02x)\n",
+ dev->name, mask);
+#endif
+ DBG(3, "%s: Interrupt done (%d loops)\n",
+ dev->name, MAX_IRQ_LOOPS - timeout);
+
+ /*
+ * We return IRQ_HANDLED unconditionally here even if there was
+ * nothing to do. There is a possibility that a packet might
+ * get enqueued into the chip right after TX_EMPTY_INT is raised
+ * but just before the CPU acknowledges the IRQ.
+ * Better take an unneeded IRQ in some occasions than complexifying
+ * the code for all cases.
+ */
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling receive - used by netconsole and other diagnostic tools
+ * to allow network i/o with interrupts disabled.
+ */
+static void smc_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smc_interrupt(dev->irq, dev);
+ enable_irq(dev->irq);
+}
+#endif
+
+/* Our watchdog timed out. Called by the networking layer */
+static void smc_timeout(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ int status, mask, eph_st, meminfo, fifo;
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ spin_lock_irq(&lp->lock);
+ status = SMC_GET_INT(lp);
+ mask = SMC_GET_INT_MASK(lp);
+ fifo = SMC_GET_FIFO(lp);
+ SMC_SELECT_BANK(lp, 0);
+ eph_st = SMC_GET_EPH_STATUS(lp);
+ meminfo = SMC_GET_MIR(lp);
+ SMC_SELECT_BANK(lp, 2);
+ spin_unlock_irq(&lp->lock);
+ PRINTK( "%s: TX timeout (INT 0x%02x INTMASK 0x%02x "
+ "MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n",
+ dev->name, status, mask, meminfo, fifo, eph_st );
+
+ smc_reset(dev);
+ smc_enable(dev);
+
+ /*
+ * Reconfiguring the PHY doesn't seem like a bad idea here, but
+ * smc_phy_configure() calls msleep() which calls schedule_timeout()
+ * which calls schedule(). Hence we use a work queue.
+ */
+ if (lp->phy_type != 0)
+ schedule_work(&lp->phy_configure);
+
+ /* We can accept TX packets again */
+ dev->trans_start = jiffies; /* prevent tx timeout */
+ netif_wake_queue(dev);
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void smc_set_multicast_list(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+ unsigned char multicast_table[8];
+ int update_multicast = 0;
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ if (dev->flags & IFF_PROMISC) {
+ DBG(2, "%s: RCR_PRMS\n", dev->name);
+ lp->rcr_cur_mode |= RCR_PRMS;
+ }
+
+/* BUG? I never disable promiscuous mode if multicasting was turned on.
+ Now, I turn off promiscuous mode, but I don't do anything to multicasting
+ when promiscuous mode is turned on.
+*/
+
+ /*
+ * Here, I am setting this to accept all multicast packets.
+ * I don't need to zero the multicast table, because the flag is
+ * checked before the table is
+ */
+ else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) {
+ DBG(2, "%s: RCR_ALMUL\n", dev->name);
+ lp->rcr_cur_mode |= RCR_ALMUL;
+ }
+
+ /*
+ * This sets the internal hardware table to filter out unwanted
+ * multicast packets before they take up memory.
+ *
+ * The SMC chip uses a hash table where the high 6 bits of the CRC of
+ * address are the offset into the table. If that bit is 1, then the
+ * multicast packet is accepted. Otherwise, it's dropped silently.
+ *
+ * To use the 6 bits as an offset into the table, the high 3 bits are
+ * the number of the 8 bit register, while the low 3 bits are the bit
+ * within that register.
+ */
+ else if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+
+ /* table for flipping the order of 3 bits */
+ static const unsigned char invert3[] = {0, 4, 2, 6, 1, 5, 3, 7};
+
+ /* start with a table of all zeros: reject all */
+ memset(multicast_table, 0, sizeof(multicast_table));
+
+ netdev_for_each_mc_addr(ha, dev) {
+ int position;
+
+ /* only use the low order bits */
+ position = crc32_le(~0, ha->addr, 6) & 0x3f;
+
+ /* do some messy swapping to put the bit in the right spot */
+ multicast_table[invert3[position&7]] |=
+ (1<<invert3[(position>>3)&7]);
+ }
+
+ /* be sure I get rid of flags I might have set */
+ lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
+
+ /* now, the table can be loaded into the chipset */
+ update_multicast = 1;
+ } else {
+ DBG(2, "%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name);
+ lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL);
+
+ /*
+ * since I'm disabling all multicast entirely, I need to
+ * clear the multicast list
+ */
+ memset(multicast_table, 0, sizeof(multicast_table));
+ update_multicast = 1;
+ }
+
+ spin_lock_irq(&lp->lock);
+ SMC_SELECT_BANK(lp, 0);
+ SMC_SET_RCR(lp, lp->rcr_cur_mode);
+ if (update_multicast) {
+ SMC_SELECT_BANK(lp, 3);
+ SMC_SET_MCAST(lp, multicast_table);
+ }
+ SMC_SELECT_BANK(lp, 2);
+ spin_unlock_irq(&lp->lock);
+}
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int
+smc_open(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ /*
+ * Check that the address is valid. If its not, refuse
+ * to bring the device up. The user must specify an
+ * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+ */
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ PRINTK("%s: no valid ethernet hw addr\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Setup the default Register Modes */
+ lp->tcr_cur_mode = TCR_DEFAULT;
+ lp->rcr_cur_mode = RCR_DEFAULT;
+ lp->rpc_cur_mode = RPC_DEFAULT |
+ lp->cfg.leda << RPC_LSXA_SHFT |
+ lp->cfg.ledb << RPC_LSXB_SHFT;
+
+ /*
+ * If we are not using a MII interface, we need to
+ * monitor our own carrier signal to detect faults.
+ */
+ if (lp->phy_type == 0)
+ lp->tcr_cur_mode |= TCR_MON_CSN;
+
+ /* reset the hardware */
+ smc_reset(dev);
+ smc_enable(dev);
+
+ /* Configure the PHY, initialize the link state */
+ if (lp->phy_type != 0)
+ smc_phy_configure(&lp->phy_configure);
+ else {
+ spin_lock_irq(&lp->lock);
+ smc_10bt_check_media(dev, 1);
+ spin_unlock_irq(&lp->lock);
+ }
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/*
+ * smc_close
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world. Caused by
+ * an 'ifconfig ethX down'
+ */
+static int smc_close(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+
+ DBG(2, "%s: %s\n", dev->name, __func__);
+
+ netif_stop_queue(dev);
+ netif_carrier_off(dev);
+
+ /* clear everything */
+ smc_shutdown(dev);
+ tasklet_kill(&lp->tx_task);
+ smc_phy_powerdown(dev);
+ return 0;
+}
+
+/*
+ * Ethtool support
+ */
+static int
+smc_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int ret;
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irq(&lp->lock);
+ ret = mii_ethtool_gset(&lp->mii, cmd);
+ spin_unlock_irq(&lp->lock);
+ } else {
+ cmd->supported = SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_TP | SUPPORTED_AUI;
+
+ if (lp->ctl_rspeed == 10)
+ ethtool_cmd_speed_set(cmd, SPEED_10);
+ else if (lp->ctl_rspeed == 100)
+ ethtool_cmd_speed_set(cmd, SPEED_100);
+
+ cmd->autoneg = AUTONEG_DISABLE;
+ cmd->transceiver = XCVR_INTERNAL;
+ cmd->port = 0;
+ cmd->duplex = lp->tcr_cur_mode & TCR_SWFDUP ? DUPLEX_FULL : DUPLEX_HALF;
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int
+smc_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int ret;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irq(&lp->lock);
+ ret = mii_ethtool_sset(&lp->mii, cmd);
+ spin_unlock_irq(&lp->lock);
+ } else {
+ if (cmd->autoneg != AUTONEG_DISABLE ||
+ cmd->speed != SPEED_10 ||
+ (cmd->duplex != DUPLEX_HALF && cmd->duplex != DUPLEX_FULL) ||
+ (cmd->port != PORT_TP && cmd->port != PORT_AUI))
+ return -EINVAL;
+
+// lp->port = cmd->port;
+ lp->ctl_rfduplx = cmd->duplex == DUPLEX_FULL;
+
+// if (netif_running(dev))
+// smc_set_port(dev);
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void
+smc_ethtool_getdrvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ strncpy(info->driver, CARDNAME, sizeof(info->driver));
+ strncpy(info->version, version, sizeof(info->version));
+ strncpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static int smc_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ int ret = -EINVAL;
+
+ if (lp->phy_type != 0) {
+ spin_lock_irq(&lp->lock);
+ ret = mii_nway_restart(&lp->mii);
+ spin_unlock_irq(&lp->lock);
+ }
+
+ return ret;
+}
+
+static u32 smc_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ return lp->msg_enable;
+}
+
+static void smc_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ lp->msg_enable = level;
+}
+
+static int smc_write_eeprom_word(struct net_device *dev, u16 addr, u16 word)
+{
+ u16 ctl;
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+
+ spin_lock_irq(&lp->lock);
+ /* load word into GP register */
+ SMC_SELECT_BANK(lp, 1);
+ SMC_SET_GP(lp, word);
+ /* set the address to put the data in EEPROM */
+ SMC_SELECT_BANK(lp, 2);
+ SMC_SET_PTR(lp, addr);
+ /* tell it to write */
+ SMC_SELECT_BANK(lp, 1);
+ ctl = SMC_GET_CTL(lp);
+ SMC_SET_CTL(lp, ctl | (CTL_EEPROM_SELECT | CTL_STORE));
+ /* wait for it to finish */
+ do {
+ udelay(1);
+ } while (SMC_GET_CTL(lp) & CTL_STORE);
+ /* clean up */
+ SMC_SET_CTL(lp, ctl);
+ SMC_SELECT_BANK(lp, 2);
+ spin_unlock_irq(&lp->lock);
+ return 0;
+}
+
+static int smc_read_eeprom_word(struct net_device *dev, u16 addr, u16 *word)
+{
+ u16 ctl;
+ struct smc_local *lp = netdev_priv(dev);
+ void __iomem *ioaddr = lp->base;
+
+ spin_lock_irq(&lp->lock);
+ /* set the EEPROM address to get the data from */
+ SMC_SELECT_BANK(lp, 2);
+ SMC_SET_PTR(lp, addr | PTR_READ);
+ /* tell it to load */
+ SMC_SELECT_BANK(lp, 1);
+ SMC_SET_GP(lp, 0xffff); /* init to known */
+ ctl = SMC_GET_CTL(lp);
+ SMC_SET_CTL(lp, ctl | (CTL_EEPROM_SELECT | CTL_RELOAD));
+ /* wait for it to finish */
+ do {
+ udelay(1);
+ } while (SMC_GET_CTL(lp) & CTL_RELOAD);
+ /* read word from GP register */
+ *word = SMC_GET_GP(lp);
+ /* clean up */
+ SMC_SET_CTL(lp, ctl);
+ SMC_SELECT_BANK(lp, 2);
+ spin_unlock_irq(&lp->lock);
+ return 0;
+}
+
+static int smc_ethtool_geteeprom_len(struct net_device *dev)
+{
+ return 0x23 * 2;
+}
+
+static int smc_ethtool_geteeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int i;
+ int imax;
+
+ DBG(1, "Reading %d bytes at %d(0x%x)\n",
+ eeprom->len, eeprom->offset, eeprom->offset);
+ imax = smc_ethtool_geteeprom_len(dev);
+ for (i = 0; i < eeprom->len; i += 2) {
+ int ret;
+ u16 wbuf;
+ int offset = i + eeprom->offset;
+ if (offset > imax)
+ break;
+ ret = smc_read_eeprom_word(dev, offset >> 1, &wbuf);
+ if (ret != 0)
+ return ret;
+ DBG(2, "Read 0x%x from 0x%x\n", wbuf, offset >> 1);
+ data[i] = (wbuf >> 8) & 0xff;
+ data[i+1] = wbuf & 0xff;
+ }
+ return 0;
+}
+
+static int smc_ethtool_seteeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int i;
+ int imax;
+
+ DBG(1, "Writing %d bytes to %d(0x%x)\n",
+ eeprom->len, eeprom->offset, eeprom->offset);
+ imax = smc_ethtool_geteeprom_len(dev);
+ for (i = 0; i < eeprom->len; i += 2) {
+ int ret;
+ u16 wbuf;
+ int offset = i + eeprom->offset;
+ if (offset > imax)
+ break;
+ wbuf = (data[i] << 8) | data[i + 1];
+ DBG(2, "Writing 0x%x to 0x%x\n", wbuf, offset >> 1);
+ ret = smc_write_eeprom_word(dev, offset >> 1, wbuf);
+ if (ret != 0)
+ return ret;
+ }
+ return 0;
+}
+
+
+static const struct ethtool_ops smc_ethtool_ops = {
+ .get_settings = smc_ethtool_getsettings,
+ .set_settings = smc_ethtool_setsettings,
+ .get_drvinfo = smc_ethtool_getdrvinfo,
+
+ .get_msglevel = smc_ethtool_getmsglevel,
+ .set_msglevel = smc_ethtool_setmsglevel,
+ .nway_reset = smc_ethtool_nwayreset,
+ .get_link = ethtool_op_get_link,
+ .get_eeprom_len = smc_ethtool_geteeprom_len,
+ .get_eeprom = smc_ethtool_geteeprom,
+ .set_eeprom = smc_ethtool_seteeprom,
+};
+
+static const struct net_device_ops smc_netdev_ops = {
+ .ndo_open = smc_open,
+ .ndo_stop = smc_close,
+ .ndo_start_xmit = smc_hard_start_xmit,
+ .ndo_tx_timeout = smc_timeout,
+ .ndo_set_multicast_list = smc_set_multicast_list,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = smc_poll_controller,
+#endif
+};
+
+/*
+ * smc_findirq
+ *
+ * This routine has a simple purpose -- make the SMC chip generate an
+ * interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ */
+/*
+ * does this still work?
+ *
+ * I just deleted auto_irq.c, since it was never built...
+ * --jgarzik
+ */
+static int __devinit smc_findirq(struct smc_local *lp)
+{
+ void __iomem *ioaddr = lp->base;
+ int timeout = 20;
+ unsigned long cookie;
+
+ DBG(2, "%s: %s\n", CARDNAME, __func__);
+
+ cookie = probe_irq_on();
+
+ /*
+ * What I try to do here is trigger an ALLOC_INT. This is done
+ * by allocating a small chunk of memory, which will give an interrupt
+ * when done.
+ */
+ /* enable ALLOCation interrupts ONLY */
+ SMC_SELECT_BANK(lp, 2);
+ SMC_SET_INT_MASK(lp, IM_ALLOC_INT);
+
+ /*
+ * Allocate 512 bytes of memory. Note that the chip was just
+ * reset so all the memory is available
+ */
+ SMC_SET_MMU_CMD(lp, MC_ALLOC | 1);
+
+ /*
+ * Wait until positive that the interrupt has been generated
+ */
+ do {
+ int int_status;
+ udelay(10);
+ int_status = SMC_GET_INT(lp);
+ if (int_status & IM_ALLOC_INT)
+ break; /* got the interrupt */
+ } while (--timeout);
+
+ /*
+ * there is really nothing that I can do here if timeout fails,
+ * as autoirq_report will return a 0 anyway, which is what I
+ * want in this case. Plus, the clean up is needed in both
+ * cases.
+ */
+
+ /* and disable all interrupts again */
+ SMC_SET_INT_MASK(lp, 0);
+
+ /* and return what I found */
+ return probe_irq_off(cookie);
+}
+
+/*
+ * Function: smc_probe(unsigned long ioaddr)
+ *
+ * Purpose:
+ * Tests to see if a given ioaddr points to an SMC91x chip.
+ * Returns a 0 on success
+ *
+ * Algorithm:
+ * (1) see if the high byte of BANK_SELECT is 0x33
+ * (2) compare the ioaddr with the base register's address
+ * (3) see if I recognize the chip ID in the appropriate register
+ *
+ * Here I do typical initialization tasks.
+ *
+ * o Initialize the structure if needed
+ * o print out my vanity message if not done so already
+ * o print out what type of hardware is detected
+ * o print out the ethernet address
+ * o find the IRQ
+ * o set up my private data
+ * o configure the dev structure with my subroutines
+ * o actually GRAB the irq.
+ * o GRAB the region
+ */
+static int __devinit smc_probe(struct net_device *dev, void __iomem *ioaddr,
+ unsigned long irq_flags)
+{
+ struct smc_local *lp = netdev_priv(dev);
+ static int version_printed = 0;
+ int retval;
+ unsigned int val, revision_register;
+ const char *version_string;
+
+ DBG(2, "%s: %s\n", CARDNAME, __func__);
+
+ /* First, see if the high byte is 0x33 */
+ val = SMC_CURRENT_BANK(lp);
+ DBG(2, "%s: bank signature probe returned 0x%04x\n", CARDNAME, val);
+ if ((val & 0xFF00) != 0x3300) {
+ if ((val & 0xFF) == 0x33) {
+ printk(KERN_WARNING
+ "%s: Detected possible byte-swapped interface"
+ " at IOADDR %p\n", CARDNAME, ioaddr);
+ }
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /*
+ * The above MIGHT indicate a device, but I need to write to
+ * further test this.
+ */
+ SMC_SELECT_BANK(lp, 0);
+ val = SMC_CURRENT_BANK(lp);
+ if ((val & 0xFF00) != 0x3300) {
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /*
+ * well, we've already written once, so hopefully another
+ * time won't hurt. This time, I need to switch the bank
+ * register to bank 1, so I can access the base address
+ * register
+ */
+ SMC_SELECT_BANK(lp, 1);
+ val = SMC_GET_BASE(lp);
+ val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT;
+ if (((unsigned int)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) {
+ printk("%s: IOADDR %p doesn't match configuration (%x).\n",
+ CARDNAME, ioaddr, val);
+ }
+
+ /*
+ * check if the revision register is something that I
+ * recognize. These might need to be added to later,
+ * as future revisions could be added.
+ */
+ SMC_SELECT_BANK(lp, 3);
+ revision_register = SMC_GET_REV(lp);
+ DBG(2, "%s: revision = 0x%04x\n", CARDNAME, revision_register);
+ version_string = chip_ids[ (revision_register >> 4) & 0xF];
+ if (!version_string || (revision_register & 0xff00) != 0x3300) {
+ /* I don't recognize this chip, so... */
+ printk("%s: IO %p: Unrecognized revision register 0x%04x"
+ ", Contact author.\n", CARDNAME,
+ ioaddr, revision_register);
+
+ retval = -ENODEV;
+ goto err_out;
+ }
+
+ /* At this point I'll assume that the chip is an SMC91x. */
+ if (version_printed++ == 0)
+ printk("%s", version);
+
+ /* fill in some of the fields */
+ dev->base_addr = (unsigned long)ioaddr;
+ lp->base = ioaddr;
+ lp->version = revision_register & 0xff;
+ spin_lock_init(&lp->lock);
+
+ /* Get the MAC address */
+ SMC_SELECT_BANK(lp, 1);
+ SMC_GET_MAC_ADDR(lp, dev->dev_addr);
+
+ /* now, reset the chip, and put it into a known state */
+ smc_reset(dev);
+
+ /*
+ * If dev->irq is 0, then the device has to be banged on to see
+ * what the IRQ is.
+ *
+ * This banging doesn't always detect the IRQ, for unknown reasons.
+ * a workaround is to reset the chip and try again.
+ *
+ * Interestingly, the DOS packet driver *SETS* the IRQ on the card to
+ * be what is requested on the command line. I don't do that, mostly
+ * because the card that I have uses a non-standard method of accessing
+ * the IRQs, and because this _should_ work in most configurations.
+ *
+ * Specifying an IRQ is done with the assumption that the user knows
+ * what (s)he is doing. No checking is done!!!!
+ */
+ if (dev->irq < 1) {
+ int trials;
+
+ trials = 3;
+ while (trials--) {
+ dev->irq = smc_findirq(lp);
+ if (dev->irq)
+ break;
+ /* kick the card and try again */
+ smc_reset(dev);
+ }
+ }
+ if (dev->irq == 0) {
+ printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
+ dev->name);
+ retval = -ENODEV;
+ goto err_out;
+ }
+ dev->irq = irq_canonicalize(dev->irq);
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ dev->watchdog_timeo = msecs_to_jiffies(watchdog);
+ dev->netdev_ops = &smc_netdev_ops;
+ dev->ethtool_ops = &smc_ethtool_ops;
+
+ tasklet_init(&lp->tx_task, smc_hardware_send_pkt, (unsigned long)dev);
+ INIT_WORK(&lp->phy_configure, smc_phy_configure);
+ lp->dev = dev;
+ lp->mii.phy_id_mask = 0x1f;
+ lp->mii.reg_num_mask = 0x1f;
+ lp->mii.force_media = 0;
+ lp->mii.full_duplex = 0;
+ lp->mii.dev = dev;
+ lp->mii.mdio_read = smc_phy_read;
+ lp->mii.mdio_write = smc_phy_write;
+
+ /*
+ * Locate the phy, if any.
+ */
+ if (lp->version >= (CHIP_91100 << 4))
+ smc_phy_detect(dev);
+
+ /* then shut everything down to save power */
+ smc_shutdown(dev);
+ smc_phy_powerdown(dev);
+
+ /* Set default parameters */
+ lp->msg_enable = NETIF_MSG_LINK;
+ lp->ctl_rfduplx = 0;
+ lp->ctl_rspeed = 10;
+
+ if (lp->version >= (CHIP_91100 << 4)) {
+ lp->ctl_rfduplx = 1;
+ lp->ctl_rspeed = 100;
+ }
+
+ /* Grab the IRQ */
+ retval = request_irq(dev->irq, smc_interrupt, irq_flags, dev->name, dev);
+ if (retval)
+ goto err_out;
+
+#ifdef CONFIG_ARCH_PXA
+# ifdef SMC_USE_PXA_DMA
+ lp->cfg.flags |= SMC91X_USE_DMA;
+# endif
+ if (lp->cfg.flags & SMC91X_USE_DMA) {
+ int dma = pxa_request_dma(dev->name, DMA_PRIO_LOW,
+ smc_pxa_dma_irq, NULL);
+ if (dma >= 0)
+ dev->dma = dma;
+ }
+#endif
+
+ retval = register_netdev(dev);
+ if (retval == 0) {
+ /* now, print out the card info, in a short format.. */
+ printk("%s: %s (rev %d) at %p IRQ %d",
+ dev->name, version_string, revision_register & 0x0f,
+ lp->base, dev->irq);
+
+ if (dev->dma != (unsigned char)-1)
+ printk(" DMA %d", dev->dma);
+
+ printk("%s%s\n",
+ lp->cfg.flags & SMC91X_NOWAIT ? " [nowait]" : "",
+ THROTTLE_TX_PKTS ? " [throttle_tx]" : "");
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ printk("%s: Invalid ethernet MAC address. Please "
+ "set using ifconfig\n", dev->name);
+ } else {
+ /* Print the Ethernet address */
+ printk("%s: Ethernet addr: %pM\n",
+ dev->name, dev->dev_addr);
+ }
+
+ if (lp->phy_type == 0) {
+ PRINTK("%s: No PHY found\n", dev->name);
+ } else if ((lp->phy_type & 0xfffffff0) == 0x0016f840) {
+ PRINTK("%s: PHY LAN83C183 (LAN91C111 Internal)\n", dev->name);
+ } else if ((lp->phy_type & 0xfffffff0) == 0x02821c50) {
+ PRINTK("%s: PHY LAN83C180\n", dev->name);
+ }
+ }
+
+err_out:
+#ifdef CONFIG_ARCH_PXA
+ if (retval && dev->dma != (unsigned char)-1)
+ pxa_free_dma(dev->dma);
+#endif
+ return retval;
+}
+
+static int smc_enable_device(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct smc_local *lp = netdev_priv(ndev);
+ unsigned long flags;
+ unsigned char ecor, ecsr;
+ void __iomem *addr;
+ struct resource * res;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+ if (!res)
+ return 0;
+
+ /*
+ * Map the attribute space. This is overkill, but clean.
+ */
+ addr = ioremap(res->start, ATTRIB_SIZE);
+ if (!addr)
+ return -ENOMEM;
+
+ /*
+ * Reset the device. We must disable IRQs around this
+ * since a reset causes the IRQ line become active.
+ */
+ local_irq_save(flags);
+ ecor = readb(addr + (ECOR << SMC_IO_SHIFT)) & ~ECOR_RESET;
+ writeb(ecor | ECOR_RESET, addr + (ECOR << SMC_IO_SHIFT));
+ readb(addr + (ECOR << SMC_IO_SHIFT));
+
+ /*
+ * Wait 100us for the chip to reset.
+ */
+ udelay(100);
+
+ /*
+ * The device will ignore all writes to the enable bit while
+ * reset is asserted, even if the reset bit is cleared in the
+ * same write. Must clear reset first, then enable the device.
+ */
+ writeb(ecor, addr + (ECOR << SMC_IO_SHIFT));
+ writeb(ecor | ECOR_ENABLE, addr + (ECOR << SMC_IO_SHIFT));
+
+ /*
+ * Set the appropriate byte/word mode.
+ */
+ ecsr = readb(addr + (ECSR << SMC_IO_SHIFT)) & ~ECSR_IOIS8;
+ if (!SMC_16BIT(lp))
+ ecsr |= ECSR_IOIS8;
+ writeb(ecsr, addr + (ECSR << SMC_IO_SHIFT));
+ local_irq_restore(flags);
+
+ iounmap(addr);
+
+ /*
+ * Wait for the chip to wake up. We could poll the control
+ * register in the main register space, but that isn't mapped
+ * yet. We know this is going to take 750us.
+ */
+ msleep(1);
+
+ return 0;
+}
+
+static int smc_request_attrib(struct platform_device *pdev,
+ struct net_device *ndev)
+{
+ struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+ struct smc_local *lp __maybe_unused = netdev_priv(ndev);
+
+ if (!res)
+ return 0;
+
+ if (!request_mem_region(res->start, ATTRIB_SIZE, CARDNAME))
+ return -EBUSY;
+
+ return 0;
+}
+
+static void smc_release_attrib(struct platform_device *pdev,
+ struct net_device *ndev)
+{
+ struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-attrib");
+ struct smc_local *lp __maybe_unused = netdev_priv(ndev);
+
+ if (res)
+ release_mem_region(res->start, ATTRIB_SIZE);
+}
+
+static inline void smc_request_datacs(struct platform_device *pdev, struct net_device *ndev)
+{
+ if (SMC_CAN_USE_DATACS) {
+ struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32");
+ struct smc_local *lp = netdev_priv(ndev);
+
+ if (!res)
+ return;
+
+ if(!request_mem_region(res->start, SMC_DATA_EXTENT, CARDNAME)) {
+ printk(KERN_INFO "%s: failed to request datacs memory region.\n", CARDNAME);
+ return;
+ }
+
+ lp->datacs = ioremap(res->start, SMC_DATA_EXTENT);
+ }
+}
+
+static void smc_release_datacs(struct platform_device *pdev, struct net_device *ndev)
+{
+ if (SMC_CAN_USE_DATACS) {
+ struct smc_local *lp = netdev_priv(ndev);
+ struct resource * res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-data32");
+
+ if (lp->datacs)
+ iounmap(lp->datacs);
+
+ lp->datacs = NULL;
+
+ if (res)
+ release_mem_region(res->start, SMC_DATA_EXTENT);
+ }
+}
+
+/*
+ * smc_init(void)
+ * Input parameters:
+ * dev->base_addr == 0, try to find all possible locations
+ * dev->base_addr > 0x1ff, this is the address to check
+ * dev->base_addr == <anything else>, return failure code
+ *
+ * Output:
+ * 0 --> there is a device
+ * anything else, error
+ */
+static int __devinit smc_drv_probe(struct platform_device *pdev)
+{
+ struct smc91x_platdata *pd = pdev->dev.platform_data;
+ struct smc_local *lp;
+ struct net_device *ndev;
+ struct resource *res, *ires;
+ unsigned int __iomem *addr;
+ unsigned long irq_flags = SMC_IRQ_FLAGS;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct smc_local));
+ if (!ndev) {
+ printk("%s: could not allocate device.\n", CARDNAME);
+ ret = -ENOMEM;
+ goto out;
+ }
+ SET_NETDEV_DEV(ndev, &pdev->dev);
+
+ /* get configuration from platform data, only allow use of
+ * bus width if both SMC_CAN_USE_xxx and SMC91X_USE_xxx are set.
+ */
+
+ lp = netdev_priv(ndev);
+
+ if (pd) {
+ memcpy(&lp->cfg, pd, sizeof(lp->cfg));
+ lp->io_shift = SMC91X_IO_SHIFT(lp->cfg.flags);
+ } else {
+ lp->cfg.flags |= (SMC_CAN_USE_8BIT) ? SMC91X_USE_8BIT : 0;
+ lp->cfg.flags |= (SMC_CAN_USE_16BIT) ? SMC91X_USE_16BIT : 0;
+ lp->cfg.flags |= (SMC_CAN_USE_32BIT) ? SMC91X_USE_32BIT : 0;
+ lp->cfg.flags |= (nowait) ? SMC91X_NOWAIT : 0;
+ }
+
+ if (!lp->cfg.leda && !lp->cfg.ledb) {
+ lp->cfg.leda = RPC_LSA_DEFAULT;
+ lp->cfg.ledb = RPC_LSB_DEFAULT;
+ }
+
+ ndev->dma = (unsigned char)-1;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ goto out_free_netdev;
+ }
+
+
+ if (!request_mem_region(res->start, SMC_IO_EXTENT, CARDNAME)) {
+ ret = -EBUSY;
+ goto out_free_netdev;
+ }
+
+ ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!ires) {
+ ret = -ENODEV;
+ goto out_release_io;
+ }
+
+ ndev->irq = ires->start;
+
+ if (irq_flags == -1 || ires->flags & IRQF_TRIGGER_MASK)
+ irq_flags = ires->flags & IRQF_TRIGGER_MASK;
+
+ ret = smc_request_attrib(pdev, ndev);
+ if (ret)
+ goto out_release_io;
+#if defined(CONFIG_SA1100_ASSABET)
+ NCR_0 |= NCR_ENET_OSC_EN;
+#endif
+ platform_set_drvdata(pdev, ndev);
+ ret = smc_enable_device(pdev);
+ if (ret)
+ goto out_release_attrib;
+
+ addr = ioremap(res->start, SMC_IO_EXTENT);
+ if (!addr) {
+ ret = -ENOMEM;
+ goto out_release_attrib;
+ }
+
+#ifdef CONFIG_ARCH_PXA
+ {
+ struct smc_local *lp = netdev_priv(ndev);
+ lp->device = &pdev->dev;
+ lp->physaddr = res->start;
+ }
+#endif
+
+ ret = smc_probe(ndev, addr, irq_flags);
+ if (ret != 0)
+ goto out_iounmap;
+
+ smc_request_datacs(pdev, ndev);
+
+ return 0;
+
+ out_iounmap:
+ platform_set_drvdata(pdev, NULL);
+ iounmap(addr);
+ out_release_attrib:
+ smc_release_attrib(pdev, ndev);
+ out_release_io:
+ release_mem_region(res->start, SMC_IO_EXTENT);
+ out_free_netdev:
+ free_netdev(ndev);
+ out:
+ printk("%s: not found (%d).\n", CARDNAME, ret);
+
+ return ret;
+}
+
+static int __devexit smc_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *ndev = platform_get_drvdata(pdev);
+ struct smc_local *lp = netdev_priv(ndev);
+ struct resource *res;
+
+ platform_set_drvdata(pdev, NULL);
+
+ unregister_netdev(ndev);
+
+ free_irq(ndev->irq, ndev);
+
+#ifdef CONFIG_ARCH_PXA
+ if (ndev->dma != (unsigned char)-1)
+ pxa_free_dma(ndev->dma);
+#endif
+ iounmap(lp->base);
+
+ smc_release_datacs(pdev,ndev);
+ smc_release_attrib(pdev,ndev);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smc91x-regs");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, SMC_IO_EXTENT);
+
+ free_netdev(ndev);
+
+ return 0;
+}
+
+static int smc_drv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ if (ndev) {
+ if (netif_running(ndev)) {
+ netif_device_detach(ndev);
+ smc_shutdown(ndev);
+ smc_phy_powerdown(ndev);
+ }
+ }
+ return 0;
+}
+
+static int smc_drv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct net_device *ndev = platform_get_drvdata(pdev);
+
+ if (ndev) {
+ struct smc_local *lp = netdev_priv(ndev);
+ smc_enable_device(pdev);
+ if (netif_running(ndev)) {
+ smc_reset(ndev);
+ smc_enable(ndev);
+ if (lp->phy_type != 0)
+ smc_phy_configure(&lp->phy_configure);
+ netif_device_attach(ndev);
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id smc91x_match[] = {
+ { .compatible = "smsc,lan91c94", },
+ { .compatible = "smsc,lan91c111", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, smc91x_match);
+#else
+#define smc91x_match NULL
+#endif
+
+static struct dev_pm_ops smc_drv_pm_ops = {
+ .suspend = smc_drv_suspend,
+ .resume = smc_drv_resume,
+};
+
+static struct platform_driver smc_driver = {
+ .probe = smc_drv_probe,
+ .remove = __devexit_p(smc_drv_remove),
+ .driver = {
+ .name = CARDNAME,
+ .owner = THIS_MODULE,
+ .pm = &smc_drv_pm_ops,
+ .of_match_table = smc91x_match,
+ },
+};
+
+static int __init smc_init(void)
+{
+ return platform_driver_register(&smc_driver);
+}
+
+static void __exit smc_cleanup(void)
+{
+ platform_driver_unregister(&smc_driver);
+}
+
+module_init(smc_init);
+module_exit(smc_cleanup);
diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h
new file mode 100644
index 000000000000..5f53fbbf67be
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smc91x.h
@@ -0,0 +1,1180 @@
+/*------------------------------------------------------------------------
+ . smc91x.h - macros for SMSC's 91C9x/91C1xx single-chip Ethernet device.
+ .
+ . Copyright (C) 1996 by Erik Stahlman
+ . Copyright (C) 2001 Standard Microsystems Corporation
+ . Developed by Simple Network Magic Corporation
+ . Copyright (C) 2003 Monta Vista Software, Inc.
+ . Unified SMC91x driver by Nicolas Pitre
+ .
+ . This program is free software; you can redistribute it and/or modify
+ . it under the terms of the GNU General Public License as published by
+ . the Free Software Foundation; either version 2 of the License, or
+ . (at your option) any later version.
+ .
+ . This program is distributed in the hope that it will be useful,
+ . but WITHOUT ANY WARRANTY; without even the implied warranty of
+ . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ . GNU General Public License for more details.
+ .
+ . You should have received a copy of the GNU General Public License
+ . along with this program; if not, write to the Free Software
+ . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ .
+ . Information contained in this file was obtained from the LAN91C111
+ . manual from SMC. To get a copy, if you really want one, you can find
+ . information under www.smsc.com.
+ .
+ . Authors
+ . Erik Stahlman <erik@vt.edu>
+ . Daris A Nevil <dnevil@snmc.com>
+ . Nicolas Pitre <nico@fluxnic.net>
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC91X_H_
+#define _SMC91X_H_
+
+#include <linux/smc91x.h>
+
+/*
+ * Define your architecture specific bus configuration parameters here.
+ */
+
+#if defined(CONFIG_ARCH_LUBBOCK) ||\
+ defined(CONFIG_MACH_MAINSTONE) ||\
+ defined(CONFIG_MACH_ZYLONITE) ||\
+ defined(CONFIG_MACH_LITTLETON) ||\
+ defined(CONFIG_MACH_ZYLONITE2) ||\
+ defined(CONFIG_ARCH_VIPER) ||\
+ defined(CONFIG_MACH_STARGATE2)
+
+#include <asm/mach-types.h>
+
+/* Now the bus width is specified in the platform data
+ * pretend here to support all I/O access types
+ */
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_NOWAIT 1
+
+#define SMC_IO_SHIFT (lp->io_shift)
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+#define SMC_IRQ_FLAGS (-1) /* from resource */
+
+/* We actually can't write halfwords properly if not word aligned */
+static inline void SMC_outw(u16 val, void __iomem *ioaddr, int reg)
+{
+ if ((machine_is_mainstone() || machine_is_stargate2()) && reg & 2) {
+ unsigned int v = val << 16;
+ v |= readl(ioaddr + (reg & ~2)) & 0xffff;
+ writel(v, ioaddr + (reg & ~2));
+ } else {
+ writew(val, ioaddr + reg);
+ }
+}
+
+#elif defined(CONFIG_SA1100_PLEB)
+/* We can only do 16-bit reads and writes in the static memory space. */
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_IO_SHIFT 0
+#define SMC_NOWAIT 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS (-1)
+
+#elif defined(CONFIG_SA1100_ASSABET)
+
+#include <mach/neponset.h>
+
+/* We can only do 8-bit reads and writes in the static memory space. */
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 0
+#define SMC_CAN_USE_32BIT 0
+#define SMC_NOWAIT 1
+
+/* The first two address lines aren't connected... */
+#define SMC_IO_SHIFT 2
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_insb(a, r, p, l) readsb((a) + (r), p, (l))
+#define SMC_outsb(a, r, p, l) writesb((a) + (r), p, (l))
+#define SMC_IRQ_FLAGS (-1) /* from resource */
+
+#elif defined(CONFIG_MACH_LOGICPD_PXA270) || \
+ defined(CONFIG_MACH_NOMADIK_8815NHK)
+
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_IO_SHIFT 0
+#define SMC_NOWAIT 1
+
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+
+#elif defined(CONFIG_ARCH_INNOKOM) || \
+ defined(CONFIG_ARCH_PXA_IDP) || \
+ defined(CONFIG_ARCH_RAMSES) || \
+ defined(CONFIG_ARCH_PCM027)
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_IO_SHIFT 0
+#define SMC_NOWAIT 1
+#define SMC_USE_PXA_DMA 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+#define SMC_IRQ_FLAGS (-1) /* from resource */
+
+/* We actually can't write halfwords properly if not word aligned */
+static inline void
+SMC_outw(u16 val, void __iomem *ioaddr, int reg)
+{
+ if (reg & 2) {
+ unsigned int v = val << 16;
+ v |= readl(ioaddr + (reg & ~2)) & 0xffff;
+ writel(v, ioaddr + (reg & ~2));
+ } else {
+ writew(val, ioaddr + reg);
+ }
+}
+
+#elif defined(CONFIG_SH_SH4202_MICRODEV)
+
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+
+#define SMC_inb(a, r) inb((a) + (r) - 0xa0000000)
+#define SMC_inw(a, r) inw((a) + (r) - 0xa0000000)
+#define SMC_inl(a, r) inl((a) + (r) - 0xa0000000)
+#define SMC_outb(v, a, r) outb(v, (a) + (r) - 0xa0000000)
+#define SMC_outw(v, a, r) outw(v, (a) + (r) - 0xa0000000)
+#define SMC_outl(v, a, r) outl(v, (a) + (r) - 0xa0000000)
+#define SMC_insl(a, r, p, l) insl((a) + (r) - 0xa0000000, p, l)
+#define SMC_outsl(a, r, p, l) outsl((a) + (r) - 0xa0000000, p, l)
+#define SMC_insw(a, r, p, l) insw((a) + (r) - 0xa0000000, p, l)
+#define SMC_outsw(a, r, p, l) outsw((a) + (r) - 0xa0000000, p, l)
+
+#define SMC_IRQ_FLAGS (0)
+
+#elif defined(CONFIG_M32R)
+
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+
+#define SMC_inb(a, r) inb(((u32)a) + (r))
+#define SMC_inw(a, r) inw(((u32)a) + (r))
+#define SMC_outb(v, a, r) outb(v, ((u32)a) + (r))
+#define SMC_outw(v, a, r) outw(v, ((u32)a) + (r))
+#define SMC_insw(a, r, p, l) insw(((u32)a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) outsw(((u32)a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS (0)
+
+#define RPC_LSA_DEFAULT RPC_LED_TX_RX
+#define RPC_LSB_DEFAULT RPC_LED_100_10
+
+#elif defined(CONFIG_ARCH_VERSATILE)
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_NOWAIT 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+#define SMC_IRQ_FLAGS (-1) /* from resource */
+
+#elif defined(CONFIG_MN10300)
+
+/*
+ * MN10300/AM33 configuration
+ */
+
+#include <unit/smc91111.h>
+
+#elif defined(CONFIG_ARCH_MSM)
+
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_NOWAIT 1
+
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS IRQF_TRIGGER_HIGH
+
+#elif defined(CONFIG_COLDFIRE)
+
+#define SMC_CAN_USE_8BIT 0
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 0
+#define SMC_NOWAIT 1
+
+static inline void mcf_insw(void *a, unsigned char *p, int l)
+{
+ u16 *wp = (u16 *) p;
+ while (l-- > 0)
+ *wp++ = readw(a);
+}
+
+static inline void mcf_outsw(void *a, unsigned char *p, int l)
+{
+ u16 *wp = (u16 *) p;
+ while (l-- > 0)
+ writew(*wp++, a);
+}
+
+#define SMC_inw(a, r) _swapw(readw((a) + (r)))
+#define SMC_outw(v, a, r) writew(_swapw(v), (a) + (r))
+#define SMC_insw(a, r, p, l) mcf_insw(a + r, p, l)
+#define SMC_outsw(a, r, p, l) mcf_outsw(a + r, p, l)
+
+#define SMC_IRQ_FLAGS (IRQF_DISABLED)
+
+#else
+
+/*
+ * Default configuration
+ */
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_NOWAIT 1
+
+#define SMC_IO_SHIFT (lp->io_shift)
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l)
+#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l)
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+
+#define RPC_LSA_DEFAULT RPC_LED_100_10
+#define RPC_LSB_DEFAULT RPC_LED_TX_RX
+
+#endif
+
+
+/* store this information for the driver.. */
+struct smc_local {
+ /*
+ * If I have to wait until memory is available to send a
+ * packet, I will store the skbuff here, until I get the
+ * desired memory. Then, I'll send it out and free it.
+ */
+ struct sk_buff *pending_tx_skb;
+ struct tasklet_struct tx_task;
+
+ /* version/revision of the SMC91x chip */
+ int version;
+
+ /* Contains the current active transmission mode */
+ int tcr_cur_mode;
+
+ /* Contains the current active receive mode */
+ int rcr_cur_mode;
+
+ /* Contains the current active receive/phy mode */
+ int rpc_cur_mode;
+ int ctl_rfduplx;
+ int ctl_rspeed;
+
+ u32 msg_enable;
+ u32 phy_type;
+ struct mii_if_info mii;
+
+ /* work queue */
+ struct work_struct phy_configure;
+ struct net_device *dev;
+ int work_pending;
+
+ spinlock_t lock;
+
+#ifdef CONFIG_ARCH_PXA
+ /* DMA needs the physical address of the chip */
+ u_long physaddr;
+ struct device *device;
+#endif
+ void __iomem *base;
+ void __iomem *datacs;
+
+ /* the low address lines on some platforms aren't connected... */
+ int io_shift;
+
+ struct smc91x_platdata cfg;
+};
+
+#define SMC_8BIT(p) ((p)->cfg.flags & SMC91X_USE_8BIT)
+#define SMC_16BIT(p) ((p)->cfg.flags & SMC91X_USE_16BIT)
+#define SMC_32BIT(p) ((p)->cfg.flags & SMC91X_USE_32BIT)
+
+#ifdef CONFIG_ARCH_PXA
+/*
+ * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is
+ * always happening in irq context so no need to worry about races. TX is
+ * different and probably not worth it for that reason, and not as critical
+ * as RX which can overrun memory and lose packets.
+ */
+#include <linux/dma-mapping.h>
+#include <mach/dma.h>
+
+#ifdef SMC_insl
+#undef SMC_insl
+#define SMC_insl(a, r, p, l) \
+ smc_pxa_dma_insl(a, lp, r, dev->dma, p, l)
+static inline void
+smc_pxa_dma_insl(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
+ u_char *buf, int len)
+{
+ u_long physaddr = lp->physaddr;
+ dma_addr_t dmabuf;
+
+ /* fallback if no DMA available */
+ if (dma == (unsigned char)-1) {
+ readsl(ioaddr + reg, buf, len);
+ return;
+ }
+
+ /* 64 bit alignment is required for memory to memory DMA */
+ if ((long)buf & 4) {
+ *((u32 *)buf) = SMC_inl(ioaddr, reg);
+ buf += 4;
+ len--;
+ }
+
+ len *= 4;
+ dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE);
+ DCSR(dma) = DCSR_NODESC;
+ DTADR(dma) = dmabuf;
+ DSADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+ DCMD_WIDTH4 | (DCMD_LENGTH & len));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+ while (!(DCSR(dma) & DCSR_STOPSTATE))
+ cpu_relax();
+ DCSR(dma) = 0;
+ dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE);
+}
+#endif
+
+#ifdef SMC_insw
+#undef SMC_insw
+#define SMC_insw(a, r, p, l) \
+ smc_pxa_dma_insw(a, lp, r, dev->dma, p, l)
+static inline void
+smc_pxa_dma_insw(void __iomem *ioaddr, struct smc_local *lp, int reg, int dma,
+ u_char *buf, int len)
+{
+ u_long physaddr = lp->physaddr;
+ dma_addr_t dmabuf;
+
+ /* fallback if no DMA available */
+ if (dma == (unsigned char)-1) {
+ readsw(ioaddr + reg, buf, len);
+ return;
+ }
+
+ /* 64 bit alignment is required for memory to memory DMA */
+ while ((long)buf & 6) {
+ *((u16 *)buf) = SMC_inw(ioaddr, reg);
+ buf += 2;
+ len--;
+ }
+
+ len *= 2;
+ dmabuf = dma_map_single(lp->device, buf, len, DMA_FROM_DEVICE);
+ DCSR(dma) = DCSR_NODESC;
+ DTADR(dma) = dmabuf;
+ DSADR(dma) = physaddr + reg;
+ DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 |
+ DCMD_WIDTH2 | (DCMD_LENGTH & len));
+ DCSR(dma) = DCSR_NODESC | DCSR_RUN;
+ while (!(DCSR(dma) & DCSR_STOPSTATE))
+ cpu_relax();
+ DCSR(dma) = 0;
+ dma_unmap_single(lp->device, dmabuf, len, DMA_FROM_DEVICE);
+}
+#endif
+
+static void
+smc_pxa_dma_irq(int dma, void *dummy)
+{
+ DCSR(dma) = 0;
+}
+#endif /* CONFIG_ARCH_PXA */
+
+
+/*
+ * Everything a particular hardware setup needs should have been defined
+ * at this point. Add stubs for the undefined cases, mainly to avoid
+ * compilation warnings since they'll be optimized away, or to prevent buggy
+ * use of them.
+ */
+
+#if ! SMC_CAN_USE_32BIT
+#define SMC_inl(ioaddr, reg) ({ BUG(); 0; })
+#define SMC_outl(x, ioaddr, reg) BUG()
+#define SMC_insl(a, r, p, l) BUG()
+#define SMC_outsl(a, r, p, l) BUG()
+#endif
+
+#if !defined(SMC_insl) || !defined(SMC_outsl)
+#define SMC_insl(a, r, p, l) BUG()
+#define SMC_outsl(a, r, p, l) BUG()
+#endif
+
+#if ! SMC_CAN_USE_16BIT
+
+/*
+ * Any 16-bit access is performed with two 8-bit accesses if the hardware
+ * can't do it directly. Most registers are 16-bit so those are mandatory.
+ */
+#define SMC_outw(x, ioaddr, reg) \
+ do { \
+ unsigned int __val16 = (x); \
+ SMC_outb( __val16, ioaddr, reg ); \
+ SMC_outb( __val16 >> 8, ioaddr, reg + (1 << SMC_IO_SHIFT));\
+ } while (0)
+#define SMC_inw(ioaddr, reg) \
+ ({ \
+ unsigned int __val16; \
+ __val16 = SMC_inb( ioaddr, reg ); \
+ __val16 |= SMC_inb( ioaddr, reg + (1 << SMC_IO_SHIFT)) << 8; \
+ __val16; \
+ })
+
+#define SMC_insw(a, r, p, l) BUG()
+#define SMC_outsw(a, r, p, l) BUG()
+
+#endif
+
+#if !defined(SMC_insw) || !defined(SMC_outsw)
+#define SMC_insw(a, r, p, l) BUG()
+#define SMC_outsw(a, r, p, l) BUG()
+#endif
+
+#if ! SMC_CAN_USE_8BIT
+#define SMC_inb(ioaddr, reg) ({ BUG(); 0; })
+#define SMC_outb(x, ioaddr, reg) BUG()
+#define SMC_insb(a, r, p, l) BUG()
+#define SMC_outsb(a, r, p, l) BUG()
+#endif
+
+#if !defined(SMC_insb) || !defined(SMC_outsb)
+#define SMC_insb(a, r, p, l) BUG()
+#define SMC_outsb(a, r, p, l) BUG()
+#endif
+
+#ifndef SMC_CAN_USE_DATACS
+#define SMC_CAN_USE_DATACS 0
+#endif
+
+#ifndef SMC_IO_SHIFT
+#define SMC_IO_SHIFT 0
+#endif
+
+#ifndef SMC_IRQ_FLAGS
+#define SMC_IRQ_FLAGS IRQF_TRIGGER_RISING
+#endif
+
+#ifndef SMC_INTERRUPT_PREAMBLE
+#define SMC_INTERRUPT_PREAMBLE
+#endif
+
+
+/* Because of bank switching, the LAN91x uses only 16 I/O ports */
+#define SMC_IO_EXTENT (16 << SMC_IO_SHIFT)
+#define SMC_DATA_EXTENT (4)
+
+/*
+ . Bank Select Register:
+ .
+ . yyyy yyyy 0000 00xx
+ . xx = bank number
+ . yyyy yyyy = 0x33, for identification purposes.
+*/
+#define BANK_SELECT (14 << SMC_IO_SHIFT)
+
+
+// Transmit Control Register
+/* BANK 0 */
+#define TCR_REG(lp) SMC_REG(lp, 0x0000, 0)
+#define TCR_ENABLE 0x0001 // When 1 we can transmit
+#define TCR_LOOP 0x0002 // Controls output pin LBK
+#define TCR_FORCOL 0x0004 // When 1 will force a collision
+#define TCR_PAD_EN 0x0080 // When 1 will pad tx frames < 64 bytes w/0
+#define TCR_NOCRC 0x0100 // When 1 will not append CRC to tx frames
+#define TCR_MON_CSN 0x0400 // When 1 tx monitors carrier
+#define TCR_FDUPLX 0x0800 // When 1 enables full duplex operation
+#define TCR_STP_SQET 0x1000 // When 1 stops tx if Signal Quality Error
+#define TCR_EPH_LOOP 0x2000 // When 1 enables EPH block loopback
+#define TCR_SWFDUP 0x8000 // When 1 enables Switched Full Duplex mode
+
+#define TCR_CLEAR 0 /* do NOTHING */
+/* the default settings for the TCR register : */
+#define TCR_DEFAULT (TCR_ENABLE | TCR_PAD_EN)
+
+
+// EPH Status Register
+/* BANK 0 */
+#define EPH_STATUS_REG(lp) SMC_REG(lp, 0x0002, 0)
+#define ES_TX_SUC 0x0001 // Last TX was successful
+#define ES_SNGL_COL 0x0002 // Single collision detected for last tx
+#define ES_MUL_COL 0x0004 // Multiple collisions detected for last tx
+#define ES_LTX_MULT 0x0008 // Last tx was a multicast
+#define ES_16COL 0x0010 // 16 Collisions Reached
+#define ES_SQET 0x0020 // Signal Quality Error Test
+#define ES_LTXBRD 0x0040 // Last tx was a broadcast
+#define ES_TXDEFR 0x0080 // Transmit Deferred
+#define ES_LATCOL 0x0200 // Late collision detected on last tx
+#define ES_LOSTCARR 0x0400 // Lost Carrier Sense
+#define ES_EXC_DEF 0x0800 // Excessive Deferral
+#define ES_CTR_ROL 0x1000 // Counter Roll Over indication
+#define ES_LINK_OK 0x4000 // Driven by inverted value of nLNK pin
+#define ES_TXUNRN 0x8000 // Tx Underrun
+
+
+// Receive Control Register
+/* BANK 0 */
+#define RCR_REG(lp) SMC_REG(lp, 0x0004, 0)
+#define RCR_RX_ABORT 0x0001 // Set if a rx frame was aborted
+#define RCR_PRMS 0x0002 // Enable promiscuous mode
+#define RCR_ALMUL 0x0004 // When set accepts all multicast frames
+#define RCR_RXEN 0x0100 // IFF this is set, we can receive packets
+#define RCR_STRIP_CRC 0x0200 // When set strips CRC from rx packets
+#define RCR_ABORT_ENB 0x0200 // When set will abort rx on collision
+#define RCR_FILT_CAR 0x0400 // When set filters leading 12 bit s of carrier
+#define RCR_SOFTRST 0x8000 // resets the chip
+
+/* the normal settings for the RCR register : */
+#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN)
+#define RCR_CLEAR 0x0 // set it to a base state
+
+
+// Counter Register
+/* BANK 0 */
+#define COUNTER_REG(lp) SMC_REG(lp, 0x0006, 0)
+
+
+// Memory Information Register
+/* BANK 0 */
+#define MIR_REG(lp) SMC_REG(lp, 0x0008, 0)
+
+
+// Receive/Phy Control Register
+/* BANK 0 */
+#define RPC_REG(lp) SMC_REG(lp, 0x000A, 0)
+#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode.
+#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode
+#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode
+#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb
+#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb
+
+#ifndef RPC_LSA_DEFAULT
+#define RPC_LSA_DEFAULT RPC_LED_100
+#endif
+#ifndef RPC_LSB_DEFAULT
+#define RPC_LSB_DEFAULT RPC_LED_FD
+#endif
+
+#define RPC_DEFAULT (RPC_ANEG | RPC_SPEED | RPC_DPLX)
+
+
+/* Bank 0 0x0C is reserved */
+
+// Bank Select Register
+/* All Banks */
+#define BSR_REG 0x000E
+
+
+// Configuration Reg
+/* BANK 1 */
+#define CONFIG_REG(lp) SMC_REG(lp, 0x0000, 1)
+#define CONFIG_EXT_PHY 0x0200 // 1=external MII, 0=internal Phy
+#define CONFIG_GPCNTRL 0x0400 // Inverse value drives pin nCNTRL
+#define CONFIG_NO_WAIT 0x1000 // When 1 no extra wait states on ISA bus
+#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode.
+
+// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low
+#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN)
+
+
+// Base Address Register
+/* BANK 1 */
+#define BASE_REG(lp) SMC_REG(lp, 0x0002, 1)
+
+
+// Individual Address Registers
+/* BANK 1 */
+#define ADDR0_REG(lp) SMC_REG(lp, 0x0004, 1)
+#define ADDR1_REG(lp) SMC_REG(lp, 0x0006, 1)
+#define ADDR2_REG(lp) SMC_REG(lp, 0x0008, 1)
+
+
+// General Purpose Register
+/* BANK 1 */
+#define GP_REG(lp) SMC_REG(lp, 0x000A, 1)
+
+
+// Control Register
+/* BANK 1 */
+#define CTL_REG(lp) SMC_REG(lp, 0x000C, 1)
+#define CTL_RCV_BAD 0x4000 // When 1 bad CRC packets are received
+#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically
+#define CTL_LE_ENABLE 0x0080 // When 1 enables Link Error interrupt
+#define CTL_CR_ENABLE 0x0040 // When 1 enables Counter Rollover interrupt
+#define CTL_TE_ENABLE 0x0020 // When 1 enables Transmit Error interrupt
+#define CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store
+#define CTL_RELOAD 0x0002 // When set reads EEPROM into registers
+#define CTL_STORE 0x0001 // When set stores registers into EEPROM
+
+
+// MMU Command Register
+/* BANK 2 */
+#define MMU_CMD_REG(lp) SMC_REG(lp, 0x0000, 2)
+#define MC_BUSY 1 // When 1 the last release has not completed
+#define MC_NOP (0<<5) // No Op
+#define MC_ALLOC (1<<5) // OR with number of 256 byte packets
+#define MC_RESET (2<<5) // Reset MMU to initial state
+#define MC_REMOVE (3<<5) // Remove the current rx packet
+#define MC_RELEASE (4<<5) // Remove and release the current rx packet
+#define MC_FREEPKT (5<<5) // Release packet in PNR register
+#define MC_ENQUEUE (6<<5) // Enqueue the packet for transmit
+#define MC_RSTTXFIFO (7<<5) // Reset the TX FIFOs
+
+
+// Packet Number Register
+/* BANK 2 */
+#define PN_REG(lp) SMC_REG(lp, 0x0002, 2)
+
+
+// Allocation Result Register
+/* BANK 2 */
+#define AR_REG(lp) SMC_REG(lp, 0x0003, 2)
+#define AR_FAILED 0x80 // Alocation Failed
+
+
+// TX FIFO Ports Register
+/* BANK 2 */
+#define TXFIFO_REG(lp) SMC_REG(lp, 0x0004, 2)
+#define TXFIFO_TEMPTY 0x80 // TX FIFO Empty
+
+// RX FIFO Ports Register
+/* BANK 2 */
+#define RXFIFO_REG(lp) SMC_REG(lp, 0x0005, 2)
+#define RXFIFO_REMPTY 0x80 // RX FIFO Empty
+
+#define FIFO_REG(lp) SMC_REG(lp, 0x0004, 2)
+
+// Pointer Register
+/* BANK 2 */
+#define PTR_REG(lp) SMC_REG(lp, 0x0006, 2)
+#define PTR_RCV 0x8000 // 1=Receive area, 0=Transmit area
+#define PTR_AUTOINC 0x4000 // Auto increment the pointer on each access
+#define PTR_READ 0x2000 // When 1 the operation is a read
+
+
+// Data Register
+/* BANK 2 */
+#define DATA_REG(lp) SMC_REG(lp, 0x0008, 2)
+
+
+// Interrupt Status/Acknowledge Register
+/* BANK 2 */
+#define INT_REG(lp) SMC_REG(lp, 0x000C, 2)
+
+
+// Interrupt Mask Register
+/* BANK 2 */
+#define IM_REG(lp) SMC_REG(lp, 0x000D, 2)
+#define IM_MDINT 0x80 // PHY MI Register 18 Interrupt
+#define IM_ERCV_INT 0x40 // Early Receive Interrupt
+#define IM_EPH_INT 0x20 // Set by Ethernet Protocol Handler section
+#define IM_RX_OVRN_INT 0x10 // Set by Receiver Overruns
+#define IM_ALLOC_INT 0x08 // Set when allocation request is completed
+#define IM_TX_EMPTY_INT 0x04 // Set if the TX FIFO goes empty
+#define IM_TX_INT 0x02 // Transmit Interrupt
+#define IM_RCV_INT 0x01 // Receive Interrupt
+
+
+// Multicast Table Registers
+/* BANK 3 */
+#define MCAST_REG1(lp) SMC_REG(lp, 0x0000, 3)
+#define MCAST_REG2(lp) SMC_REG(lp, 0x0002, 3)
+#define MCAST_REG3(lp) SMC_REG(lp, 0x0004, 3)
+#define MCAST_REG4(lp) SMC_REG(lp, 0x0006, 3)
+
+
+// Management Interface Register (MII)
+/* BANK 3 */
+#define MII_REG(lp) SMC_REG(lp, 0x0008, 3)
+#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup
+#define MII_MDOE 0x0008 // MII Output Enable
+#define MII_MCLK 0x0004 // MII Clock, pin MDCLK
+#define MII_MDI 0x0002 // MII Input, pin MDI
+#define MII_MDO 0x0001 // MII Output, pin MDO
+
+
+// Revision Register
+/* BANK 3 */
+/* ( hi: chip id low: rev # ) */
+#define REV_REG(lp) SMC_REG(lp, 0x000A, 3)
+
+
+// Early RCV Register
+/* BANK 3 */
+/* this is NOT on SMC9192 */
+#define ERCV_REG(lp) SMC_REG(lp, 0x000C, 3)
+#define ERCV_RCV_DISCRD 0x0080 // When 1 discards a packet being received
+#define ERCV_THRESHOLD 0x001F // ERCV Threshold Mask
+
+
+// External Register
+/* BANK 7 */
+#define EXT_REG(lp) SMC_REG(lp, 0x0000, 7)
+
+
+#define CHIP_9192 3
+#define CHIP_9194 4
+#define CHIP_9195 5
+#define CHIP_9196 6
+#define CHIP_91100 7
+#define CHIP_91100FD 8
+#define CHIP_91111FD 9
+
+static const char * chip_ids[ 16 ] = {
+ NULL, NULL, NULL,
+ /* 3 */ "SMC91C90/91C92",
+ /* 4 */ "SMC91C94",
+ /* 5 */ "SMC91C95",
+ /* 6 */ "SMC91C96",
+ /* 7 */ "SMC91C100",
+ /* 8 */ "SMC91C100FD",
+ /* 9 */ "SMC91C11xFD",
+ NULL, NULL, NULL,
+ NULL, NULL, NULL};
+
+
+/*
+ . Receive status bits
+*/
+#define RS_ALGNERR 0x8000
+#define RS_BRODCAST 0x4000
+#define RS_BADCRC 0x2000
+#define RS_ODDFRAME 0x1000
+#define RS_TOOLONG 0x0800
+#define RS_TOOSHORT 0x0400
+#define RS_MULTICAST 0x0001
+#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+
+/*
+ * PHY IDs
+ * LAN83C183 == LAN91C111 Internal PHY
+ */
+#define PHY_LAN83C183 0x0016f840
+#define PHY_LAN83C180 0x02821c50
+
+/*
+ * PHY Register Addresses (LAN91C111 Internal PHY)
+ *
+ * Generic PHY registers can be found in <linux/mii.h>
+ *
+ * These phy registers are specific to our on-board phy.
+ */
+
+// PHY Configuration Register 1
+#define PHY_CFG1_REG 0x10
+#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled
+#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled
+#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down
+#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler
+#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable
+#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled
+#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm)
+#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db
+#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust
+#define PHY_CFG1_TLVL_MASK 0x003C
+#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time
+
+
+// PHY Configuration Register 2
+#define PHY_CFG2_REG 0x11
+#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled
+#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled
+#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt)
+#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo
+
+// PHY Status Output (and Interrupt status) Register
+#define PHY_INT_REG 0x12 // Status Output (Interrupt Status)
+#define PHY_INT_INT 0x8000 // 1=bits have changed since last read
+#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected
+#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync
+#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx
+#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx
+#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx
+#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected
+#define PHY_INT_JAB 0x0100 // 1=Jabber detected
+#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode
+#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex
+
+// PHY Interrupt/Status Mask Register
+#define PHY_MASK_REG 0x13 // Interrupt Mask
+// Uses the same bit definitions as PHY_INT_REG
+
+
+/*
+ * SMC91C96 ethernet config and status registers.
+ * These are in the "attribute" space.
+ */
+#define ECOR 0x8000
+#define ECOR_RESET 0x80
+#define ECOR_LEVEL_IRQ 0x40
+#define ECOR_WR_ATTRIB 0x04
+#define ECOR_ENABLE 0x01
+
+#define ECSR 0x8002
+#define ECSR_IOIS8 0x20
+#define ECSR_PWRDWN 0x04
+#define ECSR_INT 0x02
+
+#define ATTRIB_SIZE ((64*1024) << SMC_IO_SHIFT)
+
+
+/*
+ * Macros to abstract register access according to the data bus
+ * capabilities. Please use those and not the in/out primitives.
+ * Note: the following macros do *not* select the bank -- this must
+ * be done separately as needed in the main code. The SMC_REG() macro
+ * only uses the bank argument for debugging purposes (when enabled).
+ *
+ * Note: despite inline functions being safer, everything leading to this
+ * should preferably be macros to let BUG() display the line number in
+ * the core source code since we're interested in the top call site
+ * not in any inline function location.
+ */
+
+#if SMC_DEBUG > 0
+#define SMC_REG(lp, reg, bank) \
+ ({ \
+ int __b = SMC_CURRENT_BANK(lp); \
+ if (unlikely((__b & ~0xf0) != (0x3300 | bank))) { \
+ printk( "%s: bank reg screwed (0x%04x)\n", \
+ CARDNAME, __b ); \
+ BUG(); \
+ } \
+ reg<<SMC_IO_SHIFT; \
+ })
+#else
+#define SMC_REG(lp, reg, bank) (reg<<SMC_IO_SHIFT)
+#endif
+
+/*
+ * Hack Alert: Some setups just can't write 8 or 16 bits reliably when not
+ * aligned to a 32 bit boundary. I tell you that does exist!
+ * Fortunately the affected register accesses can be easily worked around
+ * since we can write zeroes to the preceding 16 bits without adverse
+ * effects and use a 32-bit access.
+ *
+ * Enforce it on any 32-bit capable setup for now.
+ */
+#define SMC_MUST_ALIGN_WRITE(lp) SMC_32BIT(lp)
+
+#define SMC_GET_PN(lp) \
+ (SMC_8BIT(lp) ? (SMC_inb(ioaddr, PN_REG(lp))) \
+ : (SMC_inw(ioaddr, PN_REG(lp)) & 0xFF))
+
+#define SMC_SET_PN(lp, x) \
+ do { \
+ if (SMC_MUST_ALIGN_WRITE(lp)) \
+ SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 0, 2)); \
+ else if (SMC_8BIT(lp)) \
+ SMC_outb(x, ioaddr, PN_REG(lp)); \
+ else \
+ SMC_outw(x, ioaddr, PN_REG(lp)); \
+ } while (0)
+
+#define SMC_GET_AR(lp) \
+ (SMC_8BIT(lp) ? (SMC_inb(ioaddr, AR_REG(lp))) \
+ : (SMC_inw(ioaddr, PN_REG(lp)) >> 8))
+
+#define SMC_GET_TXFIFO(lp) \
+ (SMC_8BIT(lp) ? (SMC_inb(ioaddr, TXFIFO_REG(lp))) \
+ : (SMC_inw(ioaddr, TXFIFO_REG(lp)) & 0xFF))
+
+#define SMC_GET_RXFIFO(lp) \
+ (SMC_8BIT(lp) ? (SMC_inb(ioaddr, RXFIFO_REG(lp))) \
+ : (SMC_inw(ioaddr, TXFIFO_REG(lp)) >> 8))
+
+#define SMC_GET_INT(lp) \
+ (SMC_8BIT(lp) ? (SMC_inb(ioaddr, INT_REG(lp))) \
+ : (SMC_inw(ioaddr, INT_REG(lp)) & 0xFF))
+
+#define SMC_ACK_INT(lp, x) \
+ do { \
+ if (SMC_8BIT(lp)) \
+ SMC_outb(x, ioaddr, INT_REG(lp)); \
+ else { \
+ unsigned long __flags; \
+ int __mask; \
+ local_irq_save(__flags); \
+ __mask = SMC_inw(ioaddr, INT_REG(lp)) & ~0xff; \
+ SMC_outw(__mask | (x), ioaddr, INT_REG(lp)); \
+ local_irq_restore(__flags); \
+ } \
+ } while (0)
+
+#define SMC_GET_INT_MASK(lp) \
+ (SMC_8BIT(lp) ? (SMC_inb(ioaddr, IM_REG(lp))) \
+ : (SMC_inw(ioaddr, INT_REG(lp)) >> 8))
+
+#define SMC_SET_INT_MASK(lp, x) \
+ do { \
+ if (SMC_8BIT(lp)) \
+ SMC_outb(x, ioaddr, IM_REG(lp)); \
+ else \
+ SMC_outw((x) << 8, ioaddr, INT_REG(lp)); \
+ } while (0)
+
+#define SMC_CURRENT_BANK(lp) SMC_inw(ioaddr, BANK_SELECT)
+
+#define SMC_SELECT_BANK(lp, x) \
+ do { \
+ if (SMC_MUST_ALIGN_WRITE(lp)) \
+ SMC_outl((x)<<16, ioaddr, 12<<SMC_IO_SHIFT); \
+ else \
+ SMC_outw(x, ioaddr, BANK_SELECT); \
+ } while (0)
+
+#define SMC_GET_BASE(lp) SMC_inw(ioaddr, BASE_REG(lp))
+
+#define SMC_SET_BASE(lp, x) SMC_outw(x, ioaddr, BASE_REG(lp))
+
+#define SMC_GET_CONFIG(lp) SMC_inw(ioaddr, CONFIG_REG(lp))
+
+#define SMC_SET_CONFIG(lp, x) SMC_outw(x, ioaddr, CONFIG_REG(lp))
+
+#define SMC_GET_COUNTER(lp) SMC_inw(ioaddr, COUNTER_REG(lp))
+
+#define SMC_GET_CTL(lp) SMC_inw(ioaddr, CTL_REG(lp))
+
+#define SMC_SET_CTL(lp, x) SMC_outw(x, ioaddr, CTL_REG(lp))
+
+#define SMC_GET_MII(lp) SMC_inw(ioaddr, MII_REG(lp))
+
+#define SMC_GET_GP(lp) SMC_inw(ioaddr, GP_REG(lp))
+
+#define SMC_SET_GP(lp, x) \
+ do { \
+ if (SMC_MUST_ALIGN_WRITE(lp)) \
+ SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 8, 1)); \
+ else \
+ SMC_outw(x, ioaddr, GP_REG(lp)); \
+ } while (0)
+
+#define SMC_SET_MII(lp, x) SMC_outw(x, ioaddr, MII_REG(lp))
+
+#define SMC_GET_MIR(lp) SMC_inw(ioaddr, MIR_REG(lp))
+
+#define SMC_SET_MIR(lp, x) SMC_outw(x, ioaddr, MIR_REG(lp))
+
+#define SMC_GET_MMU_CMD(lp) SMC_inw(ioaddr, MMU_CMD_REG(lp))
+
+#define SMC_SET_MMU_CMD(lp, x) SMC_outw(x, ioaddr, MMU_CMD_REG(lp))
+
+#define SMC_GET_FIFO(lp) SMC_inw(ioaddr, FIFO_REG(lp))
+
+#define SMC_GET_PTR(lp) SMC_inw(ioaddr, PTR_REG(lp))
+
+#define SMC_SET_PTR(lp, x) \
+ do { \
+ if (SMC_MUST_ALIGN_WRITE(lp)) \
+ SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 4, 2)); \
+ else \
+ SMC_outw(x, ioaddr, PTR_REG(lp)); \
+ } while (0)
+
+#define SMC_GET_EPH_STATUS(lp) SMC_inw(ioaddr, EPH_STATUS_REG(lp))
+
+#define SMC_GET_RCR(lp) SMC_inw(ioaddr, RCR_REG(lp))
+
+#define SMC_SET_RCR(lp, x) SMC_outw(x, ioaddr, RCR_REG(lp))
+
+#define SMC_GET_REV(lp) SMC_inw(ioaddr, REV_REG(lp))
+
+#define SMC_GET_RPC(lp) SMC_inw(ioaddr, RPC_REG(lp))
+
+#define SMC_SET_RPC(lp, x) \
+ do { \
+ if (SMC_MUST_ALIGN_WRITE(lp)) \
+ SMC_outl((x)<<16, ioaddr, SMC_REG(lp, 8, 0)); \
+ else \
+ SMC_outw(x, ioaddr, RPC_REG(lp)); \
+ } while (0)
+
+#define SMC_GET_TCR(lp) SMC_inw(ioaddr, TCR_REG(lp))
+
+#define SMC_SET_TCR(lp, x) SMC_outw(x, ioaddr, TCR_REG(lp))
+
+#ifndef SMC_GET_MAC_ADDR
+#define SMC_GET_MAC_ADDR(lp, addr) \
+ do { \
+ unsigned int __v; \
+ __v = SMC_inw(ioaddr, ADDR0_REG(lp)); \
+ addr[0] = __v; addr[1] = __v >> 8; \
+ __v = SMC_inw(ioaddr, ADDR1_REG(lp)); \
+ addr[2] = __v; addr[3] = __v >> 8; \
+ __v = SMC_inw(ioaddr, ADDR2_REG(lp)); \
+ addr[4] = __v; addr[5] = __v >> 8; \
+ } while (0)
+#endif
+
+#define SMC_SET_MAC_ADDR(lp, addr) \
+ do { \
+ SMC_outw(addr[0]|(addr[1] << 8), ioaddr, ADDR0_REG(lp)); \
+ SMC_outw(addr[2]|(addr[3] << 8), ioaddr, ADDR1_REG(lp)); \
+ SMC_outw(addr[4]|(addr[5] << 8), ioaddr, ADDR2_REG(lp)); \
+ } while (0)
+
+#define SMC_SET_MCAST(lp, x) \
+ do { \
+ const unsigned char *mt = (x); \
+ SMC_outw(mt[0] | (mt[1] << 8), ioaddr, MCAST_REG1(lp)); \
+ SMC_outw(mt[2] | (mt[3] << 8), ioaddr, MCAST_REG2(lp)); \
+ SMC_outw(mt[4] | (mt[5] << 8), ioaddr, MCAST_REG3(lp)); \
+ SMC_outw(mt[6] | (mt[7] << 8), ioaddr, MCAST_REG4(lp)); \
+ } while (0)
+
+#define SMC_PUT_PKT_HDR(lp, status, length) \
+ do { \
+ if (SMC_32BIT(lp)) \
+ SMC_outl((status) | (length)<<16, ioaddr, \
+ DATA_REG(lp)); \
+ else { \
+ SMC_outw(status, ioaddr, DATA_REG(lp)); \
+ SMC_outw(length, ioaddr, DATA_REG(lp)); \
+ } \
+ } while (0)
+
+#define SMC_GET_PKT_HDR(lp, status, length) \
+ do { \
+ if (SMC_32BIT(lp)) { \
+ unsigned int __val = SMC_inl(ioaddr, DATA_REG(lp)); \
+ (status) = __val & 0xffff; \
+ (length) = __val >> 16; \
+ } else { \
+ (status) = SMC_inw(ioaddr, DATA_REG(lp)); \
+ (length) = SMC_inw(ioaddr, DATA_REG(lp)); \
+ } \
+ } while (0)
+
+#define SMC_PUSH_DATA(lp, p, l) \
+ do { \
+ if (SMC_32BIT(lp)) { \
+ void *__ptr = (p); \
+ int __len = (l); \
+ void __iomem *__ioaddr = ioaddr; \
+ if (__len >= 2 && (unsigned long)__ptr & 2) { \
+ __len -= 2; \
+ SMC_outw(*(u16 *)__ptr, ioaddr, \
+ DATA_REG(lp)); \
+ __ptr += 2; \
+ } \
+ if (SMC_CAN_USE_DATACS && lp->datacs) \
+ __ioaddr = lp->datacs; \
+ SMC_outsl(__ioaddr, DATA_REG(lp), __ptr, __len>>2); \
+ if (__len & 2) { \
+ __ptr += (__len & ~3); \
+ SMC_outw(*((u16 *)__ptr), ioaddr, \
+ DATA_REG(lp)); \
+ } \
+ } else if (SMC_16BIT(lp)) \
+ SMC_outsw(ioaddr, DATA_REG(lp), p, (l) >> 1); \
+ else if (SMC_8BIT(lp)) \
+ SMC_outsb(ioaddr, DATA_REG(lp), p, l); \
+ } while (0)
+
+#define SMC_PULL_DATA(lp, p, l) \
+ do { \
+ if (SMC_32BIT(lp)) { \
+ void *__ptr = (p); \
+ int __len = (l); \
+ void __iomem *__ioaddr = ioaddr; \
+ if ((unsigned long)__ptr & 2) { \
+ /* \
+ * We want 32bit alignment here. \
+ * Since some buses perform a full \
+ * 32bit fetch even for 16bit data \
+ * we can't use SMC_inw() here. \
+ * Back both source (on-chip) and \
+ * destination pointers of 2 bytes. \
+ * This is possible since the call to \
+ * SMC_GET_PKT_HDR() already advanced \
+ * the source pointer of 4 bytes, and \
+ * the skb_reserve(skb, 2) advanced \
+ * the destination pointer of 2 bytes. \
+ */ \
+ __ptr -= 2; \
+ __len += 2; \
+ SMC_SET_PTR(lp, \
+ 2|PTR_READ|PTR_RCV|PTR_AUTOINC); \
+ } \
+ if (SMC_CAN_USE_DATACS && lp->datacs) \
+ __ioaddr = lp->datacs; \
+ __len += 2; \
+ SMC_insl(__ioaddr, DATA_REG(lp), __ptr, __len>>2); \
+ } else if (SMC_16BIT(lp)) \
+ SMC_insw(ioaddr, DATA_REG(lp), p, (l) >> 1); \
+ else if (SMC_8BIT(lp)) \
+ SMC_insb(ioaddr, DATA_REG(lp), p, l); \
+ } while (0)
+
+#endif /* _SMC91X_H_ */
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
new file mode 100644
index 000000000000..75c08a55582c
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -0,0 +1,2404 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2008 SMSC
+ * Copyright (C) 2005-2008 ARM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************
+ * Rewritten, heavily based on smsc911x simple driver by SMSC.
+ * Partly uses io macros from smc91x.c by Nicolas Pitre
+ *
+ * Supported devices:
+ * LAN9115, LAN9116, LAN9117, LAN9118
+ * LAN9215, LAN9216, LAN9217, LAN9218
+ * LAN9210, LAN9211
+ * LAN9220, LAN9221
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/bug.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/swab.h>
+#include <linux/phy.h>
+#include <linux/smsc911x.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_net.h>
+#include "smsc911x.h"
+
+#define SMSC_CHIPNAME "smsc911x"
+#define SMSC_MDIONAME "smsc911x-mdio"
+#define SMSC_DRV_VERSION "2008-10-21"
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(SMSC_DRV_VERSION);
+MODULE_ALIAS("platform:smsc911x");
+
+#if USE_DEBUG > 0
+static int debug = 16;
+#else
+static int debug = 3;
+#endif
+
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+struct smsc911x_data;
+
+struct smsc911x_ops {
+ u32 (*reg_read)(struct smsc911x_data *pdata, u32 reg);
+ void (*reg_write)(struct smsc911x_data *pdata, u32 reg, u32 val);
+ void (*rx_readfifo)(struct smsc911x_data *pdata,
+ unsigned int *buf, unsigned int wordcount);
+ void (*tx_writefifo)(struct smsc911x_data *pdata,
+ unsigned int *buf, unsigned int wordcount);
+};
+
+struct smsc911x_data {
+ void __iomem *ioaddr;
+
+ unsigned int idrev;
+
+ /* used to decide which workarounds apply */
+ unsigned int generation;
+
+ /* device configuration (copied from platform_data during probe) */
+ struct smsc911x_platform_config config;
+
+ /* This needs to be acquired before calling any of below:
+ * smsc911x_mac_read(), smsc911x_mac_write()
+ */
+ spinlock_t mac_lock;
+
+ /* spinlock to ensure register accesses are serialised */
+ spinlock_t dev_lock;
+
+ struct phy_device *phy_dev;
+ struct mii_bus *mii_bus;
+ int phy_irq[PHY_MAX_ADDR];
+ unsigned int using_extphy;
+ int last_duplex;
+ int last_carrier;
+
+ u32 msg_enable;
+ unsigned int gpio_setting;
+ unsigned int gpio_orig_setting;
+ struct net_device *dev;
+ struct napi_struct napi;
+
+ unsigned int software_irq_signal;
+
+#ifdef USE_PHY_WORK_AROUND
+#define MIN_PACKET_SIZE (64)
+ char loopback_tx_pkt[MIN_PACKET_SIZE];
+ char loopback_rx_pkt[MIN_PACKET_SIZE];
+ unsigned int resetcount;
+#endif
+
+ /* Members for Multicast filter workaround */
+ unsigned int multicast_update_pending;
+ unsigned int set_bits_mask;
+ unsigned int clear_bits_mask;
+ unsigned int hashhi;
+ unsigned int hashlo;
+
+ /* register access functions */
+ const struct smsc911x_ops *ops;
+};
+
+/* Easy access to information */
+#define __smsc_shift(pdata, reg) ((reg) << ((pdata)->config.shift))
+
+static inline u32 __smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT)
+ return readl(pdata->ioaddr + reg);
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT)
+ return ((readw(pdata->ioaddr + reg) & 0xFFFF) |
+ ((readw(pdata->ioaddr + reg + 2) & 0xFFFF) << 16));
+
+ BUG();
+ return 0;
+}
+
+static inline u32
+__smsc911x_reg_read_shift(struct smsc911x_data *pdata, u32 reg)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT)
+ return readl(pdata->ioaddr + __smsc_shift(pdata, reg));
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT)
+ return (readw(pdata->ioaddr +
+ __smsc_shift(pdata, reg)) & 0xFFFF) |
+ ((readw(pdata->ioaddr +
+ __smsc_shift(pdata, reg + 2)) & 0xFFFF) << 16);
+
+ BUG();
+ return 0;
+}
+
+static inline u32 smsc911x_reg_read(struct smsc911x_data *pdata, u32 reg)
+{
+ u32 data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+ data = pdata->ops->reg_read(pdata, reg);
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+
+ return data;
+}
+
+static inline void __smsc911x_reg_write(struct smsc911x_data *pdata, u32 reg,
+ u32 val)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ writel(val, pdata->ioaddr + reg);
+ return;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ writew(val & 0xFFFF, pdata->ioaddr + reg);
+ writew((val >> 16) & 0xFFFF, pdata->ioaddr + reg + 2);
+ return;
+ }
+
+ BUG();
+}
+
+static inline void
+__smsc911x_reg_write_shift(struct smsc911x_data *pdata, u32 reg, u32 val)
+{
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ writel(val, pdata->ioaddr + __smsc_shift(pdata, reg));
+ return;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ writew(val & 0xFFFF,
+ pdata->ioaddr + __smsc_shift(pdata, reg));
+ writew((val >> 16) & 0xFFFF,
+ pdata->ioaddr + __smsc_shift(pdata, reg + 2));
+ return;
+ }
+
+ BUG();
+}
+
+static inline void smsc911x_reg_write(struct smsc911x_data *pdata, u32 reg,
+ u32 val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+ pdata->ops->reg_write(pdata, reg, val);
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Writes a packet to the TX_DATA_FIFO */
+static inline void
+smsc911x_tx_writefifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+
+ if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+ while (wordcount--)
+ __smsc911x_reg_write(pdata, TX_DATA_FIFO,
+ swab32(*buf++));
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ writesl(pdata->ioaddr + TX_DATA_FIFO, buf, wordcount);
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ while (wordcount--)
+ __smsc911x_reg_write(pdata, TX_DATA_FIFO, *buf++);
+ goto out;
+ }
+
+ BUG();
+out:
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Writes a packet to the TX_DATA_FIFO - shifted version */
+static inline void
+smsc911x_tx_writefifo_shift(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+
+ if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+ while (wordcount--)
+ __smsc911x_reg_write_shift(pdata, TX_DATA_FIFO,
+ swab32(*buf++));
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ writesl(pdata->ioaddr + __smsc_shift(pdata,
+ TX_DATA_FIFO), buf, wordcount);
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ while (wordcount--)
+ __smsc911x_reg_write_shift(pdata,
+ TX_DATA_FIFO, *buf++);
+ goto out;
+ }
+
+ BUG();
+out:
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO */
+static inline void
+smsc911x_rx_readfifo(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+
+ if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+ while (wordcount--)
+ *buf++ = swab32(__smsc911x_reg_read(pdata,
+ RX_DATA_FIFO));
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ readsl(pdata->ioaddr + RX_DATA_FIFO, buf, wordcount);
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ while (wordcount--)
+ *buf++ = __smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ goto out;
+ }
+
+ BUG();
+out:
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* Reads a packet out of the RX_DATA_FIFO - shifted version */
+static inline void
+smsc911x_rx_readfifo_shift(struct smsc911x_data *pdata, unsigned int *buf,
+ unsigned int wordcount)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->dev_lock, flags);
+
+ if (pdata->config.flags & SMSC911X_SWAP_FIFO) {
+ while (wordcount--)
+ *buf++ = swab32(__smsc911x_reg_read_shift(pdata,
+ RX_DATA_FIFO));
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_32BIT) {
+ readsl(pdata->ioaddr + __smsc_shift(pdata,
+ RX_DATA_FIFO), buf, wordcount);
+ goto out;
+ }
+
+ if (pdata->config.flags & SMSC911X_USE_16BIT) {
+ while (wordcount--)
+ *buf++ = __smsc911x_reg_read_shift(pdata,
+ RX_DATA_FIFO);
+ goto out;
+ }
+
+ BUG();
+out:
+ spin_unlock_irqrestore(&pdata->dev_lock, flags);
+}
+
+/* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read
+ * and smsc911x_mac_write, so assumes mac_lock is held */
+static int smsc911x_mac_complete(struct smsc911x_data *pdata)
+{
+ int i;
+ u32 val;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ for (i = 0; i < 40; i++) {
+ val = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (!(val & MAC_CSR_CMD_CSR_BUSY_))
+ return 0;
+ }
+ SMSC_WARN(pdata, hw, "Timed out waiting for MAC not BUSY. "
+ "MAC_CSR_CMD: 0x%08X", val);
+ return -EIO;
+}
+
+/* Fetches a MAC register value. Assumes mac_lock is acquired */
+static u32 smsc911x_mac_read(struct smsc911x_data *pdata, unsigned int offset)
+{
+ unsigned int temp;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARN(pdata, hw, "MAC busy at entry");
+ return 0xFFFFFFFF;
+ }
+
+ /* Send the MAC cmd */
+ smsc911x_reg_write(pdata, MAC_CSR_CMD, ((offset & 0xFF) |
+ MAC_CSR_CMD_CSR_BUSY_ | MAC_CSR_CMD_R_NOT_W_));
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the read to complete */
+ if (likely(smsc911x_mac_complete(pdata) == 0))
+ return smsc911x_reg_read(pdata, MAC_CSR_DATA);
+
+ SMSC_WARN(pdata, hw, "MAC busy after read");
+ return 0xFFFFFFFF;
+}
+
+/* Set a mac register, mac_lock must be acquired before calling */
+static void smsc911x_mac_write(struct smsc911x_data *pdata,
+ unsigned int offset, u32 val)
+{
+ unsigned int temp;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ temp = smsc911x_reg_read(pdata, MAC_CSR_CMD);
+ if (unlikely(temp & MAC_CSR_CMD_CSR_BUSY_)) {
+ SMSC_WARN(pdata, hw,
+ "smsc911x_mac_write failed, MAC busy at entry");
+ return;
+ }
+
+ /* Send data to write */
+ smsc911x_reg_write(pdata, MAC_CSR_DATA, val);
+
+ /* Write the actual data */
+ smsc911x_reg_write(pdata, MAC_CSR_CMD, ((offset & 0xFF) |
+ MAC_CSR_CMD_CSR_BUSY_));
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ /* Wait for the write to complete */
+ if (likely(smsc911x_mac_complete(pdata) == 0))
+ return;
+
+ SMSC_WARN(pdata, hw, "smsc911x_mac_write failed, MAC busy after write");
+}
+
+/* Get a phy register */
+static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
+{
+ struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv;
+ unsigned long flags;
+ unsigned int addr;
+ int i, reg;
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARN(pdata, hw, "MII is busy in smsc911x_mii_read???");
+ reg = -EIO;
+ goto out;
+ }
+
+ /* Set the address, index & direction (read from PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6);
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for read to complete w/ timeout */
+ for (i = 0; i < 100; i++)
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ reg = smsc911x_mac_read(pdata, MII_DATA);
+ goto out;
+ }
+
+ SMSC_WARN(pdata, hw, "Timed out waiting for MII read to finish");
+ reg = -EIO;
+
+out:
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+ return reg;
+}
+
+/* Set a phy register */
+static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
+ u16 val)
+{
+ struct smsc911x_data *pdata = (struct smsc911x_data *)bus->priv;
+ unsigned long flags;
+ unsigned int addr;
+ int i, reg;
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+
+ /* Confirm MII not busy */
+ if (unlikely(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ SMSC_WARN(pdata, hw, "MII is busy in smsc911x_mii_write???");
+ reg = -EIO;
+ goto out;
+ }
+
+ /* Put the data to write in the MAC */
+ smsc911x_mac_write(pdata, MII_DATA, val);
+
+ /* Set the address, index & direction (write to PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+ MII_ACC_MII_WRITE_;
+ smsc911x_mac_write(pdata, MII_ACC, addr);
+
+ /* Wait for write to complete w/ timeout */
+ for (i = 0; i < 100; i++)
+ if (!(smsc911x_mac_read(pdata, MII_ACC) & MII_ACC_MII_BUSY_)) {
+ reg = 0;
+ goto out;
+ }
+
+ SMSC_WARN(pdata, hw, "Timed out waiting for MII write to finish");
+ reg = -EIO;
+
+out:
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+ return reg;
+}
+
+/* Switch to external phy. Assumes tx and rx are stopped. */
+static void smsc911x_phy_enable_external(struct smsc911x_data *pdata)
+{
+ unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+ /* Disable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_CLK_DIS_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+ udelay(10); /* Enough time for clocks to stop */
+
+ /* Switch to external phy */
+ hwcfg |= HW_CFG_EXT_PHY_EN_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+
+ /* Enable phy clocks to the MAC */
+ hwcfg &= (~HW_CFG_PHY_CLK_SEL_);
+ hwcfg |= HW_CFG_PHY_CLK_SEL_EXT_PHY_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+ udelay(10); /* Enough time for clocks to restart */
+
+ hwcfg |= HW_CFG_SMI_SEL_;
+ smsc911x_reg_write(pdata, HW_CFG, hwcfg);
+}
+
+/* Autodetects and enables external phy if present on supported chips.
+ * autodetection can be overridden by specifying SMSC911X_FORCE_INTERNAL_PHY
+ * or SMSC911X_FORCE_EXTERNAL_PHY in the platform_data flags. */
+static void smsc911x_phy_initialise_external(struct smsc911x_data *pdata)
+{
+ unsigned int hwcfg = smsc911x_reg_read(pdata, HW_CFG);
+
+ if (pdata->config.flags & SMSC911X_FORCE_INTERNAL_PHY) {
+ SMSC_TRACE(pdata, hw, "Forcing internal PHY");
+ pdata->using_extphy = 0;
+ } else if (pdata->config.flags & SMSC911X_FORCE_EXTERNAL_PHY) {
+ SMSC_TRACE(pdata, hw, "Forcing external PHY");
+ smsc911x_phy_enable_external(pdata);
+ pdata->using_extphy = 1;
+ } else if (hwcfg & HW_CFG_EXT_PHY_DET_) {
+ SMSC_TRACE(pdata, hw,
+ "HW_CFG EXT_PHY_DET set, using external PHY");
+ smsc911x_phy_enable_external(pdata);
+ pdata->using_extphy = 1;
+ } else {
+ SMSC_TRACE(pdata, hw,
+ "HW_CFG EXT_PHY_DET clear, using internal PHY");
+ pdata->using_extphy = 0;
+ }
+}
+
+/* Fetches a tx status out of the status fifo */
+static unsigned int smsc911x_tx_get_txstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, TX_STATUS_FIFO);
+
+ return result;
+}
+
+/* Fetches the next rx status */
+static unsigned int smsc911x_rx_get_rxstatus(struct smsc911x_data *pdata)
+{
+ unsigned int result =
+ smsc911x_reg_read(pdata, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED_;
+
+ if (result != 0)
+ result = smsc911x_reg_read(pdata, RX_STATUS_FIFO);
+
+ return result;
+}
+
+#ifdef USE_PHY_WORK_AROUND
+static int smsc911x_phy_check_loopbackpkt(struct smsc911x_data *pdata)
+{
+ unsigned int tries;
+ u32 wrsz;
+ u32 rdsz;
+ ulong bufp;
+
+ for (tries = 0; tries < 10; tries++) {
+ unsigned int txcmd_a;
+ unsigned int txcmd_b;
+ unsigned int status;
+ unsigned int pktlength;
+ unsigned int i;
+
+ /* Zero-out rx packet memory */
+ memset(pdata->loopback_rx_pkt, 0, MIN_PACKET_SIZE);
+
+ /* Write tx packet to 118 */
+ txcmd_a = (u32)((ulong)pdata->loopback_tx_pkt & 0x03) << 16;
+ txcmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ txcmd_a |= MIN_PACKET_SIZE;
+
+ txcmd_b = MIN_PACKET_SIZE << 16 | MIN_PACKET_SIZE;
+
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, txcmd_a);
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, txcmd_b);
+
+ bufp = (ulong)pdata->loopback_tx_pkt & (~0x3);
+ wrsz = MIN_PACKET_SIZE + 3;
+ wrsz += (u32)((ulong)pdata->loopback_tx_pkt & 0x3);
+ wrsz >>= 2;
+
+ pdata->ops->tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+
+ /* Wait till transmit is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_tx_get_txstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARN(pdata, hw,
+ "Failed to transmit during loopback test");
+ continue;
+ }
+ if (status & TX_STS_ES_) {
+ SMSC_WARN(pdata, hw,
+ "Transmit encountered errors during loopback test");
+ continue;
+ }
+
+ /* Wait till receive is done */
+ i = 60;
+ do {
+ udelay(5);
+ status = smsc911x_rx_get_rxstatus(pdata);
+ } while ((i--) && (!status));
+
+ if (!status) {
+ SMSC_WARN(pdata, hw,
+ "Failed to receive during loopback test");
+ continue;
+ }
+ if (status & RX_STS_ES_) {
+ SMSC_WARN(pdata, hw,
+ "Receive encountered errors during loopback test");
+ continue;
+ }
+
+ pktlength = ((status & 0x3FFF0000UL) >> 16);
+ bufp = (ulong)pdata->loopback_rx_pkt;
+ rdsz = pktlength + 3;
+ rdsz += (u32)((ulong)pdata->loopback_rx_pkt & 0x3);
+ rdsz >>= 2;
+
+ pdata->ops->rx_readfifo(pdata, (unsigned int *)bufp, rdsz);
+
+ if (pktlength != (MIN_PACKET_SIZE + 4)) {
+ SMSC_WARN(pdata, hw, "Unexpected packet size "
+ "during loop back test, size=%d, will retry",
+ pktlength);
+ } else {
+ unsigned int j;
+ int mismatch = 0;
+ for (j = 0; j < MIN_PACKET_SIZE; j++) {
+ if (pdata->loopback_tx_pkt[j]
+ != pdata->loopback_rx_pkt[j]) {
+ mismatch = 1;
+ break;
+ }
+ }
+ if (!mismatch) {
+ SMSC_TRACE(pdata, hw, "Successfully verified "
+ "loopback packet");
+ return 0;
+ } else {
+ SMSC_WARN(pdata, hw, "Data mismatch "
+ "during loop back test, will retry");
+ }
+ }
+ }
+
+ return -EIO;
+}
+
+static int smsc911x_phy_reset(struct smsc911x_data *pdata)
+{
+ struct phy_device *phy_dev = pdata->phy_dev;
+ unsigned int temp;
+ unsigned int i = 100000;
+
+ BUG_ON(!phy_dev);
+ BUG_ON(!phy_dev->bus);
+
+ SMSC_TRACE(pdata, hw, "Performing PHY BCR Reset");
+ smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, BMCR_RESET);
+ do {
+ msleep(1);
+ temp = smsc911x_mii_read(phy_dev->bus, phy_dev->addr,
+ MII_BMCR);
+ } while ((i--) && (temp & BMCR_RESET));
+
+ if (temp & BMCR_RESET) {
+ SMSC_WARN(pdata, hw, "PHY reset failed to complete");
+ return -EIO;
+ }
+ /* Extra delay required because the phy may not be completed with
+ * its reset when BMCR_RESET is cleared. Specs say 256 uS is
+ * enough delay but using 1ms here to be safe */
+ msleep(1);
+
+ return 0;
+}
+
+static int smsc911x_phy_loopbacktest(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phy_dev = pdata->phy_dev;
+ int result = -EIO;
+ unsigned int i, val;
+ unsigned long flags;
+
+ /* Initialise tx packet using broadcast destination address */
+ memset(pdata->loopback_tx_pkt, 0xff, ETH_ALEN);
+
+ /* Use incrementing source address */
+ for (i = 6; i < 12; i++)
+ pdata->loopback_tx_pkt[i] = (char)i;
+
+ /* Set length type field */
+ pdata->loopback_tx_pkt[12] = 0x00;
+ pdata->loopback_tx_pkt[13] = 0x00;
+
+ for (i = 14; i < MIN_PACKET_SIZE; i++)
+ pdata->loopback_tx_pkt[i] = (char)i;
+
+ val = smsc911x_reg_read(pdata, HW_CFG);
+ val &= HW_CFG_TX_FIF_SZ_;
+ val |= HW_CFG_SF_;
+ smsc911x_reg_write(pdata, HW_CFG, val);
+
+ smsc911x_reg_write(pdata, TX_CFG, TX_CFG_TX_ON_);
+ smsc911x_reg_write(pdata, RX_CFG,
+ (u32)((ulong)pdata->loopback_rx_pkt & 0x03) << 8);
+
+ for (i = 0; i < 10; i++) {
+ /* Set PHY to 10/FD, no ANEG, and loopback mode */
+ smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR,
+ BMCR_LOOPBACK | BMCR_FULLDPLX);
+
+ /* Enable MAC tx/rx, FD */
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, MAC_CR_FDPX_
+ | MAC_CR_TXEN_ | MAC_CR_RXEN_);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ if (smsc911x_phy_check_loopbackpkt(pdata) == 0) {
+ result = 0;
+ break;
+ }
+ pdata->resetcount++;
+
+ /* Disable MAC rx */
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ smsc911x_phy_reset(pdata);
+ }
+
+ /* Disable MAC */
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, MAC_CR, 0);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ /* Cancel PHY loopback mode */
+ smsc911x_mii_write(phy_dev->bus, phy_dev->addr, MII_BMCR, 0);
+
+ smsc911x_reg_write(pdata, TX_CFG, 0);
+ smsc911x_reg_write(pdata, RX_CFG, 0);
+
+ return result;
+}
+#endif /* USE_PHY_WORK_AROUND */
+
+static void smsc911x_phy_update_flowcontrol(struct smsc911x_data *pdata)
+{
+ struct phy_device *phy_dev = pdata->phy_dev;
+ u32 afc = smsc911x_reg_read(pdata, AFC_CFG);
+ u32 flow;
+ unsigned long flags;
+
+ if (phy_dev->duplex == DUPLEX_FULL) {
+ u16 lcladv = phy_read(phy_dev, MII_ADVERTISE);
+ u16 rmtadv = phy_read(phy_dev, MII_LPA);
+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+
+ if (cap & FLOW_CTRL_RX)
+ flow = 0xFFFF0002;
+ else
+ flow = 0;
+
+ if (cap & FLOW_CTRL_TX)
+ afc |= 0xF;
+ else
+ afc &= ~0xF;
+
+ SMSC_TRACE(pdata, hw, "rx pause %s, tx pause %s",
+ (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
+ (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
+ } else {
+ SMSC_TRACE(pdata, hw, "half duplex");
+ flow = 0;
+ afc |= 0xF;
+ }
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ smsc911x_mac_write(pdata, FLOW, flow);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ smsc911x_reg_write(pdata, AFC_CFG, afc);
+}
+
+/* Update link mode if anything has changed. Called periodically when the
+ * PHY is in polling mode, even if nothing has changed. */
+static void smsc911x_phy_adjust_link(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phy_dev = pdata->phy_dev;
+ unsigned long flags;
+ int carrier;
+
+ if (phy_dev->duplex != pdata->last_duplex) {
+ unsigned int mac_cr;
+ SMSC_TRACE(pdata, hw, "duplex state has changed");
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ if (phy_dev->duplex) {
+ SMSC_TRACE(pdata, hw,
+ "configuring for full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ SMSC_TRACE(pdata, hw,
+ "configuring for half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+
+ smsc911x_phy_update_flowcontrol(pdata);
+ pdata->last_duplex = phy_dev->duplex;
+ }
+
+ carrier = netif_carrier_ok(dev);
+ if (carrier != pdata->last_carrier) {
+ SMSC_TRACE(pdata, hw, "carrier state has changed");
+ if (carrier) {
+ SMSC_TRACE(pdata, hw, "configuring for carrier OK");
+ if ((pdata->gpio_orig_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
+ /* Restore original GPIO configuration */
+ pdata->gpio_setting = pdata->gpio_orig_setting;
+ smsc911x_reg_write(pdata, GPIO_CFG,
+ pdata->gpio_setting);
+ }
+ } else {
+ SMSC_TRACE(pdata, hw, "configuring for no carrier");
+ /* Check global setting that LED1
+ * usage is 10/100 indicator */
+ pdata->gpio_setting = smsc911x_reg_read(pdata,
+ GPIO_CFG);
+ if ((pdata->gpio_setting & GPIO_CFG_LED1_EN_) &&
+ (!pdata->using_extphy)) {
+ /* Force 10/100 LED off, after saving
+ * original GPIO configuration */
+ pdata->gpio_orig_setting = pdata->gpio_setting;
+
+ pdata->gpio_setting &= ~GPIO_CFG_LED1_EN_;
+ pdata->gpio_setting |= (GPIO_CFG_GPIOBUF0_
+ | GPIO_CFG_GPIODIR0_
+ | GPIO_CFG_GPIOD0_);
+ smsc911x_reg_write(pdata, GPIO_CFG,
+ pdata->gpio_setting);
+ }
+ }
+ pdata->last_carrier = carrier;
+ }
+}
+
+static int smsc911x_mii_probe(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+ int ret;
+
+ /* find the first phy */
+ phydev = phy_find_first(pdata->mii_bus);
+ if (!phydev) {
+ netdev_err(dev, "no PHY found\n");
+ return -ENODEV;
+ }
+
+ SMSC_TRACE(pdata, probe, "PHY: addr %d, phy_id 0x%08X",
+ phydev->addr, phydev->phy_id);
+
+ ret = phy_connect_direct(dev, phydev,
+ &smsc911x_phy_adjust_link, 0,
+ pdata->config.phy_interface);
+
+ if (ret) {
+ netdev_err(dev, "Could not attach to PHY\n");
+ return ret;
+ }
+
+ netdev_info(dev,
+ "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+
+ /* mask with MAC supported features */
+ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ phydev->advertising = phydev->supported;
+
+ pdata->phy_dev = phydev;
+ pdata->last_duplex = -1;
+ pdata->last_carrier = -1;
+
+#ifdef USE_PHY_WORK_AROUND
+ if (smsc911x_phy_loopbacktest(dev) < 0) {
+ SMSC_WARN(pdata, hw, "Failed Loop Back Test");
+ return -ENODEV;
+ }
+ SMSC_TRACE(pdata, hw, "Passed Loop Back Test");
+#endif /* USE_PHY_WORK_AROUND */
+
+ SMSC_TRACE(pdata, hw, "phy initialised successfully");
+ return 0;
+}
+
+static int __devinit smsc911x_mii_init(struct platform_device *pdev,
+ struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ int err = -ENXIO, i;
+
+ pdata->mii_bus = mdiobus_alloc();
+ if (!pdata->mii_bus) {
+ err = -ENOMEM;
+ goto err_out_1;
+ }
+
+ pdata->mii_bus->name = SMSC_MDIONAME;
+ snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id);
+ pdata->mii_bus->priv = pdata;
+ pdata->mii_bus->read = smsc911x_mii_read;
+ pdata->mii_bus->write = smsc911x_mii_write;
+ pdata->mii_bus->irq = pdata->phy_irq;
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ pdata->mii_bus->irq[i] = PHY_POLL;
+
+ pdata->mii_bus->parent = &pdev->dev;
+
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01170000:
+ case 0x01150000:
+ case 0x117A0000:
+ case 0x115A0000:
+ /* External PHY supported, try to autodetect */
+ smsc911x_phy_initialise_external(pdata);
+ break;
+ default:
+ SMSC_TRACE(pdata, hw, "External PHY is not supported, "
+ "using internal PHY");
+ pdata->using_extphy = 0;
+ break;
+ }
+
+ if (!pdata->using_extphy) {
+ /* Mask all PHYs except ID 1 (internal) */
+ pdata->mii_bus->phy_mask = ~(1 << 1);
+ }
+
+ if (mdiobus_register(pdata->mii_bus)) {
+ SMSC_WARN(pdata, probe, "Error registering mii bus");
+ goto err_out_free_bus_2;
+ }
+
+ if (smsc911x_mii_probe(dev) < 0) {
+ SMSC_WARN(pdata, probe, "Error registering mii bus");
+ goto err_out_unregister_bus_3;
+ }
+
+ return 0;
+
+err_out_unregister_bus_3:
+ mdiobus_unregister(pdata->mii_bus);
+err_out_free_bus_2:
+ mdiobus_free(pdata->mii_bus);
+err_out_1:
+ return err;
+}
+
+/* Gets the number of tx statuses in the fifo */
+static unsigned int smsc911x_tx_get_txstatcount(struct smsc911x_data *pdata)
+{
+ return (smsc911x_reg_read(pdata, TX_FIFO_INF)
+ & TX_FIFO_INF_TSUSED_) >> 16;
+}
+
+/* Reads tx statuses and increments counters where necessary */
+static void smsc911x_tx_update_txcounters(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int tx_stat;
+
+ while ((tx_stat = smsc911x_tx_get_txstatus(pdata)) != 0) {
+ if (unlikely(tx_stat & 0x80000000)) {
+ /* In this driver the packet tag is used as the packet
+ * length. Since a packet length can never reach the
+ * size of 0x8000, this bit is reserved. It is worth
+ * noting that the "reserved bit" in the warning above
+ * does not reference a hardware defined reserved bit
+ * but rather a driver defined one.
+ */
+ SMSC_WARN(pdata, hw, "Packet tag reserved bit is high");
+ } else {
+ if (unlikely(tx_stat & TX_STS_ES_)) {
+ dev->stats.tx_errors++;
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += (tx_stat >> 16);
+ }
+ if (unlikely(tx_stat & TX_STS_EXCESS_COL_)) {
+ dev->stats.collisions += 16;
+ dev->stats.tx_aborted_errors += 1;
+ } else {
+ dev->stats.collisions +=
+ ((tx_stat >> 3) & 0xF);
+ }
+ if (unlikely(tx_stat & TX_STS_LOST_CARRIER_))
+ dev->stats.tx_carrier_errors += 1;
+ if (unlikely(tx_stat & TX_STS_LATE_COL_)) {
+ dev->stats.collisions++;
+ dev->stats.tx_aborted_errors++;
+ }
+ }
+ }
+}
+
+/* Increments the Rx error counters */
+static void
+smsc911x_rx_counterrors(struct net_device *dev, unsigned int rxstat)
+{
+ int crc_err = 0;
+
+ if (unlikely(rxstat & RX_STS_ES_)) {
+ dev->stats.rx_errors++;
+ if (unlikely(rxstat & RX_STS_CRC_ERR_)) {
+ dev->stats.rx_crc_errors++;
+ crc_err = 1;
+ }
+ }
+ if (likely(!crc_err)) {
+ if (unlikely((rxstat & RX_STS_FRAME_TYPE_) &&
+ (rxstat & RX_STS_LENGTH_ERR_)))
+ dev->stats.rx_length_errors++;
+ if (rxstat & RX_STS_MCAST_)
+ dev->stats.multicast++;
+ }
+}
+
+/* Quickly dumps bad packets */
+static void
+smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes)
+{
+ unsigned int pktwords = (pktbytes + NET_IP_ALIGN + 3) >> 2;
+
+ if (likely(pktwords >= 4)) {
+ unsigned int timeout = 500;
+ unsigned int val;
+ smsc911x_reg_write(pdata, RX_DP_CTRL, RX_DP_CTRL_RX_FFWD_);
+ do {
+ udelay(1);
+ val = smsc911x_reg_read(pdata, RX_DP_CTRL);
+ } while ((val & RX_DP_CTRL_RX_FFWD_) && --timeout);
+
+ if (unlikely(timeout == 0))
+ SMSC_WARN(pdata, hw, "Timed out waiting for "
+ "RX FFWD to finish, RX_DP_CTRL: 0x%08X", val);
+ } else {
+ unsigned int temp;
+ while (pktwords--)
+ temp = smsc911x_reg_read(pdata, RX_DATA_FIFO);
+ }
+}
+
+/* NAPI poll function */
+static int smsc911x_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc911x_data *pdata =
+ container_of(napi, struct smsc911x_data, napi);
+ struct net_device *dev = pdata->dev;
+ int npackets = 0;
+
+ while (npackets < budget) {
+ unsigned int pktlength;
+ unsigned int pktwords;
+ struct sk_buff *skb;
+ unsigned int rxstat = smsc911x_rx_get_rxstatus(pdata);
+
+ if (!rxstat) {
+ unsigned int temp;
+ /* We processed all packets available. Tell NAPI it can
+ * stop polling then re-enable rx interrupts */
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_RSFL_);
+ napi_complete(napi);
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_RSFL_EN_;
+ smsc911x_reg_write(pdata, INT_EN, temp);
+ break;
+ }
+
+ /* Count packet for NAPI scheduling, even if it has an error.
+ * Error packets still require cycles to discard */
+ npackets++;
+
+ pktlength = ((rxstat & 0x3FFF0000) >> 16);
+ pktwords = (pktlength + NET_IP_ALIGN + 3) >> 2;
+ smsc911x_rx_counterrors(dev, rxstat);
+
+ if (unlikely(rxstat & RX_STS_ES_)) {
+ SMSC_WARN(pdata, rx_err,
+ "Discarding packet with error bit set");
+ /* Packet has an error, discard it and continue with
+ * the next */
+ smsc911x_rx_fastforward(pdata, pktwords);
+ dev->stats.rx_dropped++;
+ continue;
+ }
+
+ skb = netdev_alloc_skb(dev, pktlength + NET_IP_ALIGN);
+ if (unlikely(!skb)) {
+ SMSC_WARN(pdata, rx_err,
+ "Unable to allocate skb for rx packet");
+ /* Drop the packet and stop this polling iteration */
+ smsc911x_rx_fastforward(pdata, pktwords);
+ dev->stats.rx_dropped++;
+ break;
+ }
+
+ skb->data = skb->head;
+ skb_reset_tail_pointer(skb);
+
+ /* Align IP on 16B boundary */
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, pktlength - 4);
+ pdata->ops->rx_readfifo(pdata,
+ (unsigned int *)skb->head, pktwords);
+ skb->protocol = eth_type_trans(skb, dev);
+ skb_checksum_none_assert(skb);
+ netif_receive_skb(skb);
+
+ /* Update counters */
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += (pktlength - 4);
+ }
+
+ /* Return total received packets */
+ return npackets;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static unsigned int smsc911x_hash(char addr[ETH_ALEN])
+{
+ return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f;
+}
+
+static void smsc911x_rx_multicast_update(struct smsc911x_data *pdata)
+{
+ /* Performs the multicast & mac_cr update. This is called when
+ * safe on the current hardware, and with the mac_lock held */
+ unsigned int mac_cr;
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= pdata->set_bits_mask;
+ mac_cr &= ~(pdata->clear_bits_mask);
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+ smsc911x_mac_write(pdata, HASHH, pdata->hashhi);
+ smsc911x_mac_write(pdata, HASHL, pdata->hashlo);
+ SMSC_TRACE(pdata, hw, "maccr 0x%08X, HASHH 0x%08X, HASHL 0x%08X",
+ mac_cr, pdata->hashhi, pdata->hashlo);
+}
+
+static void smsc911x_rx_multicast_update_workaround(struct smsc911x_data *pdata)
+{
+ unsigned int mac_cr;
+
+ /* This function is only called for older LAN911x devices
+ * (revA or revB), where MAC_CR, HASHH and HASHL should not
+ * be modified during Rx - newer devices immediately update the
+ * registers.
+ *
+ * This is called from interrupt context */
+
+ spin_lock(&pdata->mac_lock);
+
+ /* Check Rx has stopped */
+ if (smsc911x_mac_read(pdata, MAC_CR) & MAC_CR_RXEN_)
+ SMSC_WARN(pdata, drv, "Rx not stopped");
+
+ /* Perform the update - safe to do now Rx has stopped */
+ smsc911x_rx_multicast_update(pdata);
+
+ /* Re-enable Rx */
+ mac_cr = smsc911x_mac_read(pdata, MAC_CR);
+ mac_cr |= MAC_CR_RXEN_;
+ smsc911x_mac_write(pdata, MAC_CR, mac_cr);
+
+ pdata->multicast_update_pending = 0;
+
+ spin_unlock(&pdata->mac_lock);
+}
+
+static int smsc911x_soft_reset(struct smsc911x_data *pdata)
+{
+ unsigned int timeout;
+ unsigned int temp;
+
+ /* Reset the LAN911x */
+ smsc911x_reg_write(pdata, HW_CFG, HW_CFG_SRST_);
+ timeout = 10;
+ do {
+ udelay(10);
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ } while ((--timeout) && (temp & HW_CFG_SRST_));
+
+ if (unlikely(temp & HW_CFG_SRST_)) {
+ SMSC_WARN(pdata, drv, "Failed to complete reset");
+ return -EIO;
+ }
+ return 0;
+}
+
+/* Sets the device MAC address to dev_addr, called with mac_lock held */
+static void
+smsc911x_set_hw_mac_address(struct smsc911x_data *pdata, u8 dev_addr[6])
+{
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ SMSC_ASSERT_MAC_LOCK(pdata);
+
+ smsc911x_mac_write(pdata, ADDRH, mac_high16);
+ smsc911x_mac_write(pdata, ADDRL, mac_low32);
+}
+
+static int smsc911x_open(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int timeout;
+ unsigned int temp;
+ unsigned int intcfg;
+
+ /* if the phy is not yet registered, retry later*/
+ if (!pdata->phy_dev) {
+ SMSC_WARN(pdata, hw, "phy_dev is NULL");
+ return -EAGAIN;
+ }
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ SMSC_WARN(pdata, hw, "dev_addr is not a valid MAC address");
+ return -EADDRNOTAVAIL;
+ }
+
+ /* Reset the LAN911x */
+ if (smsc911x_soft_reset(pdata)) {
+ SMSC_WARN(pdata, hw, "soft reset failed");
+ return -EIO;
+ }
+
+ smsc911x_reg_write(pdata, HW_CFG, 0x00050000);
+ smsc911x_reg_write(pdata, AFC_CFG, 0x006E3740);
+
+ /* Increase the legal frame size of VLAN tagged frames to 1522 bytes */
+ spin_lock_irq(&pdata->mac_lock);
+ smsc911x_mac_write(pdata, VLAN1, ETH_P_8021Q);
+ spin_unlock_irq(&pdata->mac_lock);
+
+ /* Make sure EEPROM has finished loading before setting GPIO_CFG */
+ timeout = 50;
+ while ((smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) &&
+ --timeout) {
+ udelay(10);
+ }
+
+ if (unlikely(timeout == 0))
+ SMSC_WARN(pdata, ifup,
+ "Timed out waiting for EEPROM busy bit to clear");
+
+ smsc911x_reg_write(pdata, GPIO_CFG, 0x70070000);
+
+ /* The soft reset above cleared the device's MAC address,
+ * restore it from local copy (set in probe) */
+ spin_lock_irq(&pdata->mac_lock);
+ smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+ spin_unlock_irq(&pdata->mac_lock);
+
+ /* Initialise irqs, but leave all sources disabled */
+ smsc911x_reg_write(pdata, INT_EN, 0);
+ smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
+
+ /* Set interrupt deassertion to 100uS */
+ intcfg = ((10 << 24) | INT_CFG_IRQ_EN_);
+
+ if (pdata->config.irq_polarity) {
+ SMSC_TRACE(pdata, ifup, "irq polarity: active high");
+ intcfg |= INT_CFG_IRQ_POL_;
+ } else {
+ SMSC_TRACE(pdata, ifup, "irq polarity: active low");
+ }
+
+ if (pdata->config.irq_type) {
+ SMSC_TRACE(pdata, ifup, "irq type: push-pull");
+ intcfg |= INT_CFG_IRQ_TYPE_;
+ } else {
+ SMSC_TRACE(pdata, ifup, "irq type: open drain");
+ }
+
+ smsc911x_reg_write(pdata, INT_CFG, intcfg);
+
+ SMSC_TRACE(pdata, ifup, "Testing irq handler using IRQ %d", dev->irq);
+ pdata->software_irq_signal = 0;
+ smp_wmb();
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= INT_EN_SW_INT_EN_;
+ smsc911x_reg_write(pdata, INT_EN, temp);
+
+ timeout = 1000;
+ while (timeout--) {
+ if (pdata->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ if (!pdata->software_irq_signal) {
+ netdev_warn(dev, "ISR failed signaling test (IRQ %d)\n",
+ dev->irq);
+ return -ENODEV;
+ }
+ SMSC_TRACE(pdata, ifup, "IRQ handler passed test using IRQ %d",
+ dev->irq);
+
+ netdev_info(dev, "SMSC911x/921x identified at %#08lx, IRQ: %d\n",
+ (unsigned long)pdata->ioaddr, dev->irq);
+
+ /* Reset the last known duplex and carrier */
+ pdata->last_duplex = -1;
+ pdata->last_carrier = -1;
+
+ /* Bring the PHY up */
+ phy_start(pdata->phy_dev);
+
+ temp = smsc911x_reg_read(pdata, HW_CFG);
+ /* Preserve TX FIFO size and external PHY configuration */
+ temp &= (HW_CFG_TX_FIF_SZ_|0x00000FFF);
+ temp |= HW_CFG_SF_;
+ smsc911x_reg_write(pdata, HW_CFG, temp);
+
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ temp &= ~(FIFO_INT_RX_STS_LEVEL_);
+ smsc911x_reg_write(pdata, FIFO_INT, temp);
+
+ /* set RX Data offset to 2 bytes for alignment */
+ smsc911x_reg_write(pdata, RX_CFG, (2 << 8));
+
+ /* enable NAPI polling before enabling RX interrupts */
+ napi_enable(&pdata->napi);
+
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp |= (INT_EN_TDFA_EN_ | INT_EN_RSFL_EN_ | INT_EN_RXSTOP_INT_EN_);
+ smsc911x_reg_write(pdata, INT_EN, temp);
+
+ spin_lock_irq(&pdata->mac_lock);
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp |= (MAC_CR_TXEN_ | MAC_CR_RXEN_ | MAC_CR_HBDIS_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ spin_unlock_irq(&pdata->mac_lock);
+
+ smsc911x_reg_write(pdata, TX_CFG, TX_CFG_TX_ON_);
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Entry point for stopping the interface */
+static int smsc911x_stop(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int temp;
+
+ /* Disable all device interrupts */
+ temp = smsc911x_reg_read(pdata, INT_CFG);
+ temp &= ~INT_CFG_IRQ_EN_;
+ smsc911x_reg_write(pdata, INT_CFG, temp);
+
+ /* Stop Tx and Rx polling */
+ netif_stop_queue(dev);
+ napi_disable(&pdata->napi);
+
+ /* At this point all Rx and Tx activity is stopped */
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ smsc911x_tx_update_txcounters(dev);
+
+ /* Bring the PHY down */
+ if (pdata->phy_dev)
+ phy_stop(pdata->phy_dev);
+
+ SMSC_TRACE(pdata, ifdown, "Interface stopped");
+ return 0;
+}
+
+/* Entry point for transmitting a packet */
+static int smsc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int freespace;
+ unsigned int tx_cmd_a;
+ unsigned int tx_cmd_b;
+ unsigned int temp;
+ u32 wrsz;
+ ulong bufp;
+
+ freespace = smsc911x_reg_read(pdata, TX_FIFO_INF) & TX_FIFO_INF_TDFREE_;
+
+ if (unlikely(freespace < TX_FIFO_LOW_THRESHOLD))
+ SMSC_WARN(pdata, tx_err,
+ "Tx data fifo low, space available: %d", freespace);
+
+ /* Word alignment adjustment */
+ tx_cmd_a = (u32)((ulong)skb->data & 0x03) << 16;
+ tx_cmd_a |= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_;
+ tx_cmd_a |= (unsigned int)skb->len;
+
+ tx_cmd_b = ((unsigned int)skb->len) << 16;
+ tx_cmd_b |= (unsigned int)skb->len;
+
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_a);
+ smsc911x_reg_write(pdata, TX_DATA_FIFO, tx_cmd_b);
+
+ bufp = (ulong)skb->data & (~0x3);
+ wrsz = (u32)skb->len + 3;
+ wrsz += (u32)((ulong)skb->data & 0x3);
+ wrsz >>= 2;
+
+ pdata->ops->tx_writefifo(pdata, (unsigned int *)bufp, wrsz);
+ freespace -= (skb->len + 32);
+ skb_tx_timestamp(skb);
+ dev_kfree_skb(skb);
+
+ if (unlikely(smsc911x_tx_get_txstatcount(pdata) >= 30))
+ smsc911x_tx_update_txcounters(dev);
+
+ if (freespace < TX_FIFO_LOW_THRESHOLD) {
+ netif_stop_queue(dev);
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp &= 0x00FFFFFF;
+ temp |= 0x32000000;
+ smsc911x_reg_write(pdata, FIFO_INT, temp);
+ }
+
+ return NETDEV_TX_OK;
+}
+
+/* Entry point for getting status counters */
+static struct net_device_stats *smsc911x_get_stats(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ smsc911x_tx_update_txcounters(dev);
+ dev->stats.rx_dropped += smsc911x_reg_read(pdata, RX_DROP);
+ return &dev->stats;
+}
+
+/* Entry point for setting addressing modes */
+static void smsc911x_set_multicast_list(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned long flags;
+
+ if (dev->flags & IFF_PROMISC) {
+ /* Enabling promiscuous mode */
+ pdata->set_bits_mask = MAC_CR_PRMS_;
+ pdata->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* Enabling all multicast mode */
+ pdata->set_bits_mask = MAC_CR_MCPAS_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ } else if (!netdev_mc_empty(dev)) {
+ /* Enabling specific multicast addresses */
+ unsigned int hash_high = 0;
+ unsigned int hash_low = 0;
+ struct netdev_hw_addr *ha;
+
+ pdata->set_bits_mask = MAC_CR_HPFILT_;
+ pdata->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ netdev_for_each_mc_addr(ha, dev) {
+ unsigned int bitnum = smsc911x_hash(ha->addr);
+ unsigned int mask = 0x01 << (bitnum & 0x1F);
+
+ if (bitnum & 0x20)
+ hash_high |= mask;
+ else
+ hash_low |= mask;
+ }
+
+ pdata->hashhi = hash_high;
+ pdata->hashlo = hash_low;
+ } else {
+ /* Enabling local MAC address only */
+ pdata->set_bits_mask = 0;
+ pdata->clear_bits_mask =
+ (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+ pdata->hashhi = 0;
+ pdata->hashlo = 0;
+ }
+
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+
+ if (pdata->generation <= 1) {
+ /* Older hardware revision - cannot change these flags while
+ * receiving data */
+ if (!pdata->multicast_update_pending) {
+ unsigned int temp;
+ SMSC_TRACE(pdata, hw, "scheduling mcast update");
+ pdata->multicast_update_pending = 1;
+
+ /* Request the hardware to stop, then perform the
+ * update when we get an RX_STOP interrupt */
+ temp = smsc911x_mac_read(pdata, MAC_CR);
+ temp &= ~(MAC_CR_RXEN_);
+ smsc911x_mac_write(pdata, MAC_CR, temp);
+ } else {
+ /* There is another update pending, this should now
+ * use the newer values */
+ }
+ } else {
+ /* Newer hardware revision - can write immediately */
+ smsc911x_rx_multicast_update(pdata);
+ }
+
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+}
+
+static irqreturn_t smsc911x_irqhandler(int irq, void *dev_id)
+{
+ struct net_device *dev = dev_id;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u32 intsts = smsc911x_reg_read(pdata, INT_STS);
+ u32 inten = smsc911x_reg_read(pdata, INT_EN);
+ int serviced = IRQ_NONE;
+ u32 temp;
+
+ if (unlikely(intsts & inten & INT_STS_SW_INT_)) {
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_SW_INT_EN_);
+ smsc911x_reg_write(pdata, INT_EN, temp);
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_SW_INT_);
+ pdata->software_irq_signal = 1;
+ smp_wmb();
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXSTOP_INT_)) {
+ /* Called when there is a multicast update scheduled and
+ * it is now safe to complete the update */
+ SMSC_TRACE(pdata, intr, "RX Stop interrupt");
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_RXSTOP_INT_);
+ if (pdata->multicast_update_pending)
+ smsc911x_rx_multicast_update_workaround(pdata);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (intsts & inten & INT_STS_TDFA_) {
+ temp = smsc911x_reg_read(pdata, FIFO_INT);
+ temp |= FIFO_INT_TX_AVAIL_LEVEL_;
+ smsc911x_reg_write(pdata, FIFO_INT, temp);
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_TDFA_);
+ netif_wake_queue(dev);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (unlikely(intsts & inten & INT_STS_RXE_)) {
+ SMSC_TRACE(pdata, intr, "RX Error interrupt");
+ smsc911x_reg_write(pdata, INT_STS, INT_STS_RXE_);
+ serviced = IRQ_HANDLED;
+ }
+
+ if (likely(intsts & inten & INT_STS_RSFL_)) {
+ if (likely(napi_schedule_prep(&pdata->napi))) {
+ /* Disable Rx interrupts */
+ temp = smsc911x_reg_read(pdata, INT_EN);
+ temp &= (~INT_EN_RSFL_EN_);
+ smsc911x_reg_write(pdata, INT_EN, temp);
+ /* Schedule a NAPI poll */
+ __napi_schedule(&pdata->napi);
+ } else {
+ SMSC_WARN(pdata, rx_err, "napi_schedule_prep failed");
+ }
+ serviced = IRQ_HANDLED;
+ }
+
+ return serviced;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void smsc911x_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc911x_irqhandler(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+static int smsc911x_set_mac_address(struct net_device *dev, void *p)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct sockaddr *addr = p;
+
+ /* On older hardware revisions we cannot change the mac address
+ * registers while receiving data. Newer devices can safely change
+ * this at any time. */
+ if (pdata->generation <= 1 && netif_running(dev))
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
+ spin_lock_irq(&pdata->mac_lock);
+ smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+ spin_unlock_irq(&pdata->mac_lock);
+
+ netdev_info(dev, "MAC Address: %pM\n", dev->dev_addr);
+
+ return 0;
+}
+
+/* Standard ioctls for mii-tool */
+static int smsc911x_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ if (!netif_running(dev) || !pdata->phy_dev)
+ return -EINVAL;
+
+ return phy_mii_ioctl(pdata->phy_dev, ifr, cmd);
+}
+
+static int
+smsc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return phy_ethtool_gset(pdata->phy_dev, cmd);
+}
+
+static int
+smsc911x_ethtool_setsettings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return phy_ethtool_sset(pdata->phy_dev, cmd);
+}
+
+static void smsc911x_ethtool_getdrvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strlcpy(info->driver, SMSC_CHIPNAME, sizeof(info->driver));
+ strlcpy(info->version, SMSC_DRV_VERSION, sizeof(info->version));
+ strlcpy(info->bus_info, dev_name(dev->dev.parent),
+ sizeof(info->bus_info));
+}
+
+static int smsc911x_ethtool_nwayreset(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ return phy_start_aneg(pdata->phy_dev);
+}
+
+static u32 smsc911x_ethtool_getmsglevel(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ return pdata->msg_enable;
+}
+
+static void smsc911x_ethtool_setmsglevel(struct net_device *dev, u32 level)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ pdata->msg_enable = level;
+}
+
+static int smsc911x_ethtool_getregslen(struct net_device *dev)
+{
+ return (((E2P_DATA - ID_REV) / 4 + 1) + (WUCSR - MAC_CR) + 1 + 32) *
+ sizeof(u32);
+}
+
+static void
+smsc911x_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ struct phy_device *phy_dev = pdata->phy_dev;
+ unsigned long flags;
+ unsigned int i;
+ unsigned int j = 0;
+ u32 *data = buf;
+
+ regs->version = pdata->idrev;
+ for (i = ID_REV; i <= E2P_DATA; i += (sizeof(u32)))
+ data[j++] = smsc911x_reg_read(pdata, i);
+
+ for (i = MAC_CR; i <= WUCSR; i++) {
+ spin_lock_irqsave(&pdata->mac_lock, flags);
+ data[j++] = smsc911x_mac_read(pdata, i);
+ spin_unlock_irqrestore(&pdata->mac_lock, flags);
+ }
+
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc911x_mii_read(phy_dev->bus, phy_dev->addr, i);
+}
+
+static void smsc911x_eeprom_enable_access(struct smsc911x_data *pdata)
+{
+ unsigned int temp = smsc911x_reg_read(pdata, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc911x_reg_write(pdata, GPIO_CFG, temp);
+ msleep(1);
+}
+
+static int smsc911x_eeprom_send_cmd(struct smsc911x_data *pdata, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ SMSC_TRACE(pdata, drv, "op 0x%08x", op);
+ if (smsc911x_reg_read(pdata, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ SMSC_WARN(pdata, drv, "Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc911x_reg_write(pdata, E2P_CMD, e2cmd);
+
+ do {
+ msleep(1);
+ e2cmd = smsc911x_reg_read(pdata, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout));
+
+ if (!timeout) {
+ SMSC_TRACE(pdata, drv, "TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ SMSC_TRACE(pdata, drv, "Error occurred during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc911x_eeprom_read_location(struct smsc911x_data *pdata,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ SMSC_TRACE(pdata, drv, "address 0x%x", address);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret)
+ data[address] = smsc911x_reg_read(pdata, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc911x_eeprom_write_location(struct smsc911x_data *pdata,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ u32 temp;
+ int ret;
+
+ SMSC_TRACE(pdata, drv, "address 0x%x, data 0x%x", address, data);
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc911x_reg_write(pdata, E2P_DATA, (u32)data);
+
+ /* Workaround for hardware read-after-write restriction */
+ temp = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ ret = smsc911x_eeprom_send_cmd(pdata, op);
+ }
+
+ return ret;
+}
+
+static int smsc911x_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC911X_EEPROM_SIZE;
+}
+
+static int smsc911x_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u8 eeprom_data[SMSC911X_EEPROM_SIZE];
+ int len;
+ int i;
+
+ smsc911x_eeprom_enable_access(pdata);
+
+ len = min(eeprom->len, SMSC911X_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc911x_eeprom_read_location(pdata, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc911x_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ int ret;
+ struct smsc911x_data *pdata = netdev_priv(dev);
+
+ smsc911x_eeprom_enable_access(pdata);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc911x_eeprom_write_location(pdata, eeprom->offset, *data);
+ smsc911x_eeprom_send_cmd(pdata, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static const struct ethtool_ops smsc911x_ethtool_ops = {
+ .get_settings = smsc911x_ethtool_getsettings,
+ .set_settings = smsc911x_ethtool_setsettings,
+ .get_link = ethtool_op_get_link,
+ .get_drvinfo = smsc911x_ethtool_getdrvinfo,
+ .nway_reset = smsc911x_ethtool_nwayreset,
+ .get_msglevel = smsc911x_ethtool_getmsglevel,
+ .set_msglevel = smsc911x_ethtool_setmsglevel,
+ .get_regs_len = smsc911x_ethtool_getregslen,
+ .get_regs = smsc911x_ethtool_getregs,
+ .get_eeprom_len = smsc911x_ethtool_get_eeprom_len,
+ .get_eeprom = smsc911x_ethtool_get_eeprom,
+ .set_eeprom = smsc911x_ethtool_set_eeprom,
+};
+
+static const struct net_device_ops smsc911x_netdev_ops = {
+ .ndo_open = smsc911x_open,
+ .ndo_stop = smsc911x_stop,
+ .ndo_start_xmit = smsc911x_hard_start_xmit,
+ .ndo_get_stats = smsc911x_get_stats,
+ .ndo_set_multicast_list = smsc911x_set_multicast_list,
+ .ndo_do_ioctl = smsc911x_do_ioctl,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = smsc911x_set_mac_address,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = smsc911x_poll_controller,
+#endif
+};
+
+/* copies the current mac address from hardware to dev->dev_addr */
+static void __devinit smsc911x_read_mac_address(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ u32 mac_high16 = smsc911x_mac_read(pdata, ADDRH);
+ u32 mac_low32 = smsc911x_mac_read(pdata, ADDRL);
+
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+}
+
+/* Initializing private device structures, only called from probe */
+static int __devinit smsc911x_init(struct net_device *dev)
+{
+ struct smsc911x_data *pdata = netdev_priv(dev);
+ unsigned int byte_test;
+
+ SMSC_TRACE(pdata, probe, "Driver Parameters:");
+ SMSC_TRACE(pdata, probe, "LAN base: 0x%08lX",
+ (unsigned long)pdata->ioaddr);
+ SMSC_TRACE(pdata, probe, "IRQ: %d", dev->irq);
+ SMSC_TRACE(pdata, probe, "PHY will be autodetected.");
+
+ spin_lock_init(&pdata->dev_lock);
+ spin_lock_init(&pdata->mac_lock);
+
+ if (pdata->ioaddr == 0) {
+ SMSC_WARN(pdata, probe, "pdata->ioaddr: 0x00000000");
+ return -ENODEV;
+ }
+
+ /* Check byte ordering */
+ byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+ SMSC_TRACE(pdata, probe, "BYTE_TEST: 0x%08X", byte_test);
+ if (byte_test == 0x43218765) {
+ SMSC_TRACE(pdata, probe, "BYTE_TEST looks swapped, "
+ "applying WORD_SWAP");
+ smsc911x_reg_write(pdata, WORD_SWAP, 0xffffffff);
+
+ /* 1 dummy read of BYTE_TEST is needed after a write to
+ * WORD_SWAP before its contents are valid */
+ byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+
+ byte_test = smsc911x_reg_read(pdata, BYTE_TEST);
+ }
+
+ if (byte_test != 0x87654321) {
+ SMSC_WARN(pdata, drv, "BYTE_TEST: 0x%08X", byte_test);
+ if (((byte_test >> 16) & 0xFFFF) == (byte_test & 0xFFFF)) {
+ SMSC_WARN(pdata, probe,
+ "top 16 bits equal to bottom 16 bits");
+ SMSC_TRACE(pdata, probe,
+ "This may mean the chip is set "
+ "for 32 bit while the bus is reading 16 bit");
+ }
+ return -ENODEV;
+ }
+
+ /* Default generation to zero (all workarounds apply) */
+ pdata->generation = 0;
+
+ pdata->idrev = smsc911x_reg_read(pdata, ID_REV);
+ switch (pdata->idrev & 0xFFFF0000) {
+ case 0x01180000:
+ case 0x01170000:
+ case 0x01160000:
+ case 0x01150000:
+ /* LAN911[5678] family */
+ pdata->generation = pdata->idrev & 0x0000FFFF;
+ break;
+
+ case 0x118A0000:
+ case 0x117A0000:
+ case 0x116A0000:
+ case 0x115A0000:
+ /* LAN921[5678] family */
+ pdata->generation = 3;
+ break;
+
+ case 0x92100000:
+ case 0x92110000:
+ case 0x92200000:
+ case 0x92210000:
+ /* LAN9210/LAN9211/LAN9220/LAN9221 */
+ pdata->generation = 4;
+ break;
+
+ default:
+ SMSC_WARN(pdata, probe, "LAN911x not identified, idrev: 0x%08X",
+ pdata->idrev);
+ return -ENODEV;
+ }
+
+ SMSC_TRACE(pdata, probe,
+ "LAN911x identified, idrev: 0x%08X, generation: %d",
+ pdata->idrev, pdata->generation);
+
+ if (pdata->generation == 0)
+ SMSC_WARN(pdata, probe,
+ "This driver is not intended for this chip revision");
+
+ /* workaround for platforms without an eeprom, where the mac address
+ * is stored elsewhere and set by the bootloader. This saves the
+ * mac address before resetting the device */
+ if (pdata->config.flags & SMSC911X_SAVE_MAC_ADDRESS) {
+ spin_lock_irq(&pdata->mac_lock);
+ smsc911x_read_mac_address(dev);
+ spin_unlock_irq(&pdata->mac_lock);
+ }
+
+ /* Reset the LAN911x */
+ if (smsc911x_soft_reset(pdata))
+ return -ENODEV;
+
+ /* Disable all interrupt sources until we bring the device up */
+ smsc911x_reg_write(pdata, INT_EN, 0);
+
+ ether_setup(dev);
+ dev->flags |= IFF_MULTICAST;
+ netif_napi_add(dev, &pdata->napi, smsc911x_poll, SMSC_NAPI_WEIGHT);
+ dev->netdev_ops = &smsc911x_netdev_ops;
+ dev->ethtool_ops = &smsc911x_ethtool_ops;
+
+ return 0;
+}
+
+static int __devexit smsc911x_drv_remove(struct platform_device *pdev)
+{
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct resource *res;
+
+ dev = platform_get_drvdata(pdev);
+ BUG_ON(!dev);
+ pdata = netdev_priv(dev);
+ BUG_ON(!pdata);
+ BUG_ON(!pdata->ioaddr);
+ BUG_ON(!pdata->phy_dev);
+
+ SMSC_TRACE(pdata, ifdown, "Stopping driver");
+
+ phy_disconnect(pdata->phy_dev);
+ pdata->phy_dev = NULL;
+ mdiobus_unregister(pdata->mii_bus);
+ mdiobus_free(pdata->mii_bus);
+
+ platform_set_drvdata(pdev, NULL);
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ release_mem_region(res->start, resource_size(res));
+
+ iounmap(pdata->ioaddr);
+
+ free_netdev(dev);
+
+ return 0;
+}
+
+/* standard register acces */
+static const struct smsc911x_ops standard_smsc911x_ops = {
+ .reg_read = __smsc911x_reg_read,
+ .reg_write = __smsc911x_reg_write,
+ .rx_readfifo = smsc911x_rx_readfifo,
+ .tx_writefifo = smsc911x_tx_writefifo,
+};
+
+/* shifted register access */
+static const struct smsc911x_ops shifted_smsc911x_ops = {
+ .reg_read = __smsc911x_reg_read_shift,
+ .reg_write = __smsc911x_reg_write_shift,
+ .rx_readfifo = smsc911x_rx_readfifo_shift,
+ .tx_writefifo = smsc911x_tx_writefifo_shift,
+};
+
+#ifdef CONFIG_OF
+static int __devinit smsc911x_probe_config_dt(
+ struct smsc911x_platform_config *config,
+ struct device_node *np)
+{
+ const char *mac;
+ u32 width = 0;
+
+ if (!np)
+ return -ENODEV;
+
+ config->phy_interface = of_get_phy_mode(np);
+
+ mac = of_get_mac_address(np);
+ if (mac)
+ memcpy(config->mac, mac, ETH_ALEN);
+
+ of_property_read_u32(np, "reg-shift", &config->shift);
+
+ of_property_read_u32(np, "reg-io-width", &width);
+ if (width == 4)
+ config->flags |= SMSC911X_USE_32BIT;
+
+ if (of_get_property(np, "smsc,irq-active-high", NULL))
+ config->irq_polarity = SMSC911X_IRQ_POLARITY_ACTIVE_HIGH;
+
+ if (of_get_property(np, "smsc,irq-push-pull", NULL))
+ config->irq_type = SMSC911X_IRQ_TYPE_PUSH_PULL;
+
+ if (of_get_property(np, "smsc,force-internal-phy", NULL))
+ config->flags |= SMSC911X_FORCE_INTERNAL_PHY;
+
+ if (of_get_property(np, "smsc,force-external-phy", NULL))
+ config->flags |= SMSC911X_FORCE_EXTERNAL_PHY;
+
+ if (of_get_property(np, "smsc,save-mac-address", NULL))
+ config->flags |= SMSC911X_SAVE_MAC_ADDRESS;
+
+ return 0;
+}
+#else
+static inline int smsc911x_probe_config_dt(
+ struct smsc911x_platform_config *config,
+ struct device_node *np)
+{
+ return -ENODEV;
+}
+#endif /* CONFIG_OF */
+
+static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct net_device *dev;
+ struct smsc911x_data *pdata;
+ struct smsc911x_platform_config *config = pdev->dev.platform_data;
+ struct resource *res, *irq_res;
+ unsigned int intcfg = 0;
+ int res_size, irq_flags;
+ int retval;
+
+ pr_info("Driver version %s\n", SMSC_DRV_VERSION);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "smsc911x-memory");
+ if (!res)
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_warn("Could not allocate resource\n");
+ retval = -ENODEV;
+ goto out_0;
+ }
+ res_size = resource_size(res);
+
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq_res) {
+ pr_warn("Could not allocate irq resource\n");
+ retval = -ENODEV;
+ goto out_0;
+ }
+
+ if (!request_mem_region(res->start, res_size, SMSC_CHIPNAME)) {
+ retval = -EBUSY;
+ goto out_0;
+ }
+
+ dev = alloc_etherdev(sizeof(struct smsc911x_data));
+ if (!dev) {
+ pr_warn("Could not allocate device\n");
+ retval = -ENOMEM;
+ goto out_release_io_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ pdata = netdev_priv(dev);
+
+ dev->irq = irq_res->start;
+ irq_flags = irq_res->flags & IRQF_TRIGGER_MASK;
+ pdata->ioaddr = ioremap_nocache(res->start, res_size);
+
+ pdata->dev = dev;
+ pdata->msg_enable = ((1 << debug) - 1);
+
+ if (pdata->ioaddr == NULL) {
+ SMSC_WARN(pdata, probe, "Error smsc911x base address invalid");
+ retval = -ENOMEM;
+ goto out_free_netdev_2;
+ }
+
+ retval = smsc911x_probe_config_dt(&pdata->config, np);
+ if (retval && config) {
+ /* copy config parameters across to pdata */
+ memcpy(&pdata->config, config, sizeof(pdata->config));
+ retval = 0;
+ }
+
+ if (retval) {
+ SMSC_WARN(pdata, probe, "Error smsc911x config not found");
+ goto out_unmap_io_3;
+ }
+
+ /* assume standard, non-shifted, access to HW registers */
+ pdata->ops = &standard_smsc911x_ops;
+ /* apply the right access if shifting is needed */
+ if (pdata->config.shift)
+ pdata->ops = &shifted_smsc911x_ops;
+
+ retval = smsc911x_init(dev);
+ if (retval < 0)
+ goto out_unmap_io_3;
+
+ /* configure irq polarity and type before connecting isr */
+ if (pdata->config.irq_polarity == SMSC911X_IRQ_POLARITY_ACTIVE_HIGH)
+ intcfg |= INT_CFG_IRQ_POL_;
+
+ if (pdata->config.irq_type == SMSC911X_IRQ_TYPE_PUSH_PULL)
+ intcfg |= INT_CFG_IRQ_TYPE_;
+
+ smsc911x_reg_write(pdata, INT_CFG, intcfg);
+
+ /* Ensure interrupts are globally disabled before connecting ISR */
+ smsc911x_reg_write(pdata, INT_EN, 0);
+ smsc911x_reg_write(pdata, INT_STS, 0xFFFFFFFF);
+
+ retval = request_irq(dev->irq, smsc911x_irqhandler,
+ irq_flags | IRQF_SHARED, dev->name, dev);
+ if (retval) {
+ SMSC_WARN(pdata, probe,
+ "Unable to claim requested irq: %d", dev->irq);
+ goto out_unmap_io_3;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ retval = register_netdev(dev);
+ if (retval) {
+ SMSC_WARN(pdata, probe, "Error %i registering device", retval);
+ goto out_unset_drvdata_4;
+ } else {
+ SMSC_TRACE(pdata, probe,
+ "Network interface: \"%s\"", dev->name);
+ }
+
+ retval = smsc911x_mii_init(pdev, dev);
+ if (retval) {
+ SMSC_WARN(pdata, probe, "Error %i initialising mii", retval);
+ goto out_unregister_netdev_5;
+ }
+
+ spin_lock_irq(&pdata->mac_lock);
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE(pdata, probe,
+ "MAC Address is specified by configuration");
+ } else if (is_valid_ether_addr(pdata->config.mac)) {
+ memcpy(dev->dev_addr, pdata->config.mac, 6);
+ SMSC_TRACE(pdata, probe,
+ "MAC Address specified by platform data");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ smsc_get_mac(dev);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ SMSC_TRACE(pdata, probe,
+ "Mac Address is read from LAN911x EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc911x_set_hw_mac_address(pdata, dev->dev_addr);
+ SMSC_TRACE(pdata, probe,
+ "MAC Address is set to random_ether_addr");
+ }
+ }
+
+ spin_unlock_irq(&pdata->mac_lock);
+
+ netdev_info(dev, "MAC Address: %pM\n", dev->dev_addr);
+
+ return 0;
+
+out_unregister_netdev_5:
+ unregister_netdev(dev);
+out_unset_drvdata_4:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(dev->irq, dev);
+out_unmap_io_3:
+ iounmap(pdata->ioaddr);
+out_free_netdev_2:
+ free_netdev(dev);
+out_release_io_1:
+ release_mem_region(res->start, resource_size(res));
+out_0:
+ return retval;
+}
+
+#ifdef CONFIG_PM
+/* This implementation assumes the devices remains powered on its VDDVARIO
+ * pins during suspend. */
+
+/* TODO: implement freeze/thaw callbacks for hibernation.*/
+
+static int smsc911x_suspend(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct smsc911x_data *pdata = netdev_priv(ndev);
+
+ /* enable wake on LAN, energy detection and the external PME
+ * signal. */
+ smsc911x_reg_write(pdata, PMT_CTRL,
+ PMT_CTRL_PM_MODE_D1_ | PMT_CTRL_WOL_EN_ |
+ PMT_CTRL_ED_EN_ | PMT_CTRL_PME_EN_);
+
+ return 0;
+}
+
+static int smsc911x_resume(struct device *dev)
+{
+ struct net_device *ndev = dev_get_drvdata(dev);
+ struct smsc911x_data *pdata = netdev_priv(ndev);
+ unsigned int to = 100;
+
+ /* Note 3.11 from the datasheet:
+ * "When the LAN9220 is in a power saving state, a write of any
+ * data to the BYTE_TEST register will wake-up the device."
+ */
+ smsc911x_reg_write(pdata, BYTE_TEST, 0);
+
+ /* poll the READY bit in PMT_CTRL. Any other access to the device is
+ * forbidden while this bit isn't set. Try for 100ms and return -EIO
+ * if it failed. */
+ while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_) && --to)
+ udelay(1000);
+
+ return (to == 0) ? -EIO : 0;
+}
+
+static const struct dev_pm_ops smsc911x_pm_ops = {
+ .suspend = smsc911x_suspend,
+ .resume = smsc911x_resume,
+};
+
+#define SMSC911X_PM_OPS (&smsc911x_pm_ops)
+
+#else
+#define SMSC911X_PM_OPS NULL
+#endif
+
+static const struct of_device_id smsc911x_dt_ids[] = {
+ { .compatible = "smsc,lan9115", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, smsc911x_dt_ids);
+
+static struct platform_driver smsc911x_driver = {
+ .probe = smsc911x_drv_probe,
+ .remove = __devexit_p(smsc911x_drv_remove),
+ .driver = {
+ .name = SMSC_CHIPNAME,
+ .owner = THIS_MODULE,
+ .pm = SMSC911X_PM_OPS,
+ .of_match_table = smsc911x_dt_ids,
+ },
+};
+
+/* Entry point for loading the module */
+static int __init smsc911x_init_module(void)
+{
+ SMSC_INITIALIZE();
+ return platform_driver_register(&smsc911x_driver);
+}
+
+/* entry point for unloading the module */
+static void __exit smsc911x_cleanup_module(void)
+{
+ platform_driver_unregister(&smsc911x_driver);
+}
+
+module_init(smsc911x_init_module);
+module_exit(smsc911x_cleanup_module);
diff --git a/drivers/net/ethernet/smsc/smsc911x.h b/drivers/net/ethernet/smsc/smsc911x.h
new file mode 100644
index 000000000000..8d67aacf8867
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc911x.h
@@ -0,0 +1,404 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2004-2008 SMSC
+ * Copyright (C) 2005-2008 ARM
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************/
+#ifndef __SMSC911X_H__
+#define __SMSC911X_H__
+
+#define TX_FIFO_LOW_THRESHOLD ((u32)1600)
+#define SMSC911X_EEPROM_SIZE ((u32)128)
+#define USE_DEBUG 0
+
+/* This is the maximum number of packets to be received every
+ * NAPI poll */
+#define SMSC_NAPI_WEIGHT 16
+
+/* implements a PHY loopback test at initialisation time, to ensure a packet
+ * can be successfully looped back */
+#define USE_PHY_WORK_AROUND
+
+#if USE_DEBUG >= 1
+#define SMSC_WARN(pdata, nlevel, fmt, args...) \
+ netif_warn(pdata, nlevel, (pdata)->dev, \
+ "%s: " fmt "\n", __func__, ##args)
+#else
+#define SMSC_WARN(pdata, nlevel, fmt, args...) \
+ no_printk(fmt "\n", ##args)
+#endif
+
+#if USE_DEBUG >= 2
+#define SMSC_TRACE(pdata, nlevel, fmt, args...) \
+ netif_info(pdata, nlevel, pdata->dev, fmt "\n", ##args)
+#else
+#define SMSC_TRACE(pdata, nlevel, fmt, args...) \
+ no_printk(fmt "\n", ##args)
+#endif
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+#define SMSC_ASSERT_MAC_LOCK(pdata) \
+ WARN_ON(!spin_is_locked(&pdata->mac_lock))
+#else
+#define SMSC_ASSERT_MAC_LOCK(pdata) do {} while (0)
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+/* SMSC911x registers and bitfields */
+#define RX_DATA_FIFO 0x00
+
+#define TX_DATA_FIFO 0x20
+#define TX_CMD_A_ON_COMP_ 0x80000000
+#define TX_CMD_A_BUF_END_ALGN_ 0x03000000
+#define TX_CMD_A_4_BYTE_ALGN_ 0x00000000
+#define TX_CMD_A_16_BYTE_ALGN_ 0x01000000
+#define TX_CMD_A_32_BYTE_ALGN_ 0x02000000
+#define TX_CMD_A_DATA_OFFSET_ 0x001F0000
+#define TX_CMD_A_FIRST_SEG_ 0x00002000
+#define TX_CMD_A_LAST_SEG_ 0x00001000
+#define TX_CMD_A_BUF_SIZE_ 0x000007FF
+#define TX_CMD_B_PKT_TAG_ 0xFFFF0000
+#define TX_CMD_B_ADD_CRC_DISABLE_ 0x00002000
+#define TX_CMD_B_DISABLE_PADDING_ 0x00001000
+#define TX_CMD_B_PKT_BYTE_LENGTH_ 0x000007FF
+
+#define RX_STATUS_FIFO 0x40
+#define RX_STS_ES_ 0x00008000
+#define RX_STS_LENGTH_ERR_ 0x00001000
+#define RX_STS_MCAST_ 0x00000400
+#define RX_STS_FRAME_TYPE_ 0x00000020
+#define RX_STS_CRC_ERR_ 0x00000002
+
+#define RX_STATUS_FIFO_PEEK 0x44
+
+#define TX_STATUS_FIFO 0x48
+#define TX_STS_ES_ 0x00008000
+#define TX_STS_LOST_CARRIER_ 0x00000800
+#define TX_STS_NO_CARRIER_ 0x00000400
+#define TX_STS_LATE_COL_ 0x00000200
+#define TX_STS_EXCESS_COL_ 0x00000100
+
+#define TX_STATUS_FIFO_PEEK 0x4C
+
+#define ID_REV 0x50
+#define ID_REV_CHIP_ID_ 0xFFFF0000
+#define ID_REV_REV_ID_ 0x0000FFFF
+
+#define INT_CFG 0x54
+#define INT_CFG_INT_DEAS_ 0xFF000000
+#define INT_CFG_INT_DEAS_CLR_ 0x00004000
+#define INT_CFG_INT_DEAS_STS_ 0x00002000
+#define INT_CFG_IRQ_INT_ 0x00001000
+#define INT_CFG_IRQ_EN_ 0x00000100
+#define INT_CFG_IRQ_POL_ 0x00000010
+#define INT_CFG_IRQ_TYPE_ 0x00000001
+
+#define INT_STS 0x58
+#define INT_STS_SW_INT_ 0x80000000
+#define INT_STS_TXSTOP_INT_ 0x02000000
+#define INT_STS_RXSTOP_INT_ 0x01000000
+#define INT_STS_RXDFH_INT_ 0x00800000
+#define INT_STS_RXDF_INT_ 0x00400000
+#define INT_STS_TX_IOC_ 0x00200000
+#define INT_STS_RXD_INT_ 0x00100000
+#define INT_STS_GPT_INT_ 0x00080000
+#define INT_STS_PHY_INT_ 0x00040000
+#define INT_STS_PME_INT_ 0x00020000
+#define INT_STS_TXSO_ 0x00010000
+#define INT_STS_RWT_ 0x00008000
+#define INT_STS_RXE_ 0x00004000
+#define INT_STS_TXE_ 0x00002000
+#define INT_STS_TDFU_ 0x00000800
+#define INT_STS_TDFO_ 0x00000400
+#define INT_STS_TDFA_ 0x00000200
+#define INT_STS_TSFF_ 0x00000100
+#define INT_STS_TSFL_ 0x00000080
+#define INT_STS_RXDF_ 0x00000040
+#define INT_STS_RDFL_ 0x00000020
+#define INT_STS_RSFF_ 0x00000010
+#define INT_STS_RSFL_ 0x00000008
+#define INT_STS_GPIO2_INT_ 0x00000004
+#define INT_STS_GPIO1_INT_ 0x00000002
+#define INT_STS_GPIO0_INT_ 0x00000001
+
+#define INT_EN 0x5C
+#define INT_EN_SW_INT_EN_ 0x80000000
+#define INT_EN_TXSTOP_INT_EN_ 0x02000000
+#define INT_EN_RXSTOP_INT_EN_ 0x01000000
+#define INT_EN_RXDFH_INT_EN_ 0x00800000
+#define INT_EN_TIOC_INT_EN_ 0x00200000
+#define INT_EN_RXD_INT_EN_ 0x00100000
+#define INT_EN_GPT_INT_EN_ 0x00080000
+#define INT_EN_PHY_INT_EN_ 0x00040000
+#define INT_EN_PME_INT_EN_ 0x00020000
+#define INT_EN_TXSO_EN_ 0x00010000
+#define INT_EN_RWT_EN_ 0x00008000
+#define INT_EN_RXE_EN_ 0x00004000
+#define INT_EN_TXE_EN_ 0x00002000
+#define INT_EN_TDFU_EN_ 0x00000800
+#define INT_EN_TDFO_EN_ 0x00000400
+#define INT_EN_TDFA_EN_ 0x00000200
+#define INT_EN_TSFF_EN_ 0x00000100
+#define INT_EN_TSFL_EN_ 0x00000080
+#define INT_EN_RXDF_EN_ 0x00000040
+#define INT_EN_RDFL_EN_ 0x00000020
+#define INT_EN_RSFF_EN_ 0x00000010
+#define INT_EN_RSFL_EN_ 0x00000008
+#define INT_EN_GPIO2_INT_ 0x00000004
+#define INT_EN_GPIO1_INT_ 0x00000002
+#define INT_EN_GPIO0_INT_ 0x00000001
+
+#define BYTE_TEST 0x64
+
+#define FIFO_INT 0x68
+#define FIFO_INT_TX_AVAIL_LEVEL_ 0xFF000000
+#define FIFO_INT_TX_STS_LEVEL_ 0x00FF0000
+#define FIFO_INT_RX_AVAIL_LEVEL_ 0x0000FF00
+#define FIFO_INT_RX_STS_LEVEL_ 0x000000FF
+
+#define RX_CFG 0x6C
+#define RX_CFG_RX_END_ALGN_ 0xC0000000
+#define RX_CFG_RX_END_ALGN4_ 0x00000000
+#define RX_CFG_RX_END_ALGN16_ 0x40000000
+#define RX_CFG_RX_END_ALGN32_ 0x80000000
+#define RX_CFG_RX_DMA_CNT_ 0x0FFF0000
+#define RX_CFG_RX_DUMP_ 0x00008000
+#define RX_CFG_RXDOFF_ 0x00001F00
+
+#define TX_CFG 0x70
+#define TX_CFG_TXS_DUMP_ 0x00008000
+#define TX_CFG_TXD_DUMP_ 0x00004000
+#define TX_CFG_TXSAO_ 0x00000004
+#define TX_CFG_TX_ON_ 0x00000002
+#define TX_CFG_STOP_TX_ 0x00000001
+
+#define HW_CFG 0x74
+#define HW_CFG_TTM_ 0x00200000
+#define HW_CFG_SF_ 0x00100000
+#define HW_CFG_TX_FIF_SZ_ 0x000F0000
+#define HW_CFG_TR_ 0x00003000
+#define HW_CFG_SRST_ 0x00000001
+
+/* only available on 115/117 */
+#define HW_CFG_PHY_CLK_SEL_ 0x00000060
+#define HW_CFG_PHY_CLK_SEL_INT_PHY_ 0x00000000
+#define HW_CFG_PHY_CLK_SEL_EXT_PHY_ 0x00000020
+#define HW_CFG_PHY_CLK_SEL_CLK_DIS_ 0x00000040
+#define HW_CFG_SMI_SEL_ 0x00000010
+#define HW_CFG_EXT_PHY_DET_ 0x00000008
+#define HW_CFG_EXT_PHY_EN_ 0x00000004
+#define HW_CFG_SRST_TO_ 0x00000002
+
+/* only available on 116/118 */
+#define HW_CFG_32_16_BIT_MODE_ 0x00000004
+
+#define RX_DP_CTRL 0x78
+#define RX_DP_CTRL_RX_FFWD_ 0x80000000
+
+#define RX_FIFO_INF 0x7C
+#define RX_FIFO_INF_RXSUSED_ 0x00FF0000
+#define RX_FIFO_INF_RXDUSED_ 0x0000FFFF
+
+#define TX_FIFO_INF 0x80
+#define TX_FIFO_INF_TSUSED_ 0x00FF0000
+#define TX_FIFO_INF_TDFREE_ 0x0000FFFF
+
+#define PMT_CTRL 0x84
+#define PMT_CTRL_PM_MODE_ 0x00003000
+#define PMT_CTRL_PM_MODE_D0_ 0x00000000
+#define PMT_CTRL_PM_MODE_D1_ 0x00001000
+#define PMT_CTRL_PM_MODE_D2_ 0x00002000
+#define PMT_CTRL_PM_MODE_D3_ 0x00003000
+#define PMT_CTRL_PHY_RST_ 0x00000400
+#define PMT_CTRL_WOL_EN_ 0x00000200
+#define PMT_CTRL_ED_EN_ 0x00000100
+#define PMT_CTRL_PME_TYPE_ 0x00000040
+#define PMT_CTRL_WUPS_ 0x00000030
+#define PMT_CTRL_WUPS_NOWAKE_ 0x00000000
+#define PMT_CTRL_WUPS_ED_ 0x00000010
+#define PMT_CTRL_WUPS_WOL_ 0x00000020
+#define PMT_CTRL_WUPS_MULTI_ 0x00000030
+#define PMT_CTRL_PME_IND_ 0x00000008
+#define PMT_CTRL_PME_POL_ 0x00000004
+#define PMT_CTRL_PME_EN_ 0x00000002
+#define PMT_CTRL_READY_ 0x00000001
+
+#define GPIO_CFG 0x88
+#define GPIO_CFG_LED3_EN_ 0x40000000
+#define GPIO_CFG_LED2_EN_ 0x20000000
+#define GPIO_CFG_LED1_EN_ 0x10000000
+#define GPIO_CFG_GPIO2_INT_POL_ 0x04000000
+#define GPIO_CFG_GPIO1_INT_POL_ 0x02000000
+#define GPIO_CFG_GPIO0_INT_POL_ 0x01000000
+#define GPIO_CFG_EEPR_EN_ 0x00700000
+#define GPIO_CFG_GPIOBUF2_ 0x00040000
+#define GPIO_CFG_GPIOBUF1_ 0x00020000
+#define GPIO_CFG_GPIOBUF0_ 0x00010000
+#define GPIO_CFG_GPIODIR2_ 0x00000400
+#define GPIO_CFG_GPIODIR1_ 0x00000200
+#define GPIO_CFG_GPIODIR0_ 0x00000100
+#define GPIO_CFG_GPIOD4_ 0x00000020
+#define GPIO_CFG_GPIOD3_ 0x00000010
+#define GPIO_CFG_GPIOD2_ 0x00000004
+#define GPIO_CFG_GPIOD1_ 0x00000002
+#define GPIO_CFG_GPIOD0_ 0x00000001
+
+#define GPT_CFG 0x8C
+#define GPT_CFG_TIMER_EN_ 0x20000000
+#define GPT_CFG_GPT_LOAD_ 0x0000FFFF
+
+#define GPT_CNT 0x90
+#define GPT_CNT_GPT_CNT_ 0x0000FFFF
+
+#define WORD_SWAP 0x98
+
+#define FREE_RUN 0x9C
+
+#define RX_DROP 0xA0
+
+#define MAC_CSR_CMD 0xA4
+#define MAC_CSR_CMD_CSR_BUSY_ 0x80000000
+#define MAC_CSR_CMD_R_NOT_W_ 0x40000000
+#define MAC_CSR_CMD_CSR_ADDR_ 0x000000FF
+
+#define MAC_CSR_DATA 0xA8
+
+#define AFC_CFG 0xAC
+#define AFC_CFG_AFC_HI_ 0x00FF0000
+#define AFC_CFG_AFC_LO_ 0x0000FF00
+#define AFC_CFG_BACK_DUR_ 0x000000F0
+#define AFC_CFG_FCMULT_ 0x00000008
+#define AFC_CFG_FCBRD_ 0x00000004
+#define AFC_CFG_FCADD_ 0x00000002
+#define AFC_CFG_FCANY_ 0x00000001
+
+#define E2P_CMD 0xB0
+#define E2P_CMD_EPC_BUSY_ 0x80000000
+#define E2P_CMD_EPC_CMD_ 0x70000000
+#define E2P_CMD_EPC_CMD_READ_ 0x00000000
+#define E2P_CMD_EPC_CMD_EWDS_ 0x10000000
+#define E2P_CMD_EPC_CMD_EWEN_ 0x20000000
+#define E2P_CMD_EPC_CMD_WRITE_ 0x30000000
+#define E2P_CMD_EPC_CMD_WRAL_ 0x40000000
+#define E2P_CMD_EPC_CMD_ERASE_ 0x50000000
+#define E2P_CMD_EPC_CMD_ERAL_ 0x60000000
+#define E2P_CMD_EPC_CMD_RELOAD_ 0x70000000
+#define E2P_CMD_EPC_TIMEOUT_ 0x00000200
+#define E2P_CMD_MAC_ADDR_LOADED_ 0x00000100
+#define E2P_CMD_EPC_ADDR_ 0x000000FF
+
+#define E2P_DATA 0xB4
+#define E2P_DATA_EEPROM_DATA_ 0x000000FF
+#define LAN_REGISTER_EXTENT 0x00000100
+
+/*
+ * MAC Control and Status Register (Indirect Address)
+ * Offset (through the MAC_CSR CMD and DATA port)
+ */
+#define MAC_CR 0x01
+#define MAC_CR_RXALL_ 0x80000000
+#define MAC_CR_HBDIS_ 0x10000000
+#define MAC_CR_RCVOWN_ 0x00800000
+#define MAC_CR_LOOPBK_ 0x00200000
+#define MAC_CR_FDPX_ 0x00100000
+#define MAC_CR_MCPAS_ 0x00080000
+#define MAC_CR_PRMS_ 0x00040000
+#define MAC_CR_INVFILT_ 0x00020000
+#define MAC_CR_PASSBAD_ 0x00010000
+#define MAC_CR_HFILT_ 0x00008000
+#define MAC_CR_HPFILT_ 0x00002000
+#define MAC_CR_LCOLL_ 0x00001000
+#define MAC_CR_BCAST_ 0x00000800
+#define MAC_CR_DISRTY_ 0x00000400
+#define MAC_CR_PADSTR_ 0x00000100
+#define MAC_CR_BOLMT_MASK_ 0x000000C0
+#define MAC_CR_DFCHK_ 0x00000020
+#define MAC_CR_TXEN_ 0x00000008
+#define MAC_CR_RXEN_ 0x00000004
+
+#define ADDRH 0x02
+
+#define ADDRL 0x03
+
+#define HASHH 0x04
+
+#define HASHL 0x05
+
+#define MII_ACC 0x06
+#define MII_ACC_PHY_ADDR_ 0x0000F800
+#define MII_ACC_MIIRINDA_ 0x000007C0
+#define MII_ACC_MII_WRITE_ 0x00000002
+#define MII_ACC_MII_BUSY_ 0x00000001
+
+#define MII_DATA 0x07
+
+#define FLOW 0x08
+#define FLOW_FCPT_ 0xFFFF0000
+#define FLOW_FCPASS_ 0x00000004
+#define FLOW_FCEN_ 0x00000002
+#define FLOW_FCBSY_ 0x00000001
+
+#define VLAN1 0x09
+
+#define VLAN2 0x0A
+
+#define WUFF 0x0B
+
+#define WUCSR 0x0C
+#define WUCSR_GUE_ 0x00000200
+#define WUCSR_WUFR_ 0x00000040
+#define WUCSR_MPR_ 0x00000020
+#define WUCSR_WAKE_EN_ 0x00000004
+#define WUCSR_MPEN_ 0x00000002
+
+/*
+ * Phy definitions (vendor-specific)
+ */
+#define LAN9118_PHY_ID 0x00C0001C
+
+#define MII_INTSTS 0x1D
+
+#define MII_INTMSK 0x1E
+#define PHY_INTMSK_AN_RCV_ (1 << 1)
+#define PHY_INTMSK_PDFAULT_ (1 << 2)
+#define PHY_INTMSK_AN_ACK_ (1 << 3)
+#define PHY_INTMSK_LNKDOWN_ (1 << 4)
+#define PHY_INTMSK_RFAULT_ (1 << 5)
+#define PHY_INTMSK_AN_COMP_ (1 << 6)
+#define PHY_INTMSK_ENERGYON_ (1 << 7)
+#define PHY_INTMSK_DEFAULT_ (PHY_INTMSK_ENERGYON_ | \
+ PHY_INTMSK_AN_COMP_ | \
+ PHY_INTMSK_RFAULT_ | \
+ PHY_INTMSK_LNKDOWN_)
+
+#define ADVERTISE_PAUSE_ALL (ADVERTISE_PAUSE_CAP | \
+ ADVERTISE_PAUSE_ASYM)
+
+#define LPA_PAUSE_ALL (LPA_PAUSE_CAP | \
+ LPA_PAUSE_ASYM)
+
+/*
+ * Provide hooks to let the arch add to the initialisation procedure
+ * and to override the source of the MAC address.
+ */
+#define SMSC_INITIALIZE() do {} while (0)
+#define smsc_get_mac(dev) smsc911x_read_mac_address((dev))
+
+#ifdef CONFIG_SMSC911X_ARCH_HOOKS
+#include <asm/smsc911x.h>
+#endif
+
+#endif /* __SMSC911X_H__ */
diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c
new file mode 100644
index 000000000000..459726f54754
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc9420.c
@@ -0,0 +1,1763 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007,2008 SMSC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/pci.h>
+#include <linux/if_vlan.h>
+#include <linux/dma-mapping.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+#include "smsc9420.h"
+
+#define DRV_NAME "smsc9420"
+#define PFX DRV_NAME ": "
+#define DRV_MDIONAME "smsc9420-mdio"
+#define DRV_DESCRIPTION "SMSC LAN9420 driver"
+#define DRV_VERSION "1.01"
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+struct smsc9420_dma_desc {
+ u32 status;
+ u32 length;
+ u32 buffer1;
+ u32 buffer2;
+};
+
+struct smsc9420_ring_info {
+ struct sk_buff *skb;
+ dma_addr_t mapping;
+};
+
+struct smsc9420_pdata {
+ void __iomem *base_addr;
+ struct pci_dev *pdev;
+ struct net_device *dev;
+
+ struct smsc9420_dma_desc *rx_ring;
+ struct smsc9420_dma_desc *tx_ring;
+ struct smsc9420_ring_info *tx_buffers;
+ struct smsc9420_ring_info *rx_buffers;
+ dma_addr_t rx_dma_addr;
+ dma_addr_t tx_dma_addr;
+ int tx_ring_head, tx_ring_tail;
+ int rx_ring_head, rx_ring_tail;
+
+ spinlock_t int_lock;
+ spinlock_t phy_lock;
+
+ struct napi_struct napi;
+
+ bool software_irq_signal;
+ bool rx_csum;
+ u32 msg_enable;
+
+ struct phy_device *phy_dev;
+ struct mii_bus *mii_bus;
+ int phy_irq[PHY_MAX_ADDR];
+ int last_duplex;
+ int last_carrier;
+};
+
+static DEFINE_PCI_DEVICE_TABLE(smsc9420_id_table) = {
+ { PCI_VENDOR_ID_9420, PCI_DEVICE_ID_9420, PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, smsc9420_id_table);
+
+#define SMSC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+
+static uint smsc_debug;
+static uint debug = -1;
+module_param(debug, uint, 0);
+MODULE_PARM_DESC(debug, "debug level");
+
+#define smsc_dbg(TYPE, f, a...) \
+do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+ printk(KERN_DEBUG PFX f "\n", ## a); \
+} while (0)
+
+#define smsc_info(TYPE, f, a...) \
+do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+ printk(KERN_INFO PFX f "\n", ## a); \
+} while (0)
+
+#define smsc_warn(TYPE, f, a...) \
+do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \
+ printk(KERN_WARNING PFX f "\n", ## a); \
+} while (0)
+
+static inline u32 smsc9420_reg_read(struct smsc9420_pdata *pd, u32 offset)
+{
+ return ioread32(pd->base_addr + offset);
+}
+
+static inline void
+smsc9420_reg_write(struct smsc9420_pdata *pd, u32 offset, u32 value)
+{
+ iowrite32(value, pd->base_addr + offset);
+}
+
+static inline void smsc9420_pci_flush_write(struct smsc9420_pdata *pd)
+{
+ /* to ensure PCI write completion, we must perform a PCI read */
+ smsc9420_reg_read(pd, ID_REV);
+}
+
+static int smsc9420_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
+{
+ struct smsc9420_pdata *pd = (struct smsc9420_pdata *)bus->priv;
+ unsigned long flags;
+ u32 addr;
+ int i, reg = -EIO;
+
+ spin_lock_irqsave(&pd->phy_lock, flags);
+
+ /* confirm MII not busy */
+ if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
+ smsc_warn(DRV, "MII is busy???");
+ goto out;
+ }
+
+ /* set the address, index & direction (read from PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+ MII_ACCESS_MII_READ_;
+ smsc9420_reg_write(pd, MII_ACCESS, addr);
+
+ /* wait for read to complete with 50us timeout */
+ for (i = 0; i < 5; i++) {
+ if (!(smsc9420_reg_read(pd, MII_ACCESS) &
+ MII_ACCESS_MII_BUSY_)) {
+ reg = (u16)smsc9420_reg_read(pd, MII_DATA);
+ goto out;
+ }
+ udelay(10);
+ }
+
+ smsc_warn(DRV, "MII busy timeout!");
+
+out:
+ spin_unlock_irqrestore(&pd->phy_lock, flags);
+ return reg;
+}
+
+static int smsc9420_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
+ u16 val)
+{
+ struct smsc9420_pdata *pd = (struct smsc9420_pdata *)bus->priv;
+ unsigned long flags;
+ u32 addr;
+ int i, reg = -EIO;
+
+ spin_lock_irqsave(&pd->phy_lock, flags);
+
+ /* confirm MII not busy */
+ if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) {
+ smsc_warn(DRV, "MII is busy???");
+ goto out;
+ }
+
+ /* put the data to write in the MAC */
+ smsc9420_reg_write(pd, MII_DATA, (u32)val);
+
+ /* set the address, index & direction (write to PHY) */
+ addr = ((phyaddr & 0x1F) << 11) | ((regidx & 0x1F) << 6) |
+ MII_ACCESS_MII_WRITE_;
+ smsc9420_reg_write(pd, MII_ACCESS, addr);
+
+ /* wait for write to complete with 50us timeout */
+ for (i = 0; i < 5; i++) {
+ if (!(smsc9420_reg_read(pd, MII_ACCESS) &
+ MII_ACCESS_MII_BUSY_)) {
+ reg = 0;
+ goto out;
+ }
+ udelay(10);
+ }
+
+ smsc_warn(DRV, "MII busy timeout!");
+
+out:
+ spin_unlock_irqrestore(&pd->phy_lock, flags);
+ return reg;
+}
+
+/* Returns hash bit number for given MAC address
+ * Example:
+ * 01 00 5E 00 00 01 -> returns bit number 31 */
+static u32 smsc9420_hash(u8 addr[ETH_ALEN])
+{
+ return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f;
+}
+
+static int smsc9420_eeprom_reload(struct smsc9420_pdata *pd)
+{
+ int timeout = 100000;
+
+ BUG_ON(!pd);
+
+ if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ smsc_dbg(DRV, "smsc9420_eeprom_reload: Eeprom busy");
+ return -EIO;
+ }
+
+ smsc9420_reg_write(pd, E2P_CMD,
+ (E2P_CMD_EPC_BUSY_ | E2P_CMD_EPC_CMD_RELOAD_));
+
+ do {
+ udelay(10);
+ if (!(smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_))
+ return 0;
+ } while (timeout--);
+
+ smsc_warn(DRV, "smsc9420_eeprom_reload: Eeprom timed out");
+ return -EIO;
+}
+
+/* Standard ioctls for mii-tool */
+static int smsc9420_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ if (!netif_running(dev) || !pd->phy_dev)
+ return -EINVAL;
+
+ return phy_mii_ioctl(pd->phy_dev, ifr, cmd);
+}
+
+static int smsc9420_ethtool_get_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ if (!pd->phy_dev)
+ return -ENODEV;
+
+ cmd->maxtxpkt = 1;
+ cmd->maxrxpkt = 1;
+ return phy_ethtool_gset(pd->phy_dev, cmd);
+}
+
+static int smsc9420_ethtool_set_settings(struct net_device *dev,
+ struct ethtool_cmd *cmd)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ if (!pd->phy_dev)
+ return -ENODEV;
+
+ return phy_ethtool_sset(pd->phy_dev, cmd);
+}
+
+static void smsc9420_ethtool_get_drvinfo(struct net_device *netdev,
+ struct ethtool_drvinfo *drvinfo)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+
+ strcpy(drvinfo->driver, DRV_NAME);
+ strcpy(drvinfo->bus_info, pci_name(pd->pdev));
+ strcpy(drvinfo->version, DRV_VERSION);
+}
+
+static u32 smsc9420_ethtool_get_msglevel(struct net_device *netdev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+ return pd->msg_enable;
+}
+
+static void smsc9420_ethtool_set_msglevel(struct net_device *netdev, u32 data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+ pd->msg_enable = data;
+}
+
+static int smsc9420_ethtool_nway_reset(struct net_device *netdev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(netdev);
+
+ if (!pd->phy_dev)
+ return -ENODEV;
+
+ return phy_start_aneg(pd->phy_dev);
+}
+
+static int smsc9420_ethtool_getregslen(struct net_device *dev)
+{
+ /* all smsc9420 registers plus all phy registers */
+ return 0x100 + (32 * sizeof(u32));
+}
+
+static void
+smsc9420_ethtool_getregs(struct net_device *dev, struct ethtool_regs *regs,
+ void *buf)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ struct phy_device *phy_dev = pd->phy_dev;
+ unsigned int i, j = 0;
+ u32 *data = buf;
+
+ regs->version = smsc9420_reg_read(pd, ID_REV);
+ for (i = 0; i < 0x100; i += (sizeof(u32)))
+ data[j++] = smsc9420_reg_read(pd, i);
+
+ // cannot read phy registers if the net device is down
+ if (!phy_dev)
+ return;
+
+ for (i = 0; i <= 31; i++)
+ data[j++] = smsc9420_mii_read(phy_dev->bus, phy_dev->addr, i);
+}
+
+static void smsc9420_eeprom_enable_access(struct smsc9420_pdata *pd)
+{
+ unsigned int temp = smsc9420_reg_read(pd, GPIO_CFG);
+ temp &= ~GPIO_CFG_EEPR_EN_;
+ smsc9420_reg_write(pd, GPIO_CFG, temp);
+ msleep(1);
+}
+
+static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op)
+{
+ int timeout = 100;
+ u32 e2cmd;
+
+ smsc_dbg(HW, "op 0x%08x", op);
+ if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) {
+ smsc_warn(HW, "Busy at start");
+ return -EBUSY;
+ }
+
+ e2cmd = op | E2P_CMD_EPC_BUSY_;
+ smsc9420_reg_write(pd, E2P_CMD, e2cmd);
+
+ do {
+ msleep(1);
+ e2cmd = smsc9420_reg_read(pd, E2P_CMD);
+ } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout));
+
+ if (!timeout) {
+ smsc_info(HW, "TIMED OUT");
+ return -EAGAIN;
+ }
+
+ if (e2cmd & E2P_CMD_EPC_TIMEOUT_) {
+ smsc_info(HW, "Error occurred during eeprom operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int smsc9420_eeprom_read_location(struct smsc9420_pdata *pd,
+ u8 address, u8 *data)
+{
+ u32 op = E2P_CMD_EPC_CMD_READ_ | address;
+ int ret;
+
+ smsc_dbg(HW, "address 0x%x", address);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+
+ if (!ret)
+ data[address] = smsc9420_reg_read(pd, E2P_DATA);
+
+ return ret;
+}
+
+static int smsc9420_eeprom_write_location(struct smsc9420_pdata *pd,
+ u8 address, u8 data)
+{
+ u32 op = E2P_CMD_EPC_CMD_ERASE_ | address;
+ int ret;
+
+ smsc_dbg(HW, "address 0x%x, data 0x%x", address, data);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+
+ if (!ret) {
+ op = E2P_CMD_EPC_CMD_WRITE_ | address;
+ smsc9420_reg_write(pd, E2P_DATA, (u32)data);
+ ret = smsc9420_eeprom_send_cmd(pd, op);
+ }
+
+ return ret;
+}
+
+static int smsc9420_ethtool_get_eeprom_len(struct net_device *dev)
+{
+ return SMSC9420_EEPROM_SIZE;
+}
+
+static int smsc9420_ethtool_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u8 eeprom_data[SMSC9420_EEPROM_SIZE];
+ int len, i;
+
+ smsc9420_eeprom_enable_access(pd);
+
+ len = min(eeprom->len, SMSC9420_EEPROM_SIZE);
+ for (i = 0; i < len; i++) {
+ int ret = smsc9420_eeprom_read_location(pd, i, eeprom_data);
+ if (ret < 0) {
+ eeprom->len = 0;
+ return ret;
+ }
+ }
+
+ memcpy(data, &eeprom_data[eeprom->offset], len);
+ eeprom->magic = SMSC9420_EEPROM_MAGIC;
+ eeprom->len = len;
+ return 0;
+}
+
+static int smsc9420_ethtool_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ int ret;
+
+ if (eeprom->magic != SMSC9420_EEPROM_MAGIC)
+ return -EINVAL;
+
+ smsc9420_eeprom_enable_access(pd);
+ smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWEN_);
+ ret = smsc9420_eeprom_write_location(pd, eeprom->offset, *data);
+ smsc9420_eeprom_send_cmd(pd, E2P_CMD_EPC_CMD_EWDS_);
+
+ /* Single byte write, according to man page */
+ eeprom->len = 1;
+
+ return ret;
+}
+
+static const struct ethtool_ops smsc9420_ethtool_ops = {
+ .get_settings = smsc9420_ethtool_get_settings,
+ .set_settings = smsc9420_ethtool_set_settings,
+ .get_drvinfo = smsc9420_ethtool_get_drvinfo,
+ .get_msglevel = smsc9420_ethtool_get_msglevel,
+ .set_msglevel = smsc9420_ethtool_set_msglevel,
+ .nway_reset = smsc9420_ethtool_nway_reset,
+ .get_link = ethtool_op_get_link,
+ .get_eeprom_len = smsc9420_ethtool_get_eeprom_len,
+ .get_eeprom = smsc9420_ethtool_get_eeprom,
+ .set_eeprom = smsc9420_ethtool_set_eeprom,
+ .get_regs_len = smsc9420_ethtool_getregslen,
+ .get_regs = smsc9420_ethtool_getregs,
+};
+
+/* Sets the device MAC address to dev_addr */
+static void smsc9420_set_mac_address(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u8 *dev_addr = dev->dev_addr;
+ u32 mac_high16 = (dev_addr[5] << 8) | dev_addr[4];
+ u32 mac_low32 = (dev_addr[3] << 24) | (dev_addr[2] << 16) |
+ (dev_addr[1] << 8) | dev_addr[0];
+
+ smsc9420_reg_write(pd, ADDRH, mac_high16);
+ smsc9420_reg_write(pd, ADDRL, mac_low32);
+}
+
+static void smsc9420_check_mac_address(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ /* Check if mac address has been specified when bringing interface up */
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ smsc9420_set_mac_address(dev);
+ smsc_dbg(PROBE, "MAC Address is specified by configuration");
+ } else {
+ /* Try reading mac address from device. if EEPROM is present
+ * it will already have been set */
+ u32 mac_high16 = smsc9420_reg_read(pd, ADDRH);
+ u32 mac_low32 = smsc9420_reg_read(pd, ADDRL);
+ dev->dev_addr[0] = (u8)(mac_low32);
+ dev->dev_addr[1] = (u8)(mac_low32 >> 8);
+ dev->dev_addr[2] = (u8)(mac_low32 >> 16);
+ dev->dev_addr[3] = (u8)(mac_low32 >> 24);
+ dev->dev_addr[4] = (u8)(mac_high16);
+ dev->dev_addr[5] = (u8)(mac_high16 >> 8);
+
+ if (is_valid_ether_addr(dev->dev_addr)) {
+ /* eeprom values are valid so use them */
+ smsc_dbg(PROBE, "Mac Address is read from EEPROM");
+ } else {
+ /* eeprom values are invalid, generate random MAC */
+ random_ether_addr(dev->dev_addr);
+ smsc9420_set_mac_address(dev);
+ smsc_dbg(PROBE,
+ "MAC Address is set to random_ether_addr");
+ }
+ }
+}
+
+static void smsc9420_stop_tx(struct smsc9420_pdata *pd)
+{
+ u32 dmac_control, mac_cr, dma_intr_ena;
+ int timeout = 1000;
+
+ /* disable TX DMAC */
+ dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+ dmac_control &= (~DMAC_CONTROL_ST_);
+ smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+
+ /* Wait max 10ms for transmit process to stop */
+ while (--timeout) {
+ if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_TS_)
+ break;
+ udelay(10);
+ }
+
+ if (!timeout)
+ smsc_warn(IFDOWN, "TX DMAC failed to stop");
+
+ /* ACK Tx DMAC stop bit */
+ smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_TXPS_);
+
+ /* mask TX DMAC interrupts */
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena &= ~(DMAC_INTR_ENA_TX_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ /* stop MAC TX */
+ mac_cr = smsc9420_reg_read(pd, MAC_CR) & (~MAC_CR_TXEN_);
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+ smsc9420_pci_flush_write(pd);
+}
+
+static void smsc9420_free_tx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->tx_ring);
+
+ if (!pd->tx_buffers)
+ return;
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ struct sk_buff *skb = pd->tx_buffers[i].skb;
+
+ if (skb) {
+ BUG_ON(!pd->tx_buffers[i].mapping);
+ pci_unmap_single(pd->pdev, pd->tx_buffers[i].mapping,
+ skb->len, PCI_DMA_TODEVICE);
+ dev_kfree_skb_any(skb);
+ }
+
+ pd->tx_ring[i].status = 0;
+ pd->tx_ring[i].length = 0;
+ pd->tx_ring[i].buffer1 = 0;
+ pd->tx_ring[i].buffer2 = 0;
+ }
+ wmb();
+
+ kfree(pd->tx_buffers);
+ pd->tx_buffers = NULL;
+
+ pd->tx_ring_head = 0;
+ pd->tx_ring_tail = 0;
+}
+
+static void smsc9420_free_rx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->rx_ring);
+
+ if (!pd->rx_buffers)
+ return;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (pd->rx_buffers[i].skb)
+ dev_kfree_skb_any(pd->rx_buffers[i].skb);
+
+ if (pd->rx_buffers[i].mapping)
+ pci_unmap_single(pd->pdev, pd->rx_buffers[i].mapping,
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+
+ pd->rx_ring[i].status = 0;
+ pd->rx_ring[i].length = 0;
+ pd->rx_ring[i].buffer1 = 0;
+ pd->rx_ring[i].buffer2 = 0;
+ }
+ wmb();
+
+ kfree(pd->rx_buffers);
+ pd->rx_buffers = NULL;
+
+ pd->rx_ring_head = 0;
+ pd->rx_ring_tail = 0;
+}
+
+static void smsc9420_stop_rx(struct smsc9420_pdata *pd)
+{
+ int timeout = 1000;
+ u32 mac_cr, dmac_control, dma_intr_ena;
+
+ /* mask RX DMAC interrupts */
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena &= (~DMAC_INTR_ENA_RX_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ /* stop RX MAC prior to stoping DMA */
+ mac_cr = smsc9420_reg_read(pd, MAC_CR) & (~MAC_CR_RXEN_);
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+ smsc9420_pci_flush_write(pd);
+
+ /* stop RX DMAC */
+ dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+ dmac_control &= (~DMAC_CONTROL_SR_);
+ smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+ smsc9420_pci_flush_write(pd);
+
+ /* wait up to 10ms for receive to stop */
+ while (--timeout) {
+ if (smsc9420_reg_read(pd, DMAC_STATUS) & DMAC_STS_RS_)
+ break;
+ udelay(10);
+ }
+
+ if (!timeout)
+ smsc_warn(IFDOWN, "RX DMAC did not stop! timeout.");
+
+ /* ACK the Rx DMAC stop bit */
+ smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_RXPS_);
+}
+
+static irqreturn_t smsc9420_isr(int irq, void *dev_id)
+{
+ struct smsc9420_pdata *pd = dev_id;
+ u32 int_cfg, int_sts, int_ctl;
+ irqreturn_t ret = IRQ_NONE;
+ ulong flags;
+
+ BUG_ON(!pd);
+ BUG_ON(!pd->base_addr);
+
+ int_cfg = smsc9420_reg_read(pd, INT_CFG);
+
+ /* check if it's our interrupt */
+ if ((int_cfg & (INT_CFG_IRQ_EN_ | INT_CFG_IRQ_INT_)) !=
+ (INT_CFG_IRQ_EN_ | INT_CFG_IRQ_INT_))
+ return IRQ_NONE;
+
+ int_sts = smsc9420_reg_read(pd, INT_STAT);
+
+ if (likely(INT_STAT_DMAC_INT_ & int_sts)) {
+ u32 status = smsc9420_reg_read(pd, DMAC_STATUS);
+ u32 ints_to_clear = 0;
+
+ if (status & DMAC_STS_TX_) {
+ ints_to_clear |= (DMAC_STS_TX_ | DMAC_STS_NIS_);
+ netif_wake_queue(pd->dev);
+ }
+
+ if (status & DMAC_STS_RX_) {
+ /* mask RX DMAC interrupts */
+ u32 dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena &= (~DMAC_INTR_ENA_RX_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ ints_to_clear |= (DMAC_STS_RX_ | DMAC_STS_NIS_);
+ napi_schedule(&pd->napi);
+ }
+
+ if (ints_to_clear)
+ smsc9420_reg_write(pd, DMAC_STATUS, ints_to_clear);
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (unlikely(INT_STAT_SW_INT_ & int_sts)) {
+ /* mask software interrupt */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_ctl = smsc9420_reg_read(pd, INT_CTL);
+ int_ctl &= (~INT_CTL_SW_INT_EN_);
+ smsc9420_reg_write(pd, INT_CTL, int_ctl);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ smsc9420_reg_write(pd, INT_STAT, INT_STAT_SW_INT_);
+ pd->software_irq_signal = true;
+ smp_wmb();
+
+ ret = IRQ_HANDLED;
+ }
+
+ /* to ensure PCI write completion, we must perform a PCI read */
+ smsc9420_pci_flush_write(pd);
+
+ return ret;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void smsc9420_poll_controller(struct net_device *dev)
+{
+ disable_irq(dev->irq);
+ smsc9420_isr(0, dev);
+ enable_irq(dev->irq);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+static void smsc9420_dmac_soft_reset(struct smsc9420_pdata *pd)
+{
+ smsc9420_reg_write(pd, BUS_MODE, BUS_MODE_SWR_);
+ smsc9420_reg_read(pd, BUS_MODE);
+ udelay(2);
+ if (smsc9420_reg_read(pd, BUS_MODE) & BUS_MODE_SWR_)
+ smsc_warn(DRV, "Software reset not cleared");
+}
+
+static int smsc9420_stop(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 int_cfg;
+ ulong flags;
+
+ BUG_ON(!pd);
+ BUG_ON(!pd->phy_dev);
+
+ /* disable master interrupt */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ netif_tx_disable(dev);
+ napi_disable(&pd->napi);
+
+ smsc9420_stop_tx(pd);
+ smsc9420_free_tx_ring(pd);
+
+ smsc9420_stop_rx(pd);
+ smsc9420_free_rx_ring(pd);
+
+ free_irq(dev->irq, pd);
+
+ smsc9420_dmac_soft_reset(pd);
+
+ phy_stop(pd->phy_dev);
+
+ phy_disconnect(pd->phy_dev);
+ pd->phy_dev = NULL;
+ mdiobus_unregister(pd->mii_bus);
+ mdiobus_free(pd->mii_bus);
+
+ return 0;
+}
+
+static void smsc9420_rx_count_stats(struct net_device *dev, u32 desc_status)
+{
+ if (unlikely(desc_status & RDES0_ERROR_SUMMARY_)) {
+ dev->stats.rx_errors++;
+ if (desc_status & RDES0_DESCRIPTOR_ERROR_)
+ dev->stats.rx_over_errors++;
+ else if (desc_status & (RDES0_FRAME_TOO_LONG_ |
+ RDES0_RUNT_FRAME_ | RDES0_COLLISION_SEEN_))
+ dev->stats.rx_frame_errors++;
+ else if (desc_status & RDES0_CRC_ERROR_)
+ dev->stats.rx_crc_errors++;
+ }
+
+ if (unlikely(desc_status & RDES0_LENGTH_ERROR_))
+ dev->stats.rx_length_errors++;
+
+ if (unlikely(!((desc_status & RDES0_LAST_DESCRIPTOR_) &&
+ (desc_status & RDES0_FIRST_DESCRIPTOR_))))
+ dev->stats.rx_length_errors++;
+
+ if (desc_status & RDES0_MULTICAST_FRAME_)
+ dev->stats.multicast++;
+}
+
+static void smsc9420_rx_handoff(struct smsc9420_pdata *pd, const int index,
+ const u32 status)
+{
+ struct net_device *dev = pd->dev;
+ struct sk_buff *skb;
+ u16 packet_length = (status & RDES0_FRAME_LENGTH_MASK_)
+ >> RDES0_FRAME_LENGTH_SHFT_;
+
+ /* remove crc from packet lendth */
+ packet_length -= 4;
+
+ if (pd->rx_csum)
+ packet_length -= 2;
+
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += packet_length;
+
+ pci_unmap_single(pd->pdev, pd->rx_buffers[index].mapping,
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ pd->rx_buffers[index].mapping = 0;
+
+ skb = pd->rx_buffers[index].skb;
+ pd->rx_buffers[index].skb = NULL;
+
+ if (pd->rx_csum) {
+ u16 hw_csum = get_unaligned_le16(skb_tail_pointer(skb) +
+ NET_IP_ALIGN + packet_length + 4);
+ put_unaligned_le16(hw_csum, &skb->csum);
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ }
+
+ skb_reserve(skb, NET_IP_ALIGN);
+ skb_put(skb, packet_length);
+
+ skb->protocol = eth_type_trans(skb, dev);
+
+ netif_receive_skb(skb);
+}
+
+static int smsc9420_alloc_rx_buffer(struct smsc9420_pdata *pd, int index)
+{
+ struct sk_buff *skb = netdev_alloc_skb(pd->dev, PKT_BUF_SZ);
+ dma_addr_t mapping;
+
+ BUG_ON(pd->rx_buffers[index].skb);
+ BUG_ON(pd->rx_buffers[index].mapping);
+
+ if (unlikely(!skb)) {
+ smsc_warn(RX_ERR, "Failed to allocate new skb!");
+ return -ENOMEM;
+ }
+
+ skb->dev = pd->dev;
+
+ mapping = pci_map_single(pd->pdev, skb_tail_pointer(skb),
+ PKT_BUF_SZ, PCI_DMA_FROMDEVICE);
+ if (pci_dma_mapping_error(pd->pdev, mapping)) {
+ dev_kfree_skb_any(skb);
+ smsc_warn(RX_ERR, "pci_map_single failed!");
+ return -ENOMEM;
+ }
+
+ pd->rx_buffers[index].skb = skb;
+ pd->rx_buffers[index].mapping = mapping;
+ pd->rx_ring[index].buffer1 = mapping + NET_IP_ALIGN;
+ pd->rx_ring[index].status = RDES0_OWN_;
+ wmb();
+
+ return 0;
+}
+
+static void smsc9420_alloc_new_rx_buffers(struct smsc9420_pdata *pd)
+{
+ while (pd->rx_ring_tail != pd->rx_ring_head) {
+ if (smsc9420_alloc_rx_buffer(pd, pd->rx_ring_tail))
+ break;
+
+ pd->rx_ring_tail = (pd->rx_ring_tail + 1) % RX_RING_SIZE;
+ }
+}
+
+static int smsc9420_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct smsc9420_pdata *pd =
+ container_of(napi, struct smsc9420_pdata, napi);
+ struct net_device *dev = pd->dev;
+ u32 drop_frame_cnt, dma_intr_ena, status;
+ int work_done;
+
+ for (work_done = 0; work_done < budget; work_done++) {
+ rmb();
+ status = pd->rx_ring[pd->rx_ring_head].status;
+
+ /* stop if DMAC owns this dma descriptor */
+ if (status & RDES0_OWN_)
+ break;
+
+ smsc9420_rx_count_stats(dev, status);
+ smsc9420_rx_handoff(pd, pd->rx_ring_head, status);
+ pd->rx_ring_head = (pd->rx_ring_head + 1) % RX_RING_SIZE;
+ smsc9420_alloc_new_rx_buffers(pd);
+ }
+
+ drop_frame_cnt = smsc9420_reg_read(pd, MISS_FRAME_CNTR);
+ dev->stats.rx_dropped +=
+ (drop_frame_cnt & 0xFFFF) + ((drop_frame_cnt >> 17) & 0x3FF);
+
+ /* Kick RXDMA */
+ smsc9420_reg_write(pd, RX_POLL_DEMAND, 1);
+ smsc9420_pci_flush_write(pd);
+
+ if (work_done < budget) {
+ napi_complete(&pd->napi);
+
+ /* re-enable RX DMA interrupts */
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena |= (DMAC_INTR_ENA_RX_ | DMAC_INTR_ENA_NIS_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+ }
+ return work_done;
+}
+
+static void
+smsc9420_tx_update_stats(struct net_device *dev, u32 status, u32 length)
+{
+ if (unlikely(status & TDES0_ERROR_SUMMARY_)) {
+ dev->stats.tx_errors++;
+ if (status & (TDES0_EXCESSIVE_DEFERRAL_ |
+ TDES0_EXCESSIVE_COLLISIONS_))
+ dev->stats.tx_aborted_errors++;
+
+ if (status & (TDES0_LOSS_OF_CARRIER_ | TDES0_NO_CARRIER_))
+ dev->stats.tx_carrier_errors++;
+ } else {
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += (length & 0x7FF);
+ }
+
+ if (unlikely(status & TDES0_EXCESSIVE_COLLISIONS_)) {
+ dev->stats.collisions += 16;
+ } else {
+ dev->stats.collisions +=
+ (status & TDES0_COLLISION_COUNT_MASK_) >>
+ TDES0_COLLISION_COUNT_SHFT_;
+ }
+
+ if (unlikely(status & TDES0_HEARTBEAT_FAIL_))
+ dev->stats.tx_heartbeat_errors++;
+}
+
+/* Check for completed dma transfers, update stats and free skbs */
+static void smsc9420_complete_tx(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+
+ while (pd->tx_ring_tail != pd->tx_ring_head) {
+ int index = pd->tx_ring_tail;
+ u32 status, length;
+
+ rmb();
+ status = pd->tx_ring[index].status;
+ length = pd->tx_ring[index].length;
+
+ /* Check if DMA still owns this descriptor */
+ if (unlikely(TDES0_OWN_ & status))
+ break;
+
+ smsc9420_tx_update_stats(dev, status, length);
+
+ BUG_ON(!pd->tx_buffers[index].skb);
+ BUG_ON(!pd->tx_buffers[index].mapping);
+
+ pci_unmap_single(pd->pdev, pd->tx_buffers[index].mapping,
+ pd->tx_buffers[index].skb->len, PCI_DMA_TODEVICE);
+ pd->tx_buffers[index].mapping = 0;
+
+ dev_kfree_skb_any(pd->tx_buffers[index].skb);
+ pd->tx_buffers[index].skb = NULL;
+
+ pd->tx_ring[index].buffer1 = 0;
+ wmb();
+
+ pd->tx_ring_tail = (pd->tx_ring_tail + 1) % TX_RING_SIZE;
+ }
+}
+
+static netdev_tx_t smsc9420_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ dma_addr_t mapping;
+ int index = pd->tx_ring_head;
+ u32 tmp_desc1;
+ bool about_to_take_last_desc =
+ (((pd->tx_ring_head + 2) % TX_RING_SIZE) == pd->tx_ring_tail);
+
+ smsc9420_complete_tx(dev);
+
+ rmb();
+ BUG_ON(pd->tx_ring[index].status & TDES0_OWN_);
+ BUG_ON(pd->tx_buffers[index].skb);
+ BUG_ON(pd->tx_buffers[index].mapping);
+
+ mapping = pci_map_single(pd->pdev, skb->data,
+ skb->len, PCI_DMA_TODEVICE);
+ if (pci_dma_mapping_error(pd->pdev, mapping)) {
+ smsc_warn(TX_ERR, "pci_map_single failed, dropping packet");
+ return NETDEV_TX_BUSY;
+ }
+
+ pd->tx_buffers[index].skb = skb;
+ pd->tx_buffers[index].mapping = mapping;
+
+ tmp_desc1 = (TDES1_LS_ | ((u32)skb->len & 0x7FF));
+ if (unlikely(about_to_take_last_desc)) {
+ tmp_desc1 |= TDES1_IC_;
+ netif_stop_queue(pd->dev);
+ }
+
+ /* check if we are at the last descriptor and need to set EOR */
+ if (unlikely(index == (TX_RING_SIZE - 1)))
+ tmp_desc1 |= TDES1_TER_;
+
+ pd->tx_ring[index].buffer1 = mapping;
+ pd->tx_ring[index].length = tmp_desc1;
+ wmb();
+
+ /* increment head */
+ pd->tx_ring_head = (pd->tx_ring_head + 1) % TX_RING_SIZE;
+
+ /* assign ownership to DMAC */
+ pd->tx_ring[index].status = TDES0_OWN_;
+ wmb();
+
+ skb_tx_timestamp(skb);
+
+ /* kick the DMA */
+ smsc9420_reg_write(pd, TX_POLL_DEMAND, 1);
+ smsc9420_pci_flush_write(pd);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_stats *smsc9420_get_stats(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 counter = smsc9420_reg_read(pd, MISS_FRAME_CNTR);
+ dev->stats.rx_dropped +=
+ (counter & 0x0000FFFF) + ((counter >> 17) & 0x000003FF);
+ return &dev->stats;
+}
+
+static void smsc9420_set_multicast_list(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
+
+ if (dev->flags & IFF_PROMISC) {
+ smsc_dbg(HW, "Promiscuous Mode Enabled");
+ mac_cr |= MAC_CR_PRMS_;
+ mac_cr &= (~MAC_CR_MCPAS_);
+ mac_cr &= (~MAC_CR_HPFILT_);
+ } else if (dev->flags & IFF_ALLMULTI) {
+ smsc_dbg(HW, "Receive all Multicast Enabled");
+ mac_cr &= (~MAC_CR_PRMS_);
+ mac_cr |= MAC_CR_MCPAS_;
+ mac_cr &= (~MAC_CR_HPFILT_);
+ } else if (!netdev_mc_empty(dev)) {
+ struct netdev_hw_addr *ha;
+ u32 hash_lo = 0, hash_hi = 0;
+
+ smsc_dbg(HW, "Multicast filter enabled");
+ netdev_for_each_mc_addr(ha, dev) {
+ u32 bit_num = smsc9420_hash(ha->addr);
+ u32 mask = 1 << (bit_num & 0x1F);
+
+ if (bit_num & 0x20)
+ hash_hi |= mask;
+ else
+ hash_lo |= mask;
+
+ }
+ smsc9420_reg_write(pd, HASHH, hash_hi);
+ smsc9420_reg_write(pd, HASHL, hash_lo);
+
+ mac_cr &= (~MAC_CR_PRMS_);
+ mac_cr &= (~MAC_CR_MCPAS_);
+ mac_cr |= MAC_CR_HPFILT_;
+ } else {
+ smsc_dbg(HW, "Receive own packets only.");
+ smsc9420_reg_write(pd, HASHH, 0);
+ smsc9420_reg_write(pd, HASHL, 0);
+
+ mac_cr &= (~MAC_CR_PRMS_);
+ mac_cr &= (~MAC_CR_MCPAS_);
+ mac_cr &= (~MAC_CR_HPFILT_);
+ }
+
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+ smsc9420_pci_flush_write(pd);
+}
+
+static void smsc9420_phy_update_flowcontrol(struct smsc9420_pdata *pd)
+{
+ struct phy_device *phy_dev = pd->phy_dev;
+ u32 flow;
+
+ if (phy_dev->duplex == DUPLEX_FULL) {
+ u16 lcladv = phy_read(phy_dev, MII_ADVERTISE);
+ u16 rmtadv = phy_read(phy_dev, MII_LPA);
+ u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv);
+
+ if (cap & FLOW_CTRL_RX)
+ flow = 0xFFFF0002;
+ else
+ flow = 0;
+
+ smsc_info(LINK, "rx pause %s, tx pause %s",
+ (cap & FLOW_CTRL_RX ? "enabled" : "disabled"),
+ (cap & FLOW_CTRL_TX ? "enabled" : "disabled"));
+ } else {
+ smsc_info(LINK, "half duplex");
+ flow = 0;
+ }
+
+ smsc9420_reg_write(pd, FLOW, flow);
+}
+
+/* Update link mode if anything has changed. Called periodically when the
+ * PHY is in polling mode, even if nothing has changed. */
+static void smsc9420_phy_adjust_link(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ struct phy_device *phy_dev = pd->phy_dev;
+ int carrier;
+
+ if (phy_dev->duplex != pd->last_duplex) {
+ u32 mac_cr = smsc9420_reg_read(pd, MAC_CR);
+ if (phy_dev->duplex) {
+ smsc_dbg(LINK, "full duplex mode");
+ mac_cr |= MAC_CR_FDPX_;
+ } else {
+ smsc_dbg(LINK, "half duplex mode");
+ mac_cr &= ~MAC_CR_FDPX_;
+ }
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+
+ smsc9420_phy_update_flowcontrol(pd);
+ pd->last_duplex = phy_dev->duplex;
+ }
+
+ carrier = netif_carrier_ok(dev);
+ if (carrier != pd->last_carrier) {
+ if (carrier)
+ smsc_dbg(LINK, "carrier OK");
+ else
+ smsc_dbg(LINK, "no carrier");
+ pd->last_carrier = carrier;
+ }
+}
+
+static int smsc9420_mii_probe(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ struct phy_device *phydev = NULL;
+
+ BUG_ON(pd->phy_dev);
+
+ /* Device only supports internal PHY at address 1 */
+ if (!pd->mii_bus->phy_map[1]) {
+ pr_err("%s: no PHY found at address 1\n", dev->name);
+ return -ENODEV;
+ }
+
+ phydev = pd->mii_bus->phy_map[1];
+ smsc_info(PROBE, "PHY addr %d, phy_id 0x%08X", phydev->addr,
+ phydev->phy_id);
+
+ phydev = phy_connect(dev, dev_name(&phydev->dev),
+ smsc9420_phy_adjust_link, 0, PHY_INTERFACE_MODE_MII);
+
+ if (IS_ERR(phydev)) {
+ pr_err("%s: Could not attach to PHY\n", dev->name);
+ return PTR_ERR(phydev);
+ }
+
+ pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+ dev->name, phydev->drv->name, dev_name(&phydev->dev), phydev->irq);
+
+ /* mask with MAC supported features */
+ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause);
+ phydev->advertising = phydev->supported;
+
+ pd->phy_dev = phydev;
+ pd->last_duplex = -1;
+ pd->last_carrier = -1;
+
+ return 0;
+}
+
+static int smsc9420_mii_init(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ int err = -ENXIO, i;
+
+ pd->mii_bus = mdiobus_alloc();
+ if (!pd->mii_bus) {
+ err = -ENOMEM;
+ goto err_out_1;
+ }
+ pd->mii_bus->name = DRV_MDIONAME;
+ snprintf(pd->mii_bus->id, MII_BUS_ID_SIZE, "%x",
+ (pd->pdev->bus->number << 8) | pd->pdev->devfn);
+ pd->mii_bus->priv = pd;
+ pd->mii_bus->read = smsc9420_mii_read;
+ pd->mii_bus->write = smsc9420_mii_write;
+ pd->mii_bus->irq = pd->phy_irq;
+ for (i = 0; i < PHY_MAX_ADDR; ++i)
+ pd->mii_bus->irq[i] = PHY_POLL;
+
+ /* Mask all PHYs except ID 1 (internal) */
+ pd->mii_bus->phy_mask = ~(1 << 1);
+
+ if (mdiobus_register(pd->mii_bus)) {
+ smsc_warn(PROBE, "Error registering mii bus");
+ goto err_out_free_bus_2;
+ }
+
+ if (smsc9420_mii_probe(dev) < 0) {
+ smsc_warn(PROBE, "Error probing mii bus");
+ goto err_out_unregister_bus_3;
+ }
+
+ return 0;
+
+err_out_unregister_bus_3:
+ mdiobus_unregister(pd->mii_bus);
+err_out_free_bus_2:
+ mdiobus_free(pd->mii_bus);
+err_out_1:
+ return err;
+}
+
+static int smsc9420_alloc_tx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->tx_ring);
+
+ pd->tx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) *
+ TX_RING_SIZE), GFP_KERNEL);
+ if (!pd->tx_buffers) {
+ smsc_warn(IFUP, "Failed to allocated tx_buffers");
+ return -ENOMEM;
+ }
+
+ /* Initialize the TX Ring */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ pd->tx_buffers[i].skb = NULL;
+ pd->tx_buffers[i].mapping = 0;
+ pd->tx_ring[i].status = 0;
+ pd->tx_ring[i].length = 0;
+ pd->tx_ring[i].buffer1 = 0;
+ pd->tx_ring[i].buffer2 = 0;
+ }
+ pd->tx_ring[TX_RING_SIZE - 1].length = TDES1_TER_;
+ wmb();
+
+ pd->tx_ring_head = 0;
+ pd->tx_ring_tail = 0;
+
+ smsc9420_reg_write(pd, TX_BASE_ADDR, pd->tx_dma_addr);
+ smsc9420_pci_flush_write(pd);
+
+ return 0;
+}
+
+static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd)
+{
+ int i;
+
+ BUG_ON(!pd->rx_ring);
+
+ pd->rx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) *
+ RX_RING_SIZE), GFP_KERNEL);
+ if (pd->rx_buffers == NULL) {
+ smsc_warn(IFUP, "Failed to allocated rx_buffers");
+ goto out;
+ }
+
+ /* initialize the rx ring */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ pd->rx_ring[i].status = 0;
+ pd->rx_ring[i].length = PKT_BUF_SZ;
+ pd->rx_ring[i].buffer2 = 0;
+ pd->rx_buffers[i].skb = NULL;
+ pd->rx_buffers[i].mapping = 0;
+ }
+ pd->rx_ring[RX_RING_SIZE - 1].length = (PKT_BUF_SZ | RDES1_RER_);
+
+ /* now allocate the entire ring of skbs */
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ if (smsc9420_alloc_rx_buffer(pd, i)) {
+ smsc_warn(IFUP, "failed to allocate rx skb %d", i);
+ goto out_free_rx_skbs;
+ }
+ }
+
+ pd->rx_ring_head = 0;
+ pd->rx_ring_tail = 0;
+
+ smsc9420_reg_write(pd, VLAN1, ETH_P_8021Q);
+ smsc_dbg(IFUP, "VLAN1 = 0x%08x", smsc9420_reg_read(pd, VLAN1));
+
+ if (pd->rx_csum) {
+ /* Enable RX COE */
+ u32 coe = smsc9420_reg_read(pd, COE_CR) | RX_COE_EN;
+ smsc9420_reg_write(pd, COE_CR, coe);
+ smsc_dbg(IFUP, "COE_CR = 0x%08x", coe);
+ }
+
+ smsc9420_reg_write(pd, RX_BASE_ADDR, pd->rx_dma_addr);
+ smsc9420_pci_flush_write(pd);
+
+ return 0;
+
+out_free_rx_skbs:
+ smsc9420_free_rx_ring(pd);
+out:
+ return -ENOMEM;
+}
+
+static int smsc9420_open(struct net_device *dev)
+{
+ struct smsc9420_pdata *pd;
+ u32 bus_mode, mac_cr, dmac_control, int_cfg, dma_intr_ena, int_ctl;
+ unsigned long flags;
+ int result = 0, timeout;
+
+ BUG_ON(!dev);
+ pd = netdev_priv(dev);
+ BUG_ON(!pd);
+
+ if (!is_valid_ether_addr(dev->dev_addr)) {
+ smsc_warn(IFUP, "dev_addr is not a valid MAC address");
+ result = -EADDRNOTAVAIL;
+ goto out_0;
+ }
+
+ netif_carrier_off(dev);
+
+ /* disable, mask and acknowledge all interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ smsc9420_reg_write(pd, INT_CTL, 0);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, 0);
+ smsc9420_reg_write(pd, INT_STAT, 0xFFFFFFFF);
+ smsc9420_pci_flush_write(pd);
+
+ if (request_irq(dev->irq, smsc9420_isr, IRQF_SHARED | IRQF_DISABLED,
+ DRV_NAME, pd)) {
+ smsc_warn(IFUP, "Unable to use IRQ = %d", dev->irq);
+ result = -ENODEV;
+ goto out_0;
+ }
+
+ smsc9420_dmac_soft_reset(pd);
+
+ /* make sure MAC_CR is sane */
+ smsc9420_reg_write(pd, MAC_CR, 0);
+
+ smsc9420_set_mac_address(dev);
+
+ /* Configure GPIO pins to drive LEDs */
+ smsc9420_reg_write(pd, GPIO_CFG,
+ (GPIO_CFG_LED_3_ | GPIO_CFG_LED_2_ | GPIO_CFG_LED_1_));
+
+ bus_mode = BUS_MODE_DMA_BURST_LENGTH_16;
+
+#ifdef __BIG_ENDIAN
+ bus_mode |= BUS_MODE_DBO_;
+#endif
+
+ smsc9420_reg_write(pd, BUS_MODE, bus_mode);
+
+ smsc9420_pci_flush_write(pd);
+
+ /* set bus master bridge arbitration priority for Rx and TX DMA */
+ smsc9420_reg_write(pd, BUS_CFG, BUS_CFG_RXTXWEIGHT_4_1);
+
+ smsc9420_reg_write(pd, DMAC_CONTROL,
+ (DMAC_CONTROL_SF_ | DMAC_CONTROL_OSF_));
+
+ smsc9420_pci_flush_write(pd);
+
+ /* test the IRQ connection to the ISR */
+ smsc_dbg(IFUP, "Testing ISR using IRQ %d", dev->irq);
+ pd->software_irq_signal = false;
+
+ spin_lock_irqsave(&pd->int_lock, flags);
+ /* configure interrupt deassertion timer and enable interrupts */
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) | INT_CFG_IRQ_EN_;
+ int_cfg &= ~(INT_CFG_INT_DEAS_MASK);
+ int_cfg |= (INT_DEAS_TIME & INT_CFG_INT_DEAS_MASK);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+
+ /* unmask software interrupt */
+ int_ctl = smsc9420_reg_read(pd, INT_CTL) | INT_CTL_SW_INT_EN_;
+ smsc9420_reg_write(pd, INT_CTL, int_ctl);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+ smsc9420_pci_flush_write(pd);
+
+ timeout = 1000;
+ while (timeout--) {
+ if (pd->software_irq_signal)
+ break;
+ msleep(1);
+ }
+
+ /* disable interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ if (!pd->software_irq_signal) {
+ smsc_warn(IFUP, "ISR failed signaling test");
+ result = -ENODEV;
+ goto out_free_irq_1;
+ }
+
+ smsc_dbg(IFUP, "ISR passed test using IRQ %d", dev->irq);
+
+ result = smsc9420_alloc_tx_ring(pd);
+ if (result) {
+ smsc_warn(IFUP, "Failed to Initialize tx dma ring");
+ result = -ENOMEM;
+ goto out_free_irq_1;
+ }
+
+ result = smsc9420_alloc_rx_ring(pd);
+ if (result) {
+ smsc_warn(IFUP, "Failed to Initialize rx dma ring");
+ result = -ENOMEM;
+ goto out_free_tx_ring_2;
+ }
+
+ result = smsc9420_mii_init(dev);
+ if (result) {
+ smsc_warn(IFUP, "Failed to initialize Phy");
+ result = -ENODEV;
+ goto out_free_rx_ring_3;
+ }
+
+ /* Bring the PHY up */
+ phy_start(pd->phy_dev);
+
+ napi_enable(&pd->napi);
+
+ /* start tx and rx */
+ mac_cr = smsc9420_reg_read(pd, MAC_CR) | MAC_CR_TXEN_ | MAC_CR_RXEN_;
+ smsc9420_reg_write(pd, MAC_CR, mac_cr);
+
+ dmac_control = smsc9420_reg_read(pd, DMAC_CONTROL);
+ dmac_control |= DMAC_CONTROL_ST_ | DMAC_CONTROL_SR_;
+ smsc9420_reg_write(pd, DMAC_CONTROL, dmac_control);
+ smsc9420_pci_flush_write(pd);
+
+ dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA);
+ dma_intr_ena |=
+ (DMAC_INTR_ENA_TX_ | DMAC_INTR_ENA_RX_ | DMAC_INTR_ENA_NIS_);
+ smsc9420_reg_write(pd, DMAC_INTR_ENA, dma_intr_ena);
+ smsc9420_pci_flush_write(pd);
+
+ netif_wake_queue(dev);
+
+ smsc9420_reg_write(pd, RX_POLL_DEMAND, 1);
+
+ /* enable interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) | INT_CFG_IRQ_EN_;
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ return 0;
+
+out_free_rx_ring_3:
+ smsc9420_free_rx_ring(pd);
+out_free_tx_ring_2:
+ smsc9420_free_tx_ring(pd);
+out_free_irq_1:
+ free_irq(dev->irq, pd);
+out_0:
+ return result;
+}
+
+#ifdef CONFIG_PM
+
+static int smsc9420_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ u32 int_cfg;
+ ulong flags;
+
+ /* disable interrupts */
+ spin_lock_irqsave(&pd->int_lock, flags);
+ int_cfg = smsc9420_reg_read(pd, INT_CFG) & (~INT_CFG_IRQ_EN_);
+ smsc9420_reg_write(pd, INT_CFG, int_cfg);
+ spin_unlock_irqrestore(&pd->int_lock, flags);
+
+ if (netif_running(dev)) {
+ netif_tx_disable(dev);
+ smsc9420_stop_tx(pd);
+ smsc9420_free_tx_ring(pd);
+
+ napi_disable(&pd->napi);
+ smsc9420_stop_rx(pd);
+ smsc9420_free_rx_ring(pd);
+
+ free_irq(dev->irq, pd);
+
+ netif_device_detach(dev);
+ }
+
+ pci_save_state(pdev);
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+static int smsc9420_resume(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ struct smsc9420_pdata *pd = netdev_priv(dev);
+ int err;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_restore_state(pdev);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ pci_set_master(pdev);
+
+ err = pci_enable_wake(pdev, 0, 0);
+ if (err)
+ smsc_warn(IFUP, "pci_enable_wake failed: %d", err);
+
+ if (netif_running(dev)) {
+ err = smsc9420_open(dev);
+ netif_device_attach(dev);
+ }
+ return err;
+}
+
+#endif /* CONFIG_PM */
+
+static const struct net_device_ops smsc9420_netdev_ops = {
+ .ndo_open = smsc9420_open,
+ .ndo_stop = smsc9420_stop,
+ .ndo_start_xmit = smsc9420_hard_start_xmit,
+ .ndo_get_stats = smsc9420_get_stats,
+ .ndo_set_multicast_list = smsc9420_set_multicast_list,
+ .ndo_do_ioctl = smsc9420_do_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_mac_address = eth_mac_addr,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ .ndo_poll_controller = smsc9420_poll_controller,
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+};
+
+static int __devinit
+smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct net_device *dev;
+ struct smsc9420_pdata *pd;
+ void __iomem *virt_addr;
+ int result = 0;
+ u32 id_rev;
+
+ printk(KERN_INFO DRV_DESCRIPTION " version " DRV_VERSION "\n");
+
+ /* First do the PCI initialisation */
+ result = pci_enable_device(pdev);
+ if (unlikely(result)) {
+ printk(KERN_ERR "Cannot enable smsc9420\n");
+ goto out_0;
+ }
+
+ pci_set_master(pdev);
+
+ dev = alloc_etherdev(sizeof(*pd));
+ if (!dev) {
+ printk(KERN_ERR "ether device alloc failed\n");
+ goto out_disable_pci_device_1;
+ }
+
+ SET_NETDEV_DEV(dev, &pdev->dev);
+
+ if (!(pci_resource_flags(pdev, SMSC_BAR) & IORESOURCE_MEM)) {
+ printk(KERN_ERR "Cannot find PCI device base address\n");
+ goto out_free_netdev_2;
+ }
+
+ if ((pci_request_regions(pdev, DRV_NAME))) {
+ printk(KERN_ERR "Cannot obtain PCI resources, aborting.\n");
+ goto out_free_netdev_2;
+ }
+
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
+ printk(KERN_ERR "No usable DMA configuration, aborting.\n");
+ goto out_free_regions_3;
+ }
+
+ virt_addr = ioremap(pci_resource_start(pdev, SMSC_BAR),
+ pci_resource_len(pdev, SMSC_BAR));
+ if (!virt_addr) {
+ printk(KERN_ERR "Cannot map device registers, aborting.\n");
+ goto out_free_regions_3;
+ }
+
+ /* registers are double mapped with 0 offset for LE and 0x200 for BE */
+ virt_addr += LAN9420_CPSR_ENDIAN_OFFSET;
+
+ dev->base_addr = (ulong)virt_addr;
+
+ pd = netdev_priv(dev);
+
+ /* pci descriptors are created in the PCI consistent area */
+ pd->rx_ring = pci_alloc_consistent(pdev,
+ sizeof(struct smsc9420_dma_desc) * RX_RING_SIZE +
+ sizeof(struct smsc9420_dma_desc) * TX_RING_SIZE,
+ &pd->rx_dma_addr);
+
+ if (!pd->rx_ring)
+ goto out_free_io_4;
+
+ /* descriptors are aligned due to the nature of pci_alloc_consistent */
+ pd->tx_ring = (struct smsc9420_dma_desc *)
+ (pd->rx_ring + RX_RING_SIZE);
+ pd->tx_dma_addr = pd->rx_dma_addr +
+ sizeof(struct smsc9420_dma_desc) * RX_RING_SIZE;
+
+ pd->pdev = pdev;
+ pd->dev = dev;
+ pd->base_addr = virt_addr;
+ pd->msg_enable = smsc_debug;
+ pd->rx_csum = true;
+
+ smsc_dbg(PROBE, "lan_base=0x%08lx", (ulong)virt_addr);
+
+ id_rev = smsc9420_reg_read(pd, ID_REV);
+ switch (id_rev & 0xFFFF0000) {
+ case 0x94200000:
+ smsc_info(PROBE, "LAN9420 identified, ID_REV=0x%08X", id_rev);
+ break;
+ default:
+ smsc_warn(PROBE, "LAN9420 NOT identified");
+ smsc_warn(PROBE, "ID_REV=0x%08X", id_rev);
+ goto out_free_dmadesc_5;
+ }
+
+ smsc9420_dmac_soft_reset(pd);
+ smsc9420_eeprom_reload(pd);
+ smsc9420_check_mac_address(dev);
+
+ dev->netdev_ops = &smsc9420_netdev_ops;
+ dev->ethtool_ops = &smsc9420_ethtool_ops;
+ dev->irq = pdev->irq;
+
+ netif_napi_add(dev, &pd->napi, smsc9420_rx_poll, NAPI_WEIGHT);
+
+ result = register_netdev(dev);
+ if (result) {
+ smsc_warn(PROBE, "error %i registering device", result);
+ goto out_free_dmadesc_5;
+ }
+
+ pci_set_drvdata(pdev, dev);
+
+ spin_lock_init(&pd->int_lock);
+ spin_lock_init(&pd->phy_lock);
+
+ dev_info(&dev->dev, "MAC Address: %pM\n", dev->dev_addr);
+
+ return 0;
+
+out_free_dmadesc_5:
+ pci_free_consistent(pdev, sizeof(struct smsc9420_dma_desc) *
+ (RX_RING_SIZE + TX_RING_SIZE), pd->rx_ring, pd->rx_dma_addr);
+out_free_io_4:
+ iounmap(virt_addr - LAN9420_CPSR_ENDIAN_OFFSET);
+out_free_regions_3:
+ pci_release_regions(pdev);
+out_free_netdev_2:
+ free_netdev(dev);
+out_disable_pci_device_1:
+ pci_disable_device(pdev);
+out_0:
+ return -ENODEV;
+}
+
+static void __devexit smsc9420_remove(struct pci_dev *pdev)
+{
+ struct net_device *dev;
+ struct smsc9420_pdata *pd;
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return;
+
+ pci_set_drvdata(pdev, NULL);
+
+ pd = netdev_priv(dev);
+ unregister_netdev(dev);
+
+ /* tx_buffers and rx_buffers are freed in stop */
+ BUG_ON(pd->tx_buffers);
+ BUG_ON(pd->rx_buffers);
+
+ BUG_ON(!pd->tx_ring);
+ BUG_ON(!pd->rx_ring);
+
+ pci_free_consistent(pdev, sizeof(struct smsc9420_dma_desc) *
+ (RX_RING_SIZE + TX_RING_SIZE), pd->rx_ring, pd->rx_dma_addr);
+
+ iounmap(pd->base_addr - LAN9420_CPSR_ENDIAN_OFFSET);
+ pci_release_regions(pdev);
+ free_netdev(dev);
+ pci_disable_device(pdev);
+}
+
+static struct pci_driver smsc9420_driver = {
+ .name = DRV_NAME,
+ .id_table = smsc9420_id_table,
+ .probe = smsc9420_probe,
+ .remove = __devexit_p(smsc9420_remove),
+#ifdef CONFIG_PM
+ .suspend = smsc9420_suspend,
+ .resume = smsc9420_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init smsc9420_init_module(void)
+{
+ smsc_debug = netif_msg_init(debug, SMSC_MSG_DEFAULT);
+
+ return pci_register_driver(&smsc9420_driver);
+}
+
+static void __exit smsc9420_exit_module(void)
+{
+ pci_unregister_driver(&smsc9420_driver);
+}
+
+module_init(smsc9420_init_module);
+module_exit(smsc9420_exit_module);
diff --git a/drivers/net/ethernet/smsc/smsc9420.h b/drivers/net/ethernet/smsc/smsc9420.h
new file mode 100644
index 000000000000..e441402f77a2
--- /dev/null
+++ b/drivers/net/ethernet/smsc/smsc9420.h
@@ -0,0 +1,276 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007,2008 SMSC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************
+ */
+
+#ifndef _SMSC9420_H
+#define _SMSC9420_H
+
+#define TX_RING_SIZE (32)
+#define RX_RING_SIZE (128)
+
+/* interrupt deassertion in multiples of 10us */
+#define INT_DEAS_TIME (50)
+
+#define NAPI_WEIGHT (64)
+#define SMSC_BAR (3)
+
+#ifdef __BIG_ENDIAN
+/* Register set is duplicated for BE at an offset of 0x200 */
+#define LAN9420_CPSR_ENDIAN_OFFSET (0x200)
+#else
+#define LAN9420_CPSR_ENDIAN_OFFSET (0)
+#endif
+
+#define PCI_VENDOR_ID_9420 (0x1055)
+#define PCI_DEVICE_ID_9420 (0xE420)
+
+#define LAN_REGISTER_EXTENT (0x400)
+
+#define SMSC9420_EEPROM_SIZE ((u32)11)
+#define SMSC9420_EEPROM_MAGIC (0x9420)
+
+#define PKT_BUF_SZ (VLAN_ETH_FRAME_LEN + NET_IP_ALIGN + 4)
+
+/***********************************************/
+/* DMA Controller Control and Status Registers */
+/***********************************************/
+#define BUS_MODE (0x00)
+#define BUS_MODE_SWR_ (BIT(0))
+#define BUS_MODE_DMA_BURST_LENGTH_1 (BIT(8))
+#define BUS_MODE_DMA_BURST_LENGTH_2 (BIT(9))
+#define BUS_MODE_DMA_BURST_LENGTH_4 (BIT(10))
+#define BUS_MODE_DMA_BURST_LENGTH_8 (BIT(11))
+#define BUS_MODE_DMA_BURST_LENGTH_16 (BIT(12))
+#define BUS_MODE_DMA_BURST_LENGTH_32 (BIT(13))
+#define BUS_MODE_DBO_ (BIT(20))
+
+#define TX_POLL_DEMAND (0x04)
+
+#define RX_POLL_DEMAND (0x08)
+
+#define RX_BASE_ADDR (0x0C)
+
+#define TX_BASE_ADDR (0x10)
+
+#define DMAC_STATUS (0x14)
+#define DMAC_STS_TS_ (7 << 20)
+#define DMAC_STS_RS_ (7 << 17)
+#define DMAC_STS_NIS_ (BIT(16))
+#define DMAC_STS_AIS_ (BIT(15))
+#define DMAC_STS_RWT_ (BIT(9))
+#define DMAC_STS_RXPS_ (BIT(8))
+#define DMAC_STS_RXBU_ (BIT(7))
+#define DMAC_STS_RX_ (BIT(6))
+#define DMAC_STS_TXUNF_ (BIT(5))
+#define DMAC_STS_TXBU_ (BIT(2))
+#define DMAC_STS_TXPS_ (BIT(1))
+#define DMAC_STS_TX_ (BIT(0))
+
+#define DMAC_CONTROL (0x18)
+#define DMAC_CONTROL_TTM_ (BIT(22))
+#define DMAC_CONTROL_SF_ (BIT(21))
+#define DMAC_CONTROL_ST_ (BIT(13))
+#define DMAC_CONTROL_OSF_ (BIT(2))
+#define DMAC_CONTROL_SR_ (BIT(1))
+
+#define DMAC_INTR_ENA (0x1C)
+#define DMAC_INTR_ENA_NIS_ (BIT(16))
+#define DMAC_INTR_ENA_AIS_ (BIT(15))
+#define DMAC_INTR_ENA_RWT_ (BIT(9))
+#define DMAC_INTR_ENA_RXPS_ (BIT(8))
+#define DMAC_INTR_ENA_RXBU_ (BIT(7))
+#define DMAC_INTR_ENA_RX_ (BIT(6))
+#define DMAC_INTR_ENA_TXBU_ (BIT(2))
+#define DMAC_INTR_ENA_TXPS_ (BIT(1))
+#define DMAC_INTR_ENA_TX_ (BIT(0))
+
+#define MISS_FRAME_CNTR (0x20)
+
+#define TX_BUFF_ADDR (0x50)
+
+#define RX_BUFF_ADDR (0x54)
+
+/* Transmit Descriptor Bit Defs */
+#define TDES0_OWN_ (0x80000000)
+#define TDES0_ERROR_SUMMARY_ (0x00008000)
+#define TDES0_LOSS_OF_CARRIER_ (0x00000800)
+#define TDES0_NO_CARRIER_ (0x00000400)
+#define TDES0_LATE_COLLISION_ (0x00000200)
+#define TDES0_EXCESSIVE_COLLISIONS_ (0x00000100)
+#define TDES0_HEARTBEAT_FAIL_ (0x00000080)
+#define TDES0_COLLISION_COUNT_MASK_ (0x00000078)
+#define TDES0_COLLISION_COUNT_SHFT_ (3)
+#define TDES0_EXCESSIVE_DEFERRAL_ (0x00000004)
+#define TDES0_DEFERRED_ (0x00000001)
+
+#define TDES1_IC_ 0x80000000
+#define TDES1_LS_ 0x40000000
+#define TDES1_FS_ 0x20000000
+#define TDES1_TXCSEN_ 0x08000000
+#define TDES1_TER_ (BIT(25))
+#define TDES1_TCH_ 0x01000000
+
+/* Receive Descriptor 0 Bit Defs */
+#define RDES0_OWN_ (0x80000000)
+#define RDES0_FRAME_LENGTH_MASK_ (0x07FF0000)
+#define RDES0_FRAME_LENGTH_SHFT_ (16)
+#define RDES0_ERROR_SUMMARY_ (0x00008000)
+#define RDES0_DESCRIPTOR_ERROR_ (0x00004000)
+#define RDES0_LENGTH_ERROR_ (0x00001000)
+#define RDES0_RUNT_FRAME_ (0x00000800)
+#define RDES0_MULTICAST_FRAME_ (0x00000400)
+#define RDES0_FIRST_DESCRIPTOR_ (0x00000200)
+#define RDES0_LAST_DESCRIPTOR_ (0x00000100)
+#define RDES0_FRAME_TOO_LONG_ (0x00000080)
+#define RDES0_COLLISION_SEEN_ (0x00000040)
+#define RDES0_FRAME_TYPE_ (0x00000020)
+#define RDES0_WATCHDOG_TIMEOUT_ (0x00000010)
+#define RDES0_MII_ERROR_ (0x00000008)
+#define RDES0_DRIBBLING_BIT_ (0x00000004)
+#define RDES0_CRC_ERROR_ (0x00000002)
+
+/* Receive Descriptor 1 Bit Defs */
+#define RDES1_RER_ (0x02000000)
+
+/***********************************************/
+/* MAC Control and Status Registers */
+/***********************************************/
+#define MAC_CR (0x80)
+#define MAC_CR_RXALL_ (0x80000000)
+#define MAC_CR_DIS_RXOWN_ (0x00800000)
+#define MAC_CR_LOOPBK_ (0x00200000)
+#define MAC_CR_FDPX_ (0x00100000)
+#define MAC_CR_MCPAS_ (0x00080000)
+#define MAC_CR_PRMS_ (0x00040000)
+#define MAC_CR_INVFILT_ (0x00020000)
+#define MAC_CR_PASSBAD_ (0x00010000)
+#define MAC_CR_HFILT_ (0x00008000)
+#define MAC_CR_HPFILT_ (0x00002000)
+#define MAC_CR_LCOLL_ (0x00001000)
+#define MAC_CR_DIS_BCAST_ (0x00000800)
+#define MAC_CR_DIS_RTRY_ (0x00000400)
+#define MAC_CR_PADSTR_ (0x00000100)
+#define MAC_CR_BOLMT_MSK (0x000000C0)
+#define MAC_CR_MFCHK_ (0x00000020)
+#define MAC_CR_TXEN_ (0x00000008)
+#define MAC_CR_RXEN_ (0x00000004)
+
+#define ADDRH (0x84)
+
+#define ADDRL (0x88)
+
+#define HASHH (0x8C)
+
+#define HASHL (0x90)
+
+#define MII_ACCESS (0x94)
+#define MII_ACCESS_MII_BUSY_ (0x00000001)
+#define MII_ACCESS_MII_WRITE_ (0x00000002)
+#define MII_ACCESS_MII_READ_ (0x00000000)
+#define MII_ACCESS_INDX_MSK_ (0x000007C0)
+#define MII_ACCESS_PHYADDR_MSK_ (0x0000F8C0)
+#define MII_ACCESS_INDX_SHFT_CNT (6)
+#define MII_ACCESS_PHYADDR_SHFT_CNT (11)
+
+#define MII_DATA (0x98)
+
+#define FLOW (0x9C)
+
+#define VLAN1 (0xA0)
+
+#define VLAN2 (0xA4)
+
+#define WUFF (0xA8)
+
+#define WUCSR (0xAC)
+
+#define COE_CR (0xB0)
+#define TX_COE_EN (0x00010000)
+#define RX_COE_MODE (0x00000002)
+#define RX_COE_EN (0x00000001)
+
+/***********************************************/
+/* System Control and Status Registers */
+/***********************************************/
+#define ID_REV (0xC0)
+
+#define INT_CTL (0xC4)
+#define INT_CTL_SW_INT_EN_ (0x00008000)
+#define INT_CTL_SBERR_INT_EN_ (1 << 12)
+#define INT_CTL_MBERR_INT_EN_ (1 << 13)
+#define INT_CTL_GPT_INT_EN_ (0x00000008)
+#define INT_CTL_PHY_INT_EN_ (0x00000004)
+#define INT_CTL_WAKE_INT_EN_ (0x00000002)
+
+#define INT_STAT (0xC8)
+#define INT_STAT_SW_INT_ (1 << 15)
+#define INT_STAT_MBERR_INT_ (1 << 13)
+#define INT_STAT_SBERR_INT_ (1 << 12)
+#define INT_STAT_GPT_INT_ (1 << 3)
+#define INT_STAT_PHY_INT_ (0x00000004)
+#define INT_STAT_WAKE_INT_ (0x00000002)
+#define INT_STAT_DMAC_INT_ (0x00000001)
+
+#define INT_CFG (0xCC)
+#define INT_CFG_IRQ_INT_ (0x00080000)
+#define INT_CFG_IRQ_EN_ (0x00040000)
+#define INT_CFG_INT_DEAS_CLR_ (0x00000200)
+#define INT_CFG_INT_DEAS_MASK (0x000000FF)
+
+#define GPIO_CFG (0xD0)
+#define GPIO_CFG_LED_3_ (0x40000000)
+#define GPIO_CFG_LED_2_ (0x20000000)
+#define GPIO_CFG_LED_1_ (0x10000000)
+#define GPIO_CFG_EEPR_EN_ (0x00700000)
+
+#define GPT_CFG (0xD4)
+#define GPT_CFG_TIMER_EN_ (0x20000000)
+
+#define GPT_CNT (0xD8)
+
+#define BUS_CFG (0xDC)
+#define BUS_CFG_RXTXWEIGHT_1_1 (0 << 25)
+#define BUS_CFG_RXTXWEIGHT_2_1 (1 << 25)
+#define BUS_CFG_RXTXWEIGHT_3_1 (2 << 25)
+#define BUS_CFG_RXTXWEIGHT_4_1 (3 << 25)
+
+#define PMT_CTRL (0xE0)
+
+#define FREE_RUN (0xF4)
+
+#define E2P_CMD (0xF8)
+#define E2P_CMD_EPC_BUSY_ (0x80000000)
+#define E2P_CMD_EPC_CMD_ (0x70000000)
+#define E2P_CMD_EPC_CMD_READ_ (0x00000000)
+#define E2P_CMD_EPC_CMD_EWDS_ (0x10000000)
+#define E2P_CMD_EPC_CMD_EWEN_ (0x20000000)
+#define E2P_CMD_EPC_CMD_WRITE_ (0x30000000)
+#define E2P_CMD_EPC_CMD_WRAL_ (0x40000000)
+#define E2P_CMD_EPC_CMD_ERASE_ (0x50000000)
+#define E2P_CMD_EPC_CMD_ERAL_ (0x60000000)
+#define E2P_CMD_EPC_CMD_RELOAD_ (0x70000000)
+#define E2P_CMD_EPC_TIMEOUT_ (0x00000200)
+#define E2P_CMD_MAC_ADDR_LOADED_ (0x00000100)
+#define E2P_CMD_EPC_ADDR_ (0x000000FF)
+
+#define E2P_DATA (0xFC)
+#define E2P_DATA_EEPROM_DATA_ (0x000000FF)
+
+#endif /* _SMSC9420_H */
OpenPOWER on IntegriCloud